ah-resque-ui
Version:
A resque administration website for actionhero
241 lines (220 loc) • 6.74 kB
JSX
import React, { useState, useEffect } from "react";
import { ButtonToolbar, Button, Table, Modal, Row, Col } from "react-bootstrap";
import { Link } from "react-router-dom";
import Page from "../layouts/page";
import Pagination from "../components/pagination";
function FailedPage({ client, history, match }) {
const [data, setData] = useState({
failed: [],
counts: { failed: 0 },
focusedException: {
exception: "",
queue: "",
worker: "",
error: "",
payload: { args: {} },
renderedStack: "",
},
perPage: 15,
showModal: false,
page: parseInt(match.params.page || 0),
});
useEffect(() => {
data.page = parseInt(match.params.page || 0);
setData({ ...data });
}, [match.params]);
useEffect(() => {
loadData();
}, [data.page]);
async function loadData() {
const response = await client.action(
{
start: data.page * data.perPage,
stop: data.page * data.perPage + (data.perPage - 1),
},
"/api/1/resque/resqueFailed"
);
data.failed = response.failed;
setData({ ...data });
await loadFailedCount();
}
async function loadFailedCount() {
const response = await client.action({}, "/api/1/resque/resqueFailedCount");
data.counts = { failed: response.failedCount };
setData({ ...data });
}
async function removeFailedJob(index) {
await client.action({ id: index }, "/api/1/resque/removeFailed", "POST");
await loadData();
}
async function retryFailedJob(index) {
await client.action(
{ id: index },
"/api/1/resque/retryAndRemoveFailed",
"POST"
);
await loadData();
}
async function removeAllFailedJobs() {
if (window.confirm("Are you sure?")) {
await client.action({}, "/api/1/resque/removeAllFailed", "POST");
await loadData();
}
}
async function retryAllFailedJobs() {
if (window.confirm("Are you sure?")) {
await client.action({}, "/api/1/resque/retryAndRemoveAllFailed", "POST");
await loadData();
}
}
function renderFailureStack(index) {
const focusedException = data.failed[index];
focusedException.renderedStack = "";
if (focusedException.backtrace) {
focusedException.renderedStack = focusedException.backtrace.join("\r\n");
}
data.focusedException = focusedException;
data.showModal = true;
setData({ ...data });
}
function onHide() {
data.showModal = false;
setData({ ...data });
}
let index = -1 + data.perPage * data.page;
let argCounter = -1;
return (
<Page client={client}>
<h1>Failed Jobs ({data.counts.failed})</h1>
<Row>
<Col md={12}>
<ButtonToolbar>
<Button onClick={retryAllFailedJobs} size="sm" variant="warning">
Retry All
</Button>
<Button onClick={removeAllFailedJobs} size="sm" variant="danger">
Remove All
</Button>
</ButtonToolbar>
</Col>
<br />
<br />
</Row>
<Row>
<Col md={12}>
<Table id="failureTable" striped bordered hover size="sm">
<thead>
<tr>
<th> </th>
<th>Date</th>
<th>Exception</th>
<th>Queue</th>
<th>Method</th>
<th>Worker</th>
<th>Arguments</th>
<th> </th>
<th> </th>
</tr>
</thead>
<tbody>
{data.failed.map((f) => {
index++;
return (
<tr key={`failure-${index}`}>
<td>{index + 1}</td>
<td>{f.failed_at}</td>
<td>
<a onClick={renderFailureStack.bind(null, index)}>➕</a>
<strong>
{f.exception}: {f.error}
</strong>
</td>
<td>
<span className="text-success">
<Link to={`/queue/${f.queue}`}>{f.queue}</Link>
</span>
</td>
<td>{f.payload.class}</td>
<td>{f.worker}</td>
<td>
<ul>
{f.payload.args.map((a) => {
argCounter++;
return (
<li key={`arg-${argCounter}`}>
{JSON.stringify(a)}
</li>
);
})}
</ul>
</td>
<td>
<Button
onClick={retryFailedJob.bind(null, index)}
variant="warning"
size="sm"
>
Retry
</Button>
</td>
<td>
<Button
onClick={removeFailedJob.bind(null, index)}
variant="danger"
size="sm"
>
Remove
</Button>
</td>
</tr>
);
})}
</tbody>
</Table>
<Pagination
history={history}
page={data.page}
total={data.counts.failed}
perPage={data.perPage}
base="/failed"
/>
</Col>
</Row>
<Modal show={data.showModal} onHide={onHide} size="xl">
<Modal.Header>
<h3>
{data.focusedException.exception}: {data.focusedException.error}
</h3>
</Modal.Header>
<Modal.Body>
<p>
<strong>Queue</strong>: {data.focusedException.queue}{" "}
</p>
<p>
<strong>Worker</strong>: {data.focusedException.worker}{" "}
</p>
<p>
<strong>Payload</strong>:
</p>
<pre>
{data.focusedException.payload
? JSON.stringify(data.focusedException.payload.args)
: null}
</pre>
<p>
<strong>Stack</strong>:
</p>
<pre>{data.focusedException.renderedStack}</pre>
</Modal.Body>
<Modal.Footer>
<Button size="sm" onClick={onHide}>
Close
</Button>
</Modal.Footer>
</Modal>
</Page>
);
}
export default FailedPage;