diff --git a/ui/src/App.tsx b/ui/src/App.tsx
index 8e44bf2..bbacd44 100644
--- a/ui/src/App.tsx
+++ b/ui/src/App.tsx
@@ -14,7 +14,7 @@ import LayersIcon from "@material-ui/icons/Layers";
import SettingsIcon from "@material-ui/icons/Settings";
import { paths } from "./paths";
import ListItemLink from "./components/ListItemLink";
-import CronView from "./views/CronView";
+import SchedulersView from "./views/SchedulersView";
import DashboardView from "./views/DashboardView";
import TasksView from "./views/TasksView";
import SettingsView from "./views/SettingsView";
@@ -147,20 +147,20 @@ function App() {
}
/>
}
/>
}
/>
@@ -173,8 +173,8 @@ function App() {
-
-
+
+
diff --git a/ui/src/components/CronEntriesTable.tsx b/ui/src/components/SchedulerEntriesTable.tsx
similarity index 72%
rename from ui/src/components/CronEntriesTable.tsx
rename to ui/src/components/SchedulerEntriesTable.tsx
index 8953da4..a216d6d 100644
--- a/ui/src/components/CronEntriesTable.tsx
+++ b/ui/src/components/SchedulerEntriesTable.tsx
@@ -7,10 +7,14 @@ import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
+import Alert from "@material-ui/lab/Alert";
+import AlertTitle from "@material-ui/lab/AlertTitle";
import SyntaxHighlighter from "react-syntax-highlighter";
import syntaxHighlightStyle from "react-syntax-highlighter/dist/esm/styles/hljs/github";
import { SortDirection, ColumnConfig } from "../types/table";
import TableSortLabel from "@material-ui/core/TableSortLabel";
+import { SchedulerEntry } from "../api";
+import { timeAgo, durationBefore } from "../timeutil";
const useStyles = makeStyles((theme) => ({
table: {
@@ -58,7 +62,7 @@ const colConfigs: ColumnConfig[] = [
},
{
label: "Payload",
- key: "payload",
+ key: "task_payload",
sortBy: SortBy.Payload,
align: "left",
},
@@ -82,79 +86,22 @@ const colConfigs: ColumnConfig[] = [
},
];
-function createData(
- id: string,
- spec: string,
- type: string,
- payload: any,
- options: string,
- nextEnqueue: string,
- prevEnqueue: string
-) {
- return { id, spec, type, payload, options, nextEnqueue, prevEnqueue };
-}
-
-const rows = [
- createData(
- "da0e15bb-3649-45de-9c36-90b9db744b8a",
- "*/5 * * * *",
- "email:welcome",
- { user_id: 42 },
- "[Queue('email')]",
- "In 29s",
- "4m31s ago"
- ),
- createData(
- "fi0e10bb-3649-45de-9c36-90b9db744b8a",
- "* 1 * * *",
- "email:daily_digest",
- {},
- "[Queue('email')]",
- "In 23h",
- "1h ago"
- ),
- createData(
- "ca0e17bv-3649-45de-9c36-90b9db744b8a",
- "@every 10m",
- "search:reindex",
- {},
- "[Queue('index')]",
- "In 2m",
- "8m ago"
- ),
- createData(
- "we4e15bb-3649-45de-9c36-90b9db744b8a",
- "*/5 * * * *",
- "janitor",
- { user_id: 42 },
- "[Queue('low')]",
- "In 29s",
- "4m31s ago"
- ),
-];
-
-interface Entry {
- id: string;
- spec: string;
- type: string;
- payload: any;
- options: string;
- nextEnqueue: string;
- prevEnqueue: string;
-}
-
// sortEntries takes a array of entries and return a sorted array.
// It returns a new array and leave the original array untouched.
function sortEntries(
- entries: Entry[],
- cmpFn: (first: Entry, second: Entry) => number
-): Entry[] {
+ entries: SchedulerEntry[],
+ cmpFn: (first: SchedulerEntry, second: SchedulerEntry) => number
+): SchedulerEntry[] {
let copy = [...entries];
copy.sort(cmpFn);
return copy;
}
-export default function CronEntriesTable() {
+interface Props {
+ entries: SchedulerEntry[];
+}
+
+export default function SchedulerEntriesTable(props: Props) {
const classes = useStyles();
const [sortBy, setSortBy] = useState(SortBy.EntryId);
const [sortDir, setSortDir] = useState(SortDirection.Asc);
@@ -171,7 +118,7 @@ export default function CronEntriesTable() {
}
};
- const cmpFunc = (e1: Entry, q2: Entry): number => {
+ const cmpFunc = (e1: SchedulerEntry, q2: SchedulerEntry): number => {
let isE1Smaller: boolean;
switch (sortBy) {
case SortBy.EntryId:
@@ -183,24 +130,24 @@ export default function CronEntriesTable() {
isE1Smaller = e1.spec < q2.spec;
break;
case SortBy.Type:
- if (e1.type === q2.type) return 0;
- isE1Smaller = e1.type < q2.type;
+ if (e1.task_type === q2.task_type) return 0;
+ isE1Smaller = e1.task_type < q2.task_type;
break;
case SortBy.Payload:
- if (e1.payload === q2.payload) return 0;
- isE1Smaller = e1.payload < q2.payload;
+ if (e1.task_payload === q2.task_payload) return 0;
+ isE1Smaller = e1.task_payload < q2.task_payload;
break;
case SortBy.Options:
if (e1.options === q2.options) return 0;
isE1Smaller = e1.options < q2.options;
break;
case SortBy.NextEnqueue:
- if (e1.nextEnqueue === q2.nextEnqueue) return 0;
- isE1Smaller = e1.nextEnqueue < q2.nextEnqueue;
+ if (e1.next_enqueue_at === q2.next_enqueue_at) return 0;
+ isE1Smaller = e1.next_enqueue_at < q2.next_enqueue_at;
break;
case SortBy.PrevEnqueue:
- if (e1.prevEnqueue === q2.prevEnqueue) return 0;
- isE1Smaller = e1.prevEnqueue < q2.prevEnqueue;
+ if (e1.prev_enqueue_at === q2.prev_enqueue_at) return 0;
+ isE1Smaller = e1.prev_enqueue_at < q2.prev_enqueue_at;
break;
default:
// eslint-disable-next-line no-throw-literal
@@ -213,6 +160,15 @@ export default function CronEntriesTable() {
}
};
+ if (props.entries.length === 0) {
+ return (
+
+ Info
+ No entries found at this time.
+
+ );
+ }
+
return (
@@ -236,41 +192,43 @@ export default function CronEntriesTable() {
- {sortEntries(rows, cmpFunc).map((row, idx) => {
- const isLastRow = idx === rows.length - 1;
+ {sortEntries(props.entries, cmpFunc).map((entry, idx) => {
+ const isLastRow = idx === props.entries.length - 1;
return (
-
+
- {row.id}
+ {entry.id}
- {row.spec}
+ {entry.spec}
- {row.type}
+ {entry.task_type}
- {JSON.stringify(row.payload)}
+ {JSON.stringify(entry.task_payload)}
- {row.options}
+ {entry.options.length > 0
+ ? entry.options.join(", ")
+ : "No options"}
- {row.nextEnqueue}
+ {durationBefore(entry.next_enqueue_at)}
- {row.prevEnqueue}
+ {timeAgo(entry.prev_enqueue_at)}
);
diff --git a/ui/src/paths.ts b/ui/src/paths.ts
index 4a93bb4..14aaf3e 100644
--- a/ui/src/paths.ts
+++ b/ui/src/paths.ts
@@ -1,7 +1,7 @@
export const paths = {
HOME: "/",
SETTINGS: "/settings",
- CRON: "/cron",
+ SCHEDULERS: "/schedulers",
QUEUE_DETAILS: "/queues/:qname",
};
diff --git a/ui/src/timeutil.ts b/ui/src/timeutil.ts
index 570352a..ad4eb88 100644
--- a/ui/src/timeutil.ts
+++ b/ui/src/timeutil.ts
@@ -29,7 +29,7 @@ export function durationBefore(timestamp: string): string {
if (duration.totalSeconds < 1) {
return "now";
}
- return stringifyDuration(duration);
+ return "in " + stringifyDuration(duration);
} catch {
return "-";
}
diff --git a/ui/src/views/CronView.tsx b/ui/src/views/SchedulersView.tsx
similarity index 51%
rename from ui/src/views/CronView.tsx
rename to ui/src/views/SchedulersView.tsx
index 768a74c..ba0066b 100644
--- a/ui/src/views/CronView.tsx
+++ b/ui/src/views/SchedulersView.tsx
@@ -1,10 +1,14 @@
import React from "react";
+import { connect, ConnectedProps } from "react-redux";
import Container from "@material-ui/core/Container";
import { makeStyles } from "@material-ui/core/styles";
import Grid from "@material-ui/core/Grid";
import Paper from "@material-ui/core/Paper";
-import CronEntriesTable from "../components/CronEntriesTable";
+import SchedulerEntriesTable from "../components/SchedulerEntriesTable";
import Typography from "@material-ui/core/Typography";
+import { AppState } from "../store";
+import { listSchedulerEntriesAsync } from "../actions/schedulerEntriesActions";
+import { usePolling } from "../hooks";
const useStyles = makeStyles((theme) => ({
container: {
@@ -19,20 +23,37 @@ const useStyles = makeStyles((theme) => ({
},
heading: {
paddingLeft: theme.spacing(2),
+ marginBottom: theme.spacing(1),
},
}));
-function CronView() {
+function mapStateToProps(state: AppState) {
+ return {
+ loading: state.schedulerEntries.loading,
+ entries: state.schedulerEntries.data,
+ pollInterval: state.settings.pollInterval,
+ };
+}
+
+const connector = connect(mapStateToProps, { listSchedulerEntriesAsync });
+
+type Props = ConnectedProps;
+
+function SchedulersView(props: Props) {
+ const { pollInterval, listSchedulerEntriesAsync } = props;
const classes = useStyles();
+
+ usePolling(listSchedulerEntriesAsync, pollInterval);
+
return (
- Cron Entries
+ Scheduler Entries
-
+
@@ -40,4 +61,4 @@ function CronView() {
);
}
-export default CronView;
+export default connector(SchedulersView);