mirror of
https://github.com/hibiken/asynqmon.git
synced 2025-01-31 17:10:11 +08:00
Implement in-browser task search
This commit is contained in:
parent
1597dac66e
commit
2ec7a68d4a
@ -60,9 +60,11 @@ import {
|
|||||||
runAggregatingTask,
|
runAggregatingTask,
|
||||||
archiveAggregatingTask,
|
archiveAggregatingTask,
|
||||||
ListAggregatingTasksResponse,
|
ListAggregatingTasksResponse,
|
||||||
|
Queue,
|
||||||
} from "../api";
|
} from "../api";
|
||||||
import { Dispatch } from "redux";
|
import { Dispatch } from "redux";
|
||||||
import { toErrorString, toErrorStringWithHttpStatus } from "../utils";
|
import { toErrorString, toErrorStringWithHttpStatus } from "../utils";
|
||||||
|
import { AppState } from "../store";
|
||||||
|
|
||||||
// List of tasks related action types.
|
// List of tasks related action types.
|
||||||
export const GET_TASK_INFO_BEGIN = "GET_TASK_INFO_BEGIN";
|
export const GET_TASK_INFO_BEGIN = "GET_TASK_INFO_BEGIN";
|
||||||
@ -89,6 +91,11 @@ export const LIST_COMPLETED_TASKS_ERROR = "LIST_COMPLETED_TASKS_ERROR";
|
|||||||
export const LIST_AGGREGATING_TASKS_BEGIN = "LIST_AGGREGATING_TASKS_BEGIN";
|
export const LIST_AGGREGATING_TASKS_BEGIN = "LIST_AGGREGATING_TASKS_BEGIN";
|
||||||
export const LIST_AGGREGATING_TASKS_SUCCESS = "LIST_AGGREGATING_TASKS_SUCCESS";
|
export const LIST_AGGREGATING_TASKS_SUCCESS = "LIST_AGGREGATING_TASKS_SUCCESS";
|
||||||
export const LIST_AGGREGATING_TASKS_ERROR = "LIST_AGGREGATING_TASKS_ERROR";
|
export const LIST_AGGREGATING_TASKS_ERROR = "LIST_AGGREGATING_TASKS_ERROR";
|
||||||
|
export const FILTER_TASKS_BEGIN = "FILTER_TASKS_BEGIN";
|
||||||
|
export const FILTER_TASKS_PROGRESS = "FILTER_TASKS_PROGRESS";
|
||||||
|
export const FILTER_TASKS_SUCCESS = "FILTER_TASKS_SUCCESS";
|
||||||
|
export const FILTER_TASKS_ERROR = "FILTER_TASKS_ERROR";
|
||||||
|
export const FILTER_TASKS_CANCEL = "FILTER_TASKS_CANCEL";
|
||||||
export const CANCEL_ACTIVE_TASK_BEGIN = "CANCEL_ACTIVE_TASK_BEGIN";
|
export const CANCEL_ACTIVE_TASK_BEGIN = "CANCEL_ACTIVE_TASK_BEGIN";
|
||||||
export const CANCEL_ACTIVE_TASK_SUCCESS = "CANCEL_ACTIVE_TASK_SUCCESS";
|
export const CANCEL_ACTIVE_TASK_SUCCESS = "CANCEL_ACTIVE_TASK_SUCCESS";
|
||||||
export const CANCEL_ACTIVE_TASK_ERROR = "CANCEL_ACTIVE_TASK_ERROR";
|
export const CANCEL_ACTIVE_TASK_ERROR = "CANCEL_ACTIVE_TASK_ERROR";
|
||||||
@ -429,6 +436,30 @@ interface ListAggregatingTasksErrorAction {
|
|||||||
error: string; // error description
|
error: string; // error description
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface FilterTasksBeginAction {
|
||||||
|
type: typeof FILTER_TASKS_BEGIN;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilterTasksProgressAction {
|
||||||
|
type: typeof FILTER_TASKS_PROGRESS;
|
||||||
|
processedTasks: number;
|
||||||
|
filterResults: TaskInfo[];
|
||||||
|
newStats: Queue;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilterTasksSuccessAction {
|
||||||
|
type: typeof FILTER_TASKS_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilterTasksErrorAction {
|
||||||
|
type: typeof FILTER_TASKS_ERROR;
|
||||||
|
error: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface FilterTasksCancelAction {
|
||||||
|
type: typeof FILTER_TASKS_CANCEL;
|
||||||
|
}
|
||||||
|
|
||||||
interface CancelActiveTaskBeginAction {
|
interface CancelActiveTaskBeginAction {
|
||||||
type: typeof CANCEL_ACTIVE_TASK_BEGIN;
|
type: typeof CANCEL_ACTIVE_TASK_BEGIN;
|
||||||
queue: string;
|
queue: string;
|
||||||
@ -1291,6 +1322,11 @@ export type TasksActionTypes =
|
|||||||
| ListAggregatingTasksBeginAction
|
| ListAggregatingTasksBeginAction
|
||||||
| ListAggregatingTasksSuccessAction
|
| ListAggregatingTasksSuccessAction
|
||||||
| ListAggregatingTasksErrorAction
|
| ListAggregatingTasksErrorAction
|
||||||
|
| FilterTasksBeginAction
|
||||||
|
| FilterTasksProgressAction
|
||||||
|
| FilterTasksSuccessAction
|
||||||
|
| FilterTasksErrorAction
|
||||||
|
| FilterTasksCancelAction
|
||||||
| CancelActiveTaskBeginAction
|
| CancelActiveTaskBeginAction
|
||||||
| CancelActiveTaskSuccessAction
|
| CancelActiveTaskSuccessAction
|
||||||
| CancelActiveTaskErrorAction
|
| CancelActiveTaskErrorAction
|
||||||
@ -1446,14 +1482,42 @@ export function getTaskInfoAsync(qname: string, id: string) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getFilterResults(
|
||||||
|
state: AppState,
|
||||||
|
qname: string,
|
||||||
|
pageOpts?: PaginationOptions
|
||||||
|
): ListTasksResponse | null {
|
||||||
|
const filterOp = state.tasks.filterOp;
|
||||||
|
if (filterOp == null || !filterOp.done) return null;
|
||||||
|
const curQueueStats = state.queues.data.find((it) => it.name === qname);
|
||||||
|
if (curQueueStats == null) return null;
|
||||||
|
|
||||||
|
const size = pageOpts?.size ?? 20;
|
||||||
|
const page = pageOpts?.page ?? 1;
|
||||||
|
|
||||||
|
const start = (page - 1) * size;
|
||||||
|
const end = start + size;
|
||||||
|
const results = filterOp.result.slice(start, end);
|
||||||
|
|
||||||
|
return {
|
||||||
|
tasks: results,
|
||||||
|
stats: curQueueStats.currentStats,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function listActiveTasksAsync(
|
export function listActiveTasksAsync(
|
||||||
qname: string,
|
qname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
dispatch({ type: LIST_ACTIVE_TASKS_BEGIN, queue: qname });
|
dispatch({ type: LIST_ACTIVE_TASKS_BEGIN, queue: qname });
|
||||||
try {
|
try {
|
||||||
const response = await listActiveTasks(qname, pageOpts);
|
const response =
|
||||||
|
getFilterResults(getState(), qname, pageOpts) ??
|
||||||
|
(await listActiveTasks(qname, pageOpts));
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LIST_ACTIVE_TASKS_SUCCESS,
|
type: LIST_ACTIVE_TASKS_SUCCESS,
|
||||||
queue: qname,
|
queue: qname,
|
||||||
@ -1477,10 +1541,15 @@ export function listPendingTasksAsync(
|
|||||||
qname: string,
|
qname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
dispatch({ type: LIST_PENDING_TASKS_BEGIN, queue: qname });
|
dispatch({ type: LIST_PENDING_TASKS_BEGIN, queue: qname });
|
||||||
try {
|
try {
|
||||||
const response = await listPendingTasks(qname, pageOpts);
|
const response =
|
||||||
|
getFilterResults(getState(), qname, pageOpts) ??
|
||||||
|
(await listPendingTasks(qname, pageOpts));
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LIST_PENDING_TASKS_SUCCESS,
|
type: LIST_PENDING_TASKS_SUCCESS,
|
||||||
queue: qname,
|
queue: qname,
|
||||||
@ -1504,10 +1573,15 @@ export function listScheduledTasksAsync(
|
|||||||
qname: string,
|
qname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
dispatch({ type: LIST_SCHEDULED_TASKS_BEGIN, queue: qname });
|
dispatch({ type: LIST_SCHEDULED_TASKS_BEGIN, queue: qname });
|
||||||
try {
|
try {
|
||||||
const response = await listScheduledTasks(qname, pageOpts);
|
const response =
|
||||||
|
getFilterResults(getState(), qname, pageOpts) ??
|
||||||
|
(await listScheduledTasks(qname, pageOpts));
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LIST_SCHEDULED_TASKS_SUCCESS,
|
type: LIST_SCHEDULED_TASKS_SUCCESS,
|
||||||
queue: qname,
|
queue: qname,
|
||||||
@ -1531,10 +1605,15 @@ export function listRetryTasksAsync(
|
|||||||
qname: string,
|
qname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
dispatch({ type: LIST_RETRY_TASKS_BEGIN, queue: qname });
|
dispatch({ type: LIST_RETRY_TASKS_BEGIN, queue: qname });
|
||||||
try {
|
try {
|
||||||
const response = await listRetryTasks(qname, pageOpts);
|
const response =
|
||||||
|
getFilterResults(getState(), qname, pageOpts) ??
|
||||||
|
(await listRetryTasks(qname, pageOpts));
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LIST_RETRY_TASKS_SUCCESS,
|
type: LIST_RETRY_TASKS_SUCCESS,
|
||||||
queue: qname,
|
queue: qname,
|
||||||
@ -1558,10 +1637,15 @@ export function listArchivedTasksAsync(
|
|||||||
qname: string,
|
qname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
dispatch({ type: LIST_ARCHIVED_TASKS_BEGIN, queue: qname });
|
dispatch({ type: LIST_ARCHIVED_TASKS_BEGIN, queue: qname });
|
||||||
try {
|
try {
|
||||||
const response = await listArchivedTasks(qname, pageOpts);
|
const response =
|
||||||
|
getFilterResults(getState(), qname, pageOpts) ??
|
||||||
|
(await listArchivedTasks(qname, pageOpts));
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LIST_ARCHIVED_TASKS_SUCCESS,
|
type: LIST_ARCHIVED_TASKS_SUCCESS,
|
||||||
queue: qname,
|
queue: qname,
|
||||||
@ -1585,10 +1669,15 @@ export function listCompletedTasksAsync(
|
|||||||
qname: string,
|
qname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
dispatch({ type: LIST_COMPLETED_TASKS_BEGIN, queue: qname });
|
dispatch({ type: LIST_COMPLETED_TASKS_BEGIN, queue: qname });
|
||||||
const response = await listCompletedTasks(qname, pageOpts);
|
const response =
|
||||||
|
getFilterResults(getState(), qname, pageOpts) ??
|
||||||
|
(await listCompletedTasks(qname, pageOpts));
|
||||||
dispatch({
|
dispatch({
|
||||||
type: LIST_COMPLETED_TASKS_SUCCESS,
|
type: LIST_COMPLETED_TASKS_SUCCESS,
|
||||||
queue: qname,
|
queue: qname,
|
||||||
@ -1613,6 +1702,7 @@ export function listAggregatingTasksAsync(
|
|||||||
gname: string,
|
gname: string,
|
||||||
pageOpts?: PaginationOptions
|
pageOpts?: PaginationOptions
|
||||||
) {
|
) {
|
||||||
|
// TODO Add filter support
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
||||||
try {
|
try {
|
||||||
dispatch({
|
dispatch({
|
||||||
@ -1642,6 +1732,160 @@ export function listAggregatingTasksAsync(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface TasksFilter {
|
||||||
|
payloadQuery?: string;
|
||||||
|
resultQuery?: string;
|
||||||
|
payloadRegex?: RegExp;
|
||||||
|
resultRegex?: RegExp;
|
||||||
|
customJs?: string;
|
||||||
|
resultLimit?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseIfJson(str: string) {
|
||||||
|
try {
|
||||||
|
return JSON.parse(str);
|
||||||
|
} catch (e) {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function evalCustomJsFilter(js: string, task: TaskInfo): any {
|
||||||
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
||||||
|
const {
|
||||||
|
id,
|
||||||
|
queue,
|
||||||
|
type,
|
||||||
|
state,
|
||||||
|
start_time,
|
||||||
|
max_retry,
|
||||||
|
retried,
|
||||||
|
last_failed_at,
|
||||||
|
error_message,
|
||||||
|
next_process_at,
|
||||||
|
timeout_seconds,
|
||||||
|
deadline,
|
||||||
|
group,
|
||||||
|
completed_at,
|
||||||
|
ttl_seconds,
|
||||||
|
is_orphaned,
|
||||||
|
} = task;
|
||||||
|
// Parse payload and result into JSON for convenience
|
||||||
|
const payload = parseIfJson(task.payload);
|
||||||
|
const result = parseIfJson(task.result);
|
||||||
|
/* eslint-enable @typescript-eslint/no-unused-vars */
|
||||||
|
return eval(js); // eslint-disable-line no-eval
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterTask(filter: TasksFilter, task: TaskInfo): boolean {
|
||||||
|
if (
|
||||||
|
filter.payloadQuery != null &&
|
||||||
|
!task.payload.includes(filter.payloadQuery)
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (filter.resultQuery != null && !task.result.includes(filter.resultQuery)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
filter.payloadRegex != null &&
|
||||||
|
task.payload.match(filter.payloadRegex) == null
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
filter.resultRegex != null &&
|
||||||
|
task.result.match(filter.resultRegex) == null
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (filter.customJs != null) {
|
||||||
|
let customJsResult;
|
||||||
|
try {
|
||||||
|
customJsResult = evalCustomJsFilter(filter.customJs, task);
|
||||||
|
} catch (e) {
|
||||||
|
console.error(
|
||||||
|
"Custom JS filter error:",
|
||||||
|
e,
|
||||||
|
"task:",
|
||||||
|
task,
|
||||||
|
"skipping task."
|
||||||
|
);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// Custom JS can return anything so sanitize output into a boolean
|
||||||
|
return !!customJsResult;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterTasksAsync(
|
||||||
|
filter: TasksFilter,
|
||||||
|
fetchTasks: (
|
||||||
|
page?: number // page number (1 being the first page)
|
||||||
|
) => Promise<ListTasksResponse>
|
||||||
|
) {
|
||||||
|
return async (
|
||||||
|
dispatch: Dispatch<TasksActionTypes>,
|
||||||
|
getState: () => AppState
|
||||||
|
) => {
|
||||||
|
dispatch({ type: FILTER_TASKS_BEGIN });
|
||||||
|
|
||||||
|
let page = 1;
|
||||||
|
let processed = 0;
|
||||||
|
let finished = false;
|
||||||
|
do {
|
||||||
|
// Check if operation was cancelled, if so, return
|
||||||
|
if (getState().tasks.filterOp == null) return;
|
||||||
|
|
||||||
|
// Fetch next page
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = await fetchTasks(page);
|
||||||
|
} catch (error) {
|
||||||
|
dispatch({
|
||||||
|
type: FILTER_TASKS_ERROR,
|
||||||
|
error: toErrorString(error),
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
page++;
|
||||||
|
|
||||||
|
// Process page
|
||||||
|
const filterResults: TaskInfo[] = [];
|
||||||
|
for (const task of response.tasks) {
|
||||||
|
if (filterTask(filter, task)) {
|
||||||
|
filterResults.push(task);
|
||||||
|
processed++;
|
||||||
|
if (filter.resultLimit != null && processed >= filter.resultLimit) {
|
||||||
|
finished = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update state
|
||||||
|
dispatch({
|
||||||
|
type: FILTER_TASKS_PROGRESS,
|
||||||
|
processedTasks: response.tasks.length,
|
||||||
|
newStats: response.stats,
|
||||||
|
filterResults,
|
||||||
|
});
|
||||||
|
if (response.tasks.length === 0) finished = true;
|
||||||
|
} while (!finished);
|
||||||
|
|
||||||
|
dispatch({
|
||||||
|
type: FILTER_TASKS_SUCCESS,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function cancelFilterTasks() {
|
||||||
|
return {
|
||||||
|
type: FILTER_TASKS_CANCEL,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function cancelActiveTaskAsync(queue: string, taskId: string) {
|
export function cancelActiveTaskAsync(queue: string, taskId: string) {
|
||||||
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
return async (dispatch: Dispatch<TasksActionTypes>) => {
|
||||||
dispatch({ type: CANCEL_ACTIVE_TASK_BEGIN, queue, taskId });
|
dispatch({ type: CANCEL_ACTIVE_TASK_BEGIN, queue, taskId });
|
||||||
|
165
ui/src/components/TasksFilterDialog.tsx
Normal file
165
ui/src/components/TasksFilterDialog.tsx
Normal file
@ -0,0 +1,165 @@
|
|||||||
|
import React, {
|
||||||
|
ChangeEventHandler,
|
||||||
|
KeyboardEventHandler,
|
||||||
|
useState,
|
||||||
|
} from "react";
|
||||||
|
import Dialog from "@material-ui/core/Dialog";
|
||||||
|
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||||
|
import TextField from "@material-ui/core/TextField";
|
||||||
|
import DialogContent from "@material-ui/core/DialogContent";
|
||||||
|
import DialogActions from "@material-ui/core/DialogActions";
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import { TasksFilter } from "../actions/tasksActions";
|
||||||
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
|
|
||||||
|
interface TasksFilterDialogProps {
|
||||||
|
open: boolean;
|
||||||
|
onClose?: () => void;
|
||||||
|
onFilter?: (filter: TasksFilter) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TasksFilterDialog(props: TasksFilterDialogProps) {
|
||||||
|
const [customJs, setCustomJs] = useState("");
|
||||||
|
const [payloadQuery, setPayloadQuery] = useState("");
|
||||||
|
const [resultQuery, setResultQuery] = useState("");
|
||||||
|
const [payloadRegex, setPayloadRegex] = useState("");
|
||||||
|
const [resultRegex, setResultRegex] = useState("");
|
||||||
|
const [resultLimitStr, setResultLimitStr] = useState("");
|
||||||
|
const [resultLimit, setResultLimit] = useState(-1);
|
||||||
|
const [resultLimitError, setResultLimitError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
const buildFilter = () => {
|
||||||
|
if (resultLimitError != null) return null;
|
||||||
|
|
||||||
|
const filter: TasksFilter = {};
|
||||||
|
if (payloadQuery.trim().length > 0) filter.payloadQuery = payloadQuery;
|
||||||
|
if (resultQuery.trim().length > 0) filter.resultQuery = resultQuery;
|
||||||
|
if (payloadRegex.trim().length > 0)
|
||||||
|
filter.payloadRegex = RegExp(payloadRegex);
|
||||||
|
if (resultRegex.trim().length > 0) filter.resultRegex = RegExp(resultRegex);
|
||||||
|
if (customJs.trim().length > 0) filter.customJs = customJs;
|
||||||
|
if (resultLimit >= 0) filter.resultLimit = resultLimit;
|
||||||
|
return filter;
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleRunFilter = () => {
|
||||||
|
const filter = buildFilter();
|
||||||
|
if (filter != null) {
|
||||||
|
if (props.onClose != null) props.onClose();
|
||||||
|
if (props.onFilter != null) props.onFilter(filter);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleKeyDown: KeyboardEventHandler = (event) => {
|
||||||
|
if (event.key === "Enter") {
|
||||||
|
handleRunFilter();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleResultLimitChange: ChangeEventHandler<HTMLInputElement> = (
|
||||||
|
event
|
||||||
|
) => {
|
||||||
|
const value = event.target.value;
|
||||||
|
setResultLimitStr(value);
|
||||||
|
const trimmedValue = value.trim();
|
||||||
|
if (trimmedValue.length === 0) {
|
||||||
|
setResultLimit(-1);
|
||||||
|
setResultLimitError(null);
|
||||||
|
} else {
|
||||||
|
const parsed = parseInt(trimmedValue, 10);
|
||||||
|
if (isNaN(parsed) || parsed < 0) {
|
||||||
|
setResultLimit(-1);
|
||||||
|
setResultLimitError("Please enter a valid positive number.");
|
||||||
|
} else {
|
||||||
|
setResultLimit(parsed);
|
||||||
|
setResultLimitError(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={props.open} onClose={props.onClose}>
|
||||||
|
<DialogTitle>Filter tasks</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
<p style={{ marginTop: 0 }}>
|
||||||
|
Filter results are stored in browser memory, please ensure that your
|
||||||
|
filter does not return too many results.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
This feature may not behave correctly if the payload or result are
|
||||||
|
truncated due to <code>MaxPayloadLength</code> or{" "}
|
||||||
|
<code>MaxResultLength</code> being misconfigured.
|
||||||
|
</p>
|
||||||
|
</DialogContentText>
|
||||||
|
<TextField
|
||||||
|
label="Payload keyword"
|
||||||
|
placeholder="Search for tasks with payloads containing the specified text"
|
||||||
|
value={payloadQuery}
|
||||||
|
fullWidth
|
||||||
|
variant="filled"
|
||||||
|
onChange={(e) => setPayloadQuery(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Result keyword"
|
||||||
|
placeholder="Search for tasks with results containing the specified text"
|
||||||
|
value={resultQuery}
|
||||||
|
fullWidth
|
||||||
|
variant="filled"
|
||||||
|
onChange={(e) => setResultQuery(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Payload regex"
|
||||||
|
placeholder="Search for tasks with payloads matching the specified regex"
|
||||||
|
value={payloadRegex}
|
||||||
|
fullWidth
|
||||||
|
variant="filled"
|
||||||
|
onChange={(e) => setPayloadRegex(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="Query regex"
|
||||||
|
placeholder="Search for tasks with results matching the specified regex"
|
||||||
|
value={resultRegex}
|
||||||
|
fullWidth
|
||||||
|
variant="filled"
|
||||||
|
onChange={(e) => setResultRegex(e.target.value)}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label="JavaScript expression"
|
||||||
|
placeholder={
|
||||||
|
"Example:\npayload.auth.user_id === 'f094d054-64c0-42a3-a413-3f5a4eae0088'\n\nAvailable variables: " +
|
||||||
|
"id, queue, type, state, start_time, max_retry, retried, last_failed_at, error_message, next_process_at, " +
|
||||||
|
"timeout_seconds, deadline, group, completed_at, ttl_seconds, is_orphaned, payload, result"
|
||||||
|
}
|
||||||
|
value={customJs}
|
||||||
|
multiline
|
||||||
|
minRows={7}
|
||||||
|
fullWidth
|
||||||
|
variant="filled"
|
||||||
|
onChange={(e) => setCustomJs(e.target.value)}
|
||||||
|
/>
|
||||||
|
<TextField
|
||||||
|
label={"Limit number of results"}
|
||||||
|
placeholder={"Leave blank for no limit"}
|
||||||
|
variant="filled"
|
||||||
|
fullWidth
|
||||||
|
value={resultLimitStr}
|
||||||
|
onKeyDown={handleKeyDown}
|
||||||
|
onChange={handleResultLimitChange}
|
||||||
|
error={resultLimitError != null}
|
||||||
|
helperText={resultLimitError}
|
||||||
|
/>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={props.onClose}>Cancel</Button>
|
||||||
|
<Button onClick={handleRunFilter}>Run</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TasksFilterDialog;
|
55
ui/src/components/TasksFilterProgressDialog.tsx
Normal file
55
ui/src/components/TasksFilterProgressDialog.tsx
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||||
|
import React from "react";
|
||||||
|
import DialogActions from "@material-ui/core/DialogActions";
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import Dialog from "@material-ui/core/Dialog";
|
||||||
|
import DialogContent from "@material-ui/core/DialogContent";
|
||||||
|
import LinearProgress from "@material-ui/core/LinearProgress";
|
||||||
|
import { AppState } from "../store";
|
||||||
|
import { connect, ConnectedProps } from "react-redux";
|
||||||
|
import DialogContentText from "@material-ui/core/DialogContentText";
|
||||||
|
import { cancelFilterTasks } from "../actions/tasksActions";
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState) {
|
||||||
|
const filterOp = state.tasks.filterOp;
|
||||||
|
return {
|
||||||
|
open: filterOp != null && !filterOp.done,
|
||||||
|
processedTasks: filterOp?.processedTasks ?? 0,
|
||||||
|
matches: filterOp?.result?.length ?? 0,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const mapDispatchToProps = {
|
||||||
|
cancelFilterTasks,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapStateToProps, mapDispatchToProps);
|
||||||
|
type ReduxProps = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
|
interface TasksFilterDialogProps extends ReduxProps {
|
||||||
|
totalTasks: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TasksFilterDialog(props: TasksFilterDialogProps) {
|
||||||
|
let progress;
|
||||||
|
if (props.totalTasks > 0) progress = props.processedTasks / props.totalTasks;
|
||||||
|
else progress = 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={props.open} fullWidth>
|
||||||
|
<DialogTitle>Running filter</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<DialogContentText>
|
||||||
|
Processed {props.processedTasks} of {props.totalTasks} total tasks,{" "}
|
||||||
|
{props.matches} matches so far.
|
||||||
|
</DialogContentText>
|
||||||
|
<LinearProgress variant="determinate" value={progress * 100} />
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button onClick={props.cancelFilterTasks}>Cancel</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connector(TasksFilterDialog);
|
@ -26,6 +26,18 @@ import { TaskInfoExtended } from "../reducers/tasksReducer";
|
|||||||
import { TableColumn } from "../types/table";
|
import { TableColumn } from "../types/table";
|
||||||
import { PaginationOptions } from "../api";
|
import { PaginationOptions } from "../api";
|
||||||
import { TaskState } from "../types/taskState";
|
import { TaskState } from "../types/taskState";
|
||||||
|
import TasksFilterProgressDialog from "./TasksFilterProgressDialog";
|
||||||
|
import { AppState } from "../store";
|
||||||
|
import { connect, ConnectedProps } from "react-redux";
|
||||||
|
|
||||||
|
function mapStateToProps(state: AppState) {
|
||||||
|
return {
|
||||||
|
filterEnabled: state.tasks.filterOp?.done ?? false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const connector = connect(mapStateToProps);
|
||||||
|
type ReduxProps = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
table: {
|
table: {
|
||||||
@ -43,7 +55,7 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface Props {
|
interface Props extends ReduxProps {
|
||||||
queue: string; // name of the queue.
|
queue: string; // name of the queue.
|
||||||
totalTaskCount: number; // totoal number of tasks in the given state.
|
totalTaskCount: number; // totoal number of tasks in the given state.
|
||||||
taskState: TaskState;
|
taskState: TaskState;
|
||||||
@ -75,12 +87,15 @@ interface Props {
|
|||||||
renderRow: (rowProps: RowProps) => JSX.Element;
|
renderRow: (rowProps: RowProps) => JSX.Element;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default function TasksTable(props: Props) {
|
function TasksTable(props: Props) {
|
||||||
const { pollInterval, listTasks, queue, pageSize } = props;
|
const { pollInterval, listTasks, queue, pageSize } = props;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const [page, setPage] = useState(0);
|
const [page, setPage] = useState(0);
|
||||||
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
const [selectedIds, setSelectedIds] = useState<string[]>([]);
|
||||||
const [activeTaskId, setActiveTaskId] = useState<string>("");
|
const [activeTaskId, setActiveTaskId] = useState<string>("");
|
||||||
|
const [lastFilterEnabled, setLastFilterEnabled] = useState(
|
||||||
|
props.filterEnabled
|
||||||
|
);
|
||||||
|
|
||||||
const handlePageChange = (
|
const handlePageChange = (
|
||||||
event: React.MouseEvent<HTMLButtonElement> | null,
|
event: React.MouseEvent<HTMLButtonElement> | null,
|
||||||
@ -123,6 +138,8 @@ export default function TasksTable(props: Props) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let allActions = [];
|
let allActions = [];
|
||||||
|
if (!props.filterEnabled) {
|
||||||
|
// Protect against using all actions while filter enabled
|
||||||
if (props.deleteAllTasks) {
|
if (props.deleteAllTasks) {
|
||||||
allActions.push({
|
allActions.push({
|
||||||
label: "Delete All",
|
label: "Delete All",
|
||||||
@ -151,6 +168,7 @@ export default function TasksTable(props: Props) {
|
|||||||
disabled: props.allActionPending,
|
disabled: props.allActionPending,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let batchActions = [];
|
let batchActions = [];
|
||||||
if (props.batchDeleteTasks) {
|
if (props.batchDeleteTasks) {
|
||||||
@ -193,6 +211,12 @@ export default function TasksTable(props: Props) {
|
|||||||
|
|
||||||
usePolling(fetchData, pollInterval);
|
usePolling(fetchData, pollInterval);
|
||||||
|
|
||||||
|
if (props.filterEnabled !== lastFilterEnabled) {
|
||||||
|
setLastFilterEnabled(props.filterEnabled);
|
||||||
|
setPage(0);
|
||||||
|
fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
if (props.error.length > 0) {
|
if (props.error.length > 0) {
|
||||||
return (
|
return (
|
||||||
<Alert severity="error" className={classes.alert}>
|
<Alert severity="error" className={classes.alert}>
|
||||||
@ -320,6 +344,7 @@ export default function TasksTable(props: Props) {
|
|||||||
</TableFooter>
|
</TableFooter>
|
||||||
</Table>
|
</Table>
|
||||||
</TableContainer>
|
</TableContainer>
|
||||||
|
<TasksFilterProgressDialog totalTasks={props.totalTaskCount} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -376,3 +401,5 @@ export interface RowProps {
|
|||||||
onActionCellEnter: () => void;
|
onActionCellEnter: () => void;
|
||||||
onActionCellLeave: () => void;
|
onActionCellLeave: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export default connector(TasksTable);
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import { connect, ConnectedProps } from "react-redux";
|
import { connect, ConnectedProps } from "react-redux";
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
@ -18,6 +18,24 @@ import { queueDetailsPath, taskDetailsPath } from "../paths";
|
|||||||
import { QueueInfo } from "../reducers/queuesReducer";
|
import { QueueInfo } from "../reducers/queuesReducer";
|
||||||
import { AppState } from "../store";
|
import { AppState } from "../store";
|
||||||
import { isDarkTheme } from "../theme";
|
import { isDarkTheme } from "../theme";
|
||||||
|
import { Divider } from "@material-ui/core";
|
||||||
|
import FilterListIcon from "@material-ui/icons/FilterList";
|
||||||
|
import TasksFilterDialog from "./TasksFilterDialog";
|
||||||
|
import {
|
||||||
|
listActiveTasks,
|
||||||
|
listArchivedTasks,
|
||||||
|
listCompletedTasks,
|
||||||
|
listPendingTasks,
|
||||||
|
listRetryTasks,
|
||||||
|
listScheduledTasks,
|
||||||
|
ListTasksResponse,
|
||||||
|
PaginationOptions,
|
||||||
|
} from "../api";
|
||||||
|
import {
|
||||||
|
cancelFilterTasks,
|
||||||
|
filterTasksAsync,
|
||||||
|
TasksFilter,
|
||||||
|
} from "../actions/tasksActions";
|
||||||
|
|
||||||
interface TabPanelProps {
|
interface TabPanelProps {
|
||||||
children?: React.ReactNode;
|
children?: React.ReactNode;
|
||||||
@ -65,10 +83,18 @@ function mapStatetoProps(state: AppState, ownProps: Props) {
|
|||||||
failed: 0,
|
failed: 0,
|
||||||
timestamp: "n/a",
|
timestamp: "n/a",
|
||||||
};
|
};
|
||||||
return { currentStats };
|
const filterOp = state.tasks.filterOp;
|
||||||
|
const filterActive = filterOp != null;
|
||||||
|
const filterCount = filterOp?.done === true ? filterOp?.result?.length : null;
|
||||||
|
return { currentStats, filterActive, filterCount };
|
||||||
}
|
}
|
||||||
|
|
||||||
const connector = connect(mapStatetoProps);
|
const mapDispatchToProps = {
|
||||||
|
filterTasksAsync,
|
||||||
|
cancelFilterTasks,
|
||||||
|
};
|
||||||
|
|
||||||
|
const connector = connect(mapStatetoProps, mapDispatchToProps);
|
||||||
|
|
||||||
type ReduxProps = ConnectedProps<typeof connector>;
|
type ReduxProps = ConnectedProps<typeof connector>;
|
||||||
|
|
||||||
@ -146,24 +172,92 @@ const useStyles = makeStyles((theme) => ({
|
|||||||
}));
|
}));
|
||||||
|
|
||||||
function TasksTableContainer(props: Props & ReduxProps) {
|
function TasksTableContainer(props: Props & ReduxProps) {
|
||||||
const { currentStats } = props;
|
const { currentStats, cancelFilterTasks } = props;
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const chips = [
|
const chips = [
|
||||||
{ key: "active", label: "Active", count: currentStats.active },
|
{
|
||||||
{ key: "pending", label: "Pending", count: currentStats.pending },
|
key: "active",
|
||||||
|
label: "Active",
|
||||||
|
count: currentStats.active,
|
||||||
|
fetcher: listActiveTasks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "pending",
|
||||||
|
label: "Pending",
|
||||||
|
count: currentStats.pending,
|
||||||
|
fetcher: listPendingTasks,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "aggregating",
|
key: "aggregating",
|
||||||
label: "Aggregating",
|
label: "Aggregating",
|
||||||
count: currentStats.aggregating,
|
count: currentStats.aggregating,
|
||||||
|
fetcher: null, // TODO Support aggregating table
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "scheduled",
|
||||||
|
label: "Scheduled",
|
||||||
|
count: currentStats.scheduled,
|
||||||
|
fetcher: listScheduledTasks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "retry",
|
||||||
|
label: "Retry",
|
||||||
|
count: currentStats.retry,
|
||||||
|
fetcher: listRetryTasks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "archived",
|
||||||
|
label: "Archived",
|
||||||
|
count: currentStats.archived,
|
||||||
|
fetcher: listArchivedTasks,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: "completed",
|
||||||
|
label: "Completed",
|
||||||
|
count: currentStats.completed,
|
||||||
|
fetcher: listCompletedTasks,
|
||||||
},
|
},
|
||||||
{ key: "scheduled", label: "Scheduled", count: currentStats.scheduled },
|
|
||||||
{ key: "retry", label: "Retry", count: currentStats.retry },
|
|
||||||
{ key: "archived", label: "Archived", count: currentStats.archived },
|
|
||||||
{ key: "completed", label: "Completed", count: currentStats.completed },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
const [searchQuery, setSearchQuery] = useState<string>("");
|
const [searchQuery, setSearchQuery] = useState<string>("");
|
||||||
|
const [filterModalOpen, setFilterModalOpen] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
// Clear filter when the user selects a different task status
|
||||||
|
cancelFilterTasks();
|
||||||
|
return () => {
|
||||||
|
// Clear filter when leaving the page
|
||||||
|
cancelFilterTasks();
|
||||||
|
};
|
||||||
|
}, [props.selected, cancelFilterTasks]);
|
||||||
|
|
||||||
|
const taskPageFetcher = (
|
||||||
|
fetcherApi: (
|
||||||
|
qname: string,
|
||||||
|
pageOpts?: PaginationOptions
|
||||||
|
) => Promise<ListTasksResponse>
|
||||||
|
) => {
|
||||||
|
const qname = props.queue;
|
||||||
|
return async (page?: number) => {
|
||||||
|
return fetcherApi(qname, {
|
||||||
|
page,
|
||||||
|
size: 10_000, // TODO Choose a value here intelligently?
|
||||||
|
});
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const dispatchFilterAction = (filter: TasksFilter) => {
|
||||||
|
const fetcherApi = chips.find((c) => c.key === props.selected)?.fetcher;
|
||||||
|
if (fetcherApi == null) {
|
||||||
|
console.error(
|
||||||
|
"Failed to find fetcher API for selected task type:",
|
||||||
|
props.selected
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
props.filterTasksAsync(filter, taskPageFetcher(fetcherApi));
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Paper variant="outlined" className={classes.container}>
|
<Paper variant="outlined" className={classes.container}>
|
||||||
@ -187,6 +281,25 @@ function TasksTableContainer(props: Props & ReduxProps) {
|
|||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
<Divider orientation="vertical" variant="middle" flexItem />
|
||||||
|
<div>
|
||||||
|
{chips.find((c) => c.key === props.selected)?.fetcher && (
|
||||||
|
<Chip
|
||||||
|
icon={<FilterListIcon />}
|
||||||
|
label="Filter"
|
||||||
|
onClick={() => setFilterModalOpen(true)}
|
||||||
|
onDelete={
|
||||||
|
props.filterActive ? props.cancelFilterTasks : undefined
|
||||||
|
}
|
||||||
|
color={props.filterActive ? "primary" : "default"}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<TasksFilterDialog
|
||||||
|
open={filterModalOpen}
|
||||||
|
onClose={() => setFilterModalOpen(false)}
|
||||||
|
onFilter={dispatchFilterAction}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
<div className={classes.searchbar}>
|
<div className={classes.searchbar}>
|
||||||
<div className={classes.search}>
|
<div className={classes.search}>
|
||||||
<div className={classes.searchIcon}>
|
<div className={classes.searchIcon}>
|
||||||
@ -219,13 +332,13 @@ function TasksTableContainer(props: Props & ReduxProps) {
|
|||||||
<TabPanel value="active" selected={props.selected}>
|
<TabPanel value="active" selected={props.selected}>
|
||||||
<ActiveTasksTable
|
<ActiveTasksTable
|
||||||
queue={props.queue}
|
queue={props.queue}
|
||||||
totalTaskCount={currentStats.active}
|
totalTaskCount={props.filterCount ?? currentStats.active}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="pending" selected={props.selected}>
|
<TabPanel value="pending" selected={props.selected}>
|
||||||
<PendingTasksTable
|
<PendingTasksTable
|
||||||
queue={props.queue}
|
queue={props.queue}
|
||||||
totalTaskCount={currentStats.pending}
|
totalTaskCount={props.filterCount ?? currentStats.pending}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="aggregating" selected={props.selected}>
|
<TabPanel value="aggregating" selected={props.selected}>
|
||||||
@ -234,25 +347,25 @@ function TasksTableContainer(props: Props & ReduxProps) {
|
|||||||
<TabPanel value="scheduled" selected={props.selected}>
|
<TabPanel value="scheduled" selected={props.selected}>
|
||||||
<ScheduledTasksTable
|
<ScheduledTasksTable
|
||||||
queue={props.queue}
|
queue={props.queue}
|
||||||
totalTaskCount={currentStats.scheduled}
|
totalTaskCount={props.filterCount ?? currentStats.scheduled}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="retry" selected={props.selected}>
|
<TabPanel value="retry" selected={props.selected}>
|
||||||
<RetryTasksTable
|
<RetryTasksTable
|
||||||
queue={props.queue}
|
queue={props.queue}
|
||||||
totalTaskCount={currentStats.retry}
|
totalTaskCount={props.filterCount ?? currentStats.retry}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="archived" selected={props.selected}>
|
<TabPanel value="archived" selected={props.selected}>
|
||||||
<ArchivedTasksTable
|
<ArchivedTasksTable
|
||||||
queue={props.queue}
|
queue={props.queue}
|
||||||
totalTaskCount={currentStats.archived}
|
totalTaskCount={props.filterCount ?? currentStats.archived}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
<TabPanel value="completed" selected={props.selected}>
|
<TabPanel value="completed" selected={props.selected}>
|
||||||
<CompletedTasksTable
|
<CompletedTasksTable
|
||||||
queue={props.queue}
|
queue={props.queue}
|
||||||
totalTaskCount={currentStats.completed}
|
totalTaskCount={props.filterCount ?? currentStats.completed}
|
||||||
/>
|
/>
|
||||||
</TabPanel>
|
</TabPanel>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
149
ui/src/reducers/filterReducer.ts
Normal file
149
ui/src/reducers/filterReducer.ts
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
import {
|
||||||
|
ARCHIVE_AGGREGATING_TASK_SUCCESS,
|
||||||
|
ARCHIVE_PENDING_TASK_SUCCESS,
|
||||||
|
ARCHIVE_RETRY_TASK_SUCCESS,
|
||||||
|
ARCHIVE_SCHEDULED_TASK_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_PENDING_TASKS_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_RETRY_TASKS_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_COMPLETED_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_PENDING_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_RETRY_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_RETRY_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
DELETE_AGGREGATING_TASK_SUCCESS,
|
||||||
|
DELETE_ARCHIVED_TASK_SUCCESS,
|
||||||
|
DELETE_COMPLETED_TASK_SUCCESS,
|
||||||
|
DELETE_PENDING_TASK_SUCCESS,
|
||||||
|
DELETE_RETRY_TASK_SUCCESS,
|
||||||
|
DELETE_SCHEDULED_TASK_SUCCESS,
|
||||||
|
FILTER_TASKS_BEGIN,
|
||||||
|
FILTER_TASKS_CANCEL,
|
||||||
|
FILTER_TASKS_PROGRESS,
|
||||||
|
FILTER_TASKS_SUCCESS,
|
||||||
|
RUN_AGGREGATING_TASK_SUCCESS,
|
||||||
|
RUN_ARCHIVED_TASK_SUCCESS,
|
||||||
|
RUN_RETRY_TASK_SUCCESS,
|
||||||
|
RUN_SCHEDULED_TASK_SUCCESS,
|
||||||
|
TasksActionTypes,
|
||||||
|
} from "../actions/tasksActions";
|
||||||
|
import { TasksState } from "./tasksReducer";
|
||||||
|
import { TaskInfo } from "../api";
|
||||||
|
|
||||||
|
function modifyFilterResults(
|
||||||
|
state: TasksState,
|
||||||
|
action: (tasks: TaskInfo[]) => TaskInfo[]
|
||||||
|
): TasksState {
|
||||||
|
const filterOp = state.filterOp;
|
||||||
|
if (filterOp == null || !filterOp.done) return state;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
filterOp: {
|
||||||
|
...filterOp,
|
||||||
|
result: action(filterOp.result),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filterReducer(
|
||||||
|
state: TasksState,
|
||||||
|
action: TasksActionTypes
|
||||||
|
): TasksState {
|
||||||
|
switch (action.type) {
|
||||||
|
case FILTER_TASKS_BEGIN:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
filterOp: {
|
||||||
|
done: false,
|
||||||
|
processedTasks: 0,
|
||||||
|
result: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
case FILTER_TASKS_PROGRESS: {
|
||||||
|
const filterOp = state.filterOp;
|
||||||
|
if (filterOp == null) return state;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
filterOp: {
|
||||||
|
done: false,
|
||||||
|
processedTasks: filterOp.processedTasks + action.processedTasks,
|
||||||
|
result: filterOp.result.concat(action.filterResults),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case FILTER_TASKS_SUCCESS: {
|
||||||
|
const filterOp = state.filterOp;
|
||||||
|
if (filterOp == null) return state;
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
filterOp: {
|
||||||
|
...filterOp,
|
||||||
|
done: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case FILTER_TASKS_CANCEL: {
|
||||||
|
const { filterOp, ...newState } = state;
|
||||||
|
return newState;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Replicate actions made to queues to filter results
|
||||||
|
|
||||||
|
case ARCHIVE_PENDING_TASK_SUCCESS:
|
||||||
|
case DELETE_PENDING_TASK_SUCCESS:
|
||||||
|
case DELETE_COMPLETED_TASK_SUCCESS:
|
||||||
|
case RUN_SCHEDULED_TASK_SUCCESS:
|
||||||
|
case ARCHIVE_SCHEDULED_TASK_SUCCESS:
|
||||||
|
case DELETE_SCHEDULED_TASK_SUCCESS:
|
||||||
|
case RUN_AGGREGATING_TASK_SUCCESS:
|
||||||
|
case ARCHIVE_AGGREGATING_TASK_SUCCESS:
|
||||||
|
case DELETE_AGGREGATING_TASK_SUCCESS:
|
||||||
|
case RUN_RETRY_TASK_SUCCESS:
|
||||||
|
case ARCHIVE_RETRY_TASK_SUCCESS:
|
||||||
|
case DELETE_RETRY_TASK_SUCCESS:
|
||||||
|
case RUN_ARCHIVED_TASK_SUCCESS:
|
||||||
|
case DELETE_ARCHIVED_TASK_SUCCESS:
|
||||||
|
return modifyFilterResults(state, (tasks) =>
|
||||||
|
tasks.filter((task) => task.id !== action.taskId)
|
||||||
|
);
|
||||||
|
|
||||||
|
case BATCH_ARCHIVE_PENDING_TASKS_SUCCESS:
|
||||||
|
case BATCH_ARCHIVE_SCHEDULED_TASKS_SUCCESS:
|
||||||
|
case BATCH_ARCHIVE_AGGREGATING_TASKS_SUCCESS:
|
||||||
|
case BATCH_ARCHIVE_RETRY_TASKS_SUCCESS:
|
||||||
|
return modifyFilterResults(state, (tasks) =>
|
||||||
|
tasks.filter((task) => !action.payload.archived_ids.includes(task.id))
|
||||||
|
);
|
||||||
|
|
||||||
|
case BATCH_DELETE_COMPLETED_TASKS_SUCCESS:
|
||||||
|
case BATCH_DELETE_PENDING_TASKS_SUCCESS:
|
||||||
|
case BATCH_DELETE_SCHEDULED_TASKS_SUCCESS:
|
||||||
|
case BATCH_DELETE_AGGREGATING_TASKS_SUCCESS:
|
||||||
|
case BATCH_DELETE_RETRY_TASKS_SUCCESS:
|
||||||
|
case BATCH_DELETE_ARCHIVED_TASKS_SUCCESS:
|
||||||
|
return modifyFilterResults(state, (tasks) =>
|
||||||
|
tasks.filter((task) => !action.payload.deleted_ids.includes(task.id))
|
||||||
|
);
|
||||||
|
|
||||||
|
case BATCH_RUN_SCHEDULED_TASKS_SUCCESS:
|
||||||
|
case BATCH_RUN_AGGREGATING_TASKS_SUCCESS:
|
||||||
|
case BATCH_RUN_RETRY_TASKS_SUCCESS:
|
||||||
|
case BATCH_RUN_ARCHIVED_TASKS_SUCCESS:
|
||||||
|
return modifyFilterResults(state, (tasks) =>
|
||||||
|
tasks.filter((task) => !action.payload.pending_ids.includes(task.id))
|
||||||
|
);
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
@ -3,44 +3,64 @@ import {
|
|||||||
LIST_GROUPS_SUCCESS,
|
LIST_GROUPS_SUCCESS,
|
||||||
} from "../actions/groupsActions";
|
} from "../actions/groupsActions";
|
||||||
import {
|
import {
|
||||||
LIST_QUEUES_SUCCESS,
|
|
||||||
LIST_QUEUES_BEGIN,
|
|
||||||
QueuesActionTypes,
|
|
||||||
PAUSE_QUEUE_BEGIN,
|
|
||||||
PAUSE_QUEUE_SUCCESS,
|
|
||||||
PAUSE_QUEUE_ERROR,
|
|
||||||
RESUME_QUEUE_BEGIN,
|
|
||||||
RESUME_QUEUE_ERROR,
|
|
||||||
RESUME_QUEUE_SUCCESS,
|
|
||||||
DELETE_QUEUE_BEGIN,
|
DELETE_QUEUE_BEGIN,
|
||||||
DELETE_QUEUE_ERROR,
|
DELETE_QUEUE_ERROR,
|
||||||
DELETE_QUEUE_SUCCESS,
|
DELETE_QUEUE_SUCCESS,
|
||||||
|
LIST_QUEUES_BEGIN,
|
||||||
LIST_QUEUES_ERROR,
|
LIST_QUEUES_ERROR,
|
||||||
|
LIST_QUEUES_SUCCESS,
|
||||||
|
PAUSE_QUEUE_BEGIN,
|
||||||
|
PAUSE_QUEUE_ERROR,
|
||||||
|
PAUSE_QUEUE_SUCCESS,
|
||||||
|
QueuesActionTypes,
|
||||||
|
RESUME_QUEUE_BEGIN,
|
||||||
|
RESUME_QUEUE_ERROR,
|
||||||
|
RESUME_QUEUE_SUCCESS,
|
||||||
} from "../actions/queuesActions";
|
} from "../actions/queuesActions";
|
||||||
import {
|
import {
|
||||||
BATCH_DELETE_ARCHIVED_TASKS_SUCCESS,
|
ARCHIVE_AGGREGATING_TASK_SUCCESS,
|
||||||
BATCH_DELETE_RETRY_TASKS_SUCCESS,
|
ARCHIVE_ALL_AGGREGATING_TASKS_SUCCESS,
|
||||||
BATCH_DELETE_SCHEDULED_TASKS_SUCCESS,
|
ARCHIVE_ALL_PENDING_TASKS_SUCCESS,
|
||||||
|
ARCHIVE_ALL_RETRY_TASKS_SUCCESS,
|
||||||
|
ARCHIVE_ALL_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
ARCHIVE_PENDING_TASK_SUCCESS,
|
||||||
|
ARCHIVE_RETRY_TASK_SUCCESS,
|
||||||
|
ARCHIVE_SCHEDULED_TASK_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_PENDING_TASKS_SUCCESS,
|
||||||
BATCH_ARCHIVE_RETRY_TASKS_SUCCESS,
|
BATCH_ARCHIVE_RETRY_TASKS_SUCCESS,
|
||||||
BATCH_ARCHIVE_SCHEDULED_TASKS_SUCCESS,
|
BATCH_ARCHIVE_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_COMPLETED_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_PENDING_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_RETRY_TASKS_SUCCESS,
|
||||||
|
BATCH_DELETE_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_AGGREGATING_TASKS_SUCCESS,
|
||||||
BATCH_RUN_ARCHIVED_TASKS_SUCCESS,
|
BATCH_RUN_ARCHIVED_TASKS_SUCCESS,
|
||||||
BATCH_RUN_RETRY_TASKS_SUCCESS,
|
BATCH_RUN_RETRY_TASKS_SUCCESS,
|
||||||
BATCH_RUN_SCHEDULED_TASKS_SUCCESS,
|
BATCH_RUN_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
DELETE_AGGREGATING_TASK_SUCCESS,
|
||||||
|
DELETE_ALL_AGGREGATING_TASKS_SUCCESS,
|
||||||
DELETE_ALL_ARCHIVED_TASKS_SUCCESS,
|
DELETE_ALL_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_COMPLETED_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_PENDING_TASKS_SUCCESS,
|
||||||
DELETE_ALL_RETRY_TASKS_SUCCESS,
|
DELETE_ALL_RETRY_TASKS_SUCCESS,
|
||||||
DELETE_ALL_SCHEDULED_TASKS_SUCCESS,
|
DELETE_ALL_SCHEDULED_TASKS_SUCCESS,
|
||||||
DELETE_ARCHIVED_TASK_SUCCESS,
|
DELETE_ARCHIVED_TASK_SUCCESS,
|
||||||
|
DELETE_COMPLETED_TASK_SUCCESS,
|
||||||
|
DELETE_PENDING_TASK_SUCCESS,
|
||||||
DELETE_RETRY_TASK_SUCCESS,
|
DELETE_RETRY_TASK_SUCCESS,
|
||||||
DELETE_SCHEDULED_TASK_SUCCESS,
|
DELETE_SCHEDULED_TASK_SUCCESS,
|
||||||
ARCHIVE_ALL_RETRY_TASKS_SUCCESS,
|
FILTER_TASKS_PROGRESS,
|
||||||
ARCHIVE_ALL_SCHEDULED_TASKS_SUCCESS,
|
|
||||||
ARCHIVE_RETRY_TASK_SUCCESS,
|
|
||||||
ARCHIVE_SCHEDULED_TASK_SUCCESS,
|
|
||||||
LIST_ACTIVE_TASKS_SUCCESS,
|
LIST_ACTIVE_TASKS_SUCCESS,
|
||||||
|
LIST_AGGREGATING_TASKS_SUCCESS,
|
||||||
LIST_ARCHIVED_TASKS_SUCCESS,
|
LIST_ARCHIVED_TASKS_SUCCESS,
|
||||||
LIST_PENDING_TASKS_SUCCESS,
|
LIST_PENDING_TASKS_SUCCESS,
|
||||||
LIST_RETRY_TASKS_SUCCESS,
|
LIST_RETRY_TASKS_SUCCESS,
|
||||||
LIST_SCHEDULED_TASKS_SUCCESS,
|
LIST_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
RUN_AGGREGATING_TASK_SUCCESS,
|
||||||
|
RUN_ALL_AGGREGATING_TASKS_SUCCESS,
|
||||||
RUN_ALL_ARCHIVED_TASKS_SUCCESS,
|
RUN_ALL_ARCHIVED_TASKS_SUCCESS,
|
||||||
RUN_ALL_RETRY_TASKS_SUCCESS,
|
RUN_ALL_RETRY_TASKS_SUCCESS,
|
||||||
RUN_ALL_SCHEDULED_TASKS_SUCCESS,
|
RUN_ALL_SCHEDULED_TASKS_SUCCESS,
|
||||||
@ -48,25 +68,6 @@ import {
|
|||||||
RUN_RETRY_TASK_SUCCESS,
|
RUN_RETRY_TASK_SUCCESS,
|
||||||
RUN_SCHEDULED_TASK_SUCCESS,
|
RUN_SCHEDULED_TASK_SUCCESS,
|
||||||
TasksActionTypes,
|
TasksActionTypes,
|
||||||
ARCHIVE_PENDING_TASK_SUCCESS,
|
|
||||||
DELETE_PENDING_TASK_SUCCESS,
|
|
||||||
BATCH_ARCHIVE_PENDING_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_PENDING_TASKS_SUCCESS,
|
|
||||||
ARCHIVE_ALL_PENDING_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_PENDING_TASKS_SUCCESS,
|
|
||||||
DELETE_COMPLETED_TASK_SUCCESS,
|
|
||||||
DELETE_ALL_COMPLETED_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_COMPLETED_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
ARCHIVE_ALL_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
RUN_ALL_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
BATCH_RUN_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
BATCH_ARCHIVE_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
DELETE_AGGREGATING_TASK_SUCCESS,
|
|
||||||
RUN_AGGREGATING_TASK_SUCCESS,
|
|
||||||
ARCHIVE_AGGREGATING_TASK_SUCCESS,
|
|
||||||
LIST_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
} from "../actions/tasksActions";
|
} from "../actions/tasksActions";
|
||||||
import { Queue } from "../api";
|
import { Queue } from "../api";
|
||||||
|
|
||||||
@ -190,6 +191,17 @@ function queuesReducer(
|
|||||||
return { ...state, data: newData };
|
return { ...state, data: newData };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case FILTER_TASKS_PROGRESS: {
|
||||||
|
const newData = state.data
|
||||||
|
.filter((queueInfo) => queueInfo.name !== action.newStats.queue)
|
||||||
|
.concat({
|
||||||
|
name: action.newStats.queue,
|
||||||
|
currentStats: action.newStats,
|
||||||
|
requestPending: false,
|
||||||
|
});
|
||||||
|
return { ...state, data: newData };
|
||||||
|
}
|
||||||
|
|
||||||
case RUN_AGGREGATING_TASK_SUCCESS: {
|
case RUN_AGGREGATING_TASK_SUCCESS: {
|
||||||
const newData = state.data.map((queueInfo) => {
|
const newData = state.data.map((queueInfo) => {
|
||||||
if (queueInfo.name !== action.queue) {
|
if (queueInfo.name !== action.queue) {
|
||||||
|
@ -1,166 +1,167 @@
|
|||||||
import {
|
import {
|
||||||
LIST_ACTIVE_TASKS_BEGIN,
|
ARCHIVE_AGGREGATING_TASK_BEGIN,
|
||||||
LIST_ACTIVE_TASKS_SUCCESS,
|
ARCHIVE_AGGREGATING_TASK_ERROR,
|
||||||
LIST_ACTIVE_TASKS_ERROR,
|
ARCHIVE_AGGREGATING_TASK_SUCCESS,
|
||||||
TasksActionTypes,
|
ARCHIVE_ALL_AGGREGATING_TASKS_BEGIN,
|
||||||
LIST_PENDING_TASKS_BEGIN,
|
ARCHIVE_ALL_AGGREGATING_TASKS_ERROR,
|
||||||
LIST_PENDING_TASKS_SUCCESS,
|
ARCHIVE_ALL_AGGREGATING_TASKS_SUCCESS,
|
||||||
LIST_PENDING_TASKS_ERROR,
|
ARCHIVE_ALL_PENDING_TASKS_BEGIN,
|
||||||
LIST_SCHEDULED_TASKS_BEGIN,
|
ARCHIVE_ALL_PENDING_TASKS_ERROR,
|
||||||
LIST_SCHEDULED_TASKS_SUCCESS,
|
ARCHIVE_ALL_PENDING_TASKS_SUCCESS,
|
||||||
LIST_SCHEDULED_TASKS_ERROR,
|
ARCHIVE_ALL_RETRY_TASKS_BEGIN,
|
||||||
LIST_RETRY_TASKS_BEGIN,
|
ARCHIVE_ALL_RETRY_TASKS_ERROR,
|
||||||
LIST_RETRY_TASKS_SUCCESS,
|
ARCHIVE_ALL_RETRY_TASKS_SUCCESS,
|
||||||
LIST_RETRY_TASKS_ERROR,
|
|
||||||
LIST_ARCHIVED_TASKS_BEGIN,
|
|
||||||
LIST_ARCHIVED_TASKS_SUCCESS,
|
|
||||||
LIST_ARCHIVED_TASKS_ERROR,
|
|
||||||
LIST_COMPLETED_TASKS_BEGIN,
|
|
||||||
LIST_COMPLETED_TASKS_SUCCESS,
|
|
||||||
LIST_COMPLETED_TASKS_ERROR,
|
|
||||||
CANCEL_ACTIVE_TASK_BEGIN,
|
|
||||||
CANCEL_ACTIVE_TASK_SUCCESS,
|
|
||||||
CANCEL_ACTIVE_TASK_ERROR,
|
|
||||||
DELETE_RETRY_TASK_BEGIN,
|
|
||||||
DELETE_RETRY_TASK_SUCCESS,
|
|
||||||
DELETE_RETRY_TASK_ERROR,
|
|
||||||
DELETE_SCHEDULED_TASK_BEGIN,
|
|
||||||
DELETE_SCHEDULED_TASK_SUCCESS,
|
|
||||||
DELETE_SCHEDULED_TASK_ERROR,
|
|
||||||
DELETE_ARCHIVED_TASK_BEGIN,
|
|
||||||
DELETE_ARCHIVED_TASK_SUCCESS,
|
|
||||||
DELETE_ARCHIVED_TASK_ERROR,
|
|
||||||
BATCH_DELETE_ARCHIVED_TASKS_BEGIN,
|
|
||||||
BATCH_DELETE_ARCHIVED_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_ARCHIVED_TASKS_ERROR,
|
|
||||||
RUN_ARCHIVED_TASK_BEGIN,
|
|
||||||
RUN_ARCHIVED_TASK_SUCCESS,
|
|
||||||
RUN_ARCHIVED_TASK_ERROR,
|
|
||||||
BATCH_RUN_ARCHIVED_TASKS_BEGIN,
|
|
||||||
BATCH_RUN_ARCHIVED_TASKS_ERROR,
|
|
||||||
BATCH_RUN_ARCHIVED_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_ARCHIVED_TASKS_BEGIN,
|
|
||||||
DELETE_ALL_ARCHIVED_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_ARCHIVED_TASKS_ERROR,
|
|
||||||
RUN_ALL_ARCHIVED_TASKS_BEGIN,
|
|
||||||
RUN_ALL_ARCHIVED_TASKS_ERROR,
|
|
||||||
RUN_ALL_ARCHIVED_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_RETRY_TASKS_ERROR,
|
|
||||||
BATCH_RUN_RETRY_TASKS_ERROR,
|
|
||||||
BATCH_DELETE_RETRY_TASKS_SUCCESS,
|
|
||||||
BATCH_RUN_RETRY_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_RETRY_TASKS_BEGIN,
|
|
||||||
BATCH_RUN_RETRY_TASKS_BEGIN,
|
|
||||||
DELETE_ALL_RETRY_TASKS_ERROR,
|
|
||||||
RUN_ALL_RETRY_TASKS_ERROR,
|
|
||||||
DELETE_ALL_RETRY_TASKS_SUCCESS,
|
|
||||||
RUN_ALL_RETRY_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_RETRY_TASKS_BEGIN,
|
|
||||||
RUN_ALL_RETRY_TASKS_BEGIN,
|
|
||||||
BATCH_DELETE_SCHEDULED_TASKS_ERROR,
|
|
||||||
BATCH_RUN_SCHEDULED_TASKS_ERROR,
|
|
||||||
BATCH_DELETE_SCHEDULED_TASKS_SUCCESS,
|
|
||||||
BATCH_RUN_SCHEDULED_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_SCHEDULED_TASKS_BEGIN,
|
|
||||||
BATCH_RUN_SCHEDULED_TASKS_BEGIN,
|
|
||||||
DELETE_ALL_SCHEDULED_TASKS_ERROR,
|
|
||||||
RUN_ALL_SCHEDULED_TASKS_ERROR,
|
|
||||||
DELETE_ALL_SCHEDULED_TASKS_SUCCESS,
|
|
||||||
RUN_ALL_SCHEDULED_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_SCHEDULED_TASKS_BEGIN,
|
|
||||||
RUN_ALL_SCHEDULED_TASKS_BEGIN,
|
|
||||||
RUN_RETRY_TASK_BEGIN,
|
|
||||||
RUN_RETRY_TASK_SUCCESS,
|
|
||||||
RUN_RETRY_TASK_ERROR,
|
|
||||||
RUN_SCHEDULED_TASK_BEGIN,
|
|
||||||
RUN_SCHEDULED_TASK_SUCCESS,
|
|
||||||
RUN_SCHEDULED_TASK_ERROR,
|
|
||||||
ARCHIVE_SCHEDULED_TASK_BEGIN,
|
|
||||||
ARCHIVE_SCHEDULED_TASK_SUCCESS,
|
|
||||||
ARCHIVE_SCHEDULED_TASK_ERROR,
|
|
||||||
ARCHIVE_ALL_SCHEDULED_TASKS_BEGIN,
|
ARCHIVE_ALL_SCHEDULED_TASKS_BEGIN,
|
||||||
ARCHIVE_ALL_SCHEDULED_TASKS_SUCCESS,
|
|
||||||
ARCHIVE_ALL_SCHEDULED_TASKS_ERROR,
|
ARCHIVE_ALL_SCHEDULED_TASKS_ERROR,
|
||||||
|
ARCHIVE_ALL_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
ARCHIVE_PENDING_TASK_BEGIN,
|
||||||
|
ARCHIVE_PENDING_TASK_ERROR,
|
||||||
|
ARCHIVE_PENDING_TASK_SUCCESS,
|
||||||
|
ARCHIVE_RETRY_TASK_BEGIN,
|
||||||
|
ARCHIVE_RETRY_TASK_ERROR,
|
||||||
|
ARCHIVE_RETRY_TASK_SUCCESS,
|
||||||
|
ARCHIVE_SCHEDULED_TASK_BEGIN,
|
||||||
|
ARCHIVE_SCHEDULED_TASK_ERROR,
|
||||||
|
ARCHIVE_SCHEDULED_TASK_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_AGGREGATING_TASKS_BEGIN,
|
||||||
|
BATCH_ARCHIVE_AGGREGATING_TASKS_ERROR,
|
||||||
|
BATCH_ARCHIVE_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_PENDING_TASKS_BEGIN,
|
||||||
|
BATCH_ARCHIVE_PENDING_TASKS_ERROR,
|
||||||
|
BATCH_ARCHIVE_PENDING_TASKS_SUCCESS,
|
||||||
|
BATCH_ARCHIVE_RETRY_TASKS_BEGIN,
|
||||||
|
BATCH_ARCHIVE_RETRY_TASKS_ERROR,
|
||||||
|
BATCH_ARCHIVE_RETRY_TASKS_SUCCESS,
|
||||||
BATCH_ARCHIVE_SCHEDULED_TASKS_BEGIN,
|
BATCH_ARCHIVE_SCHEDULED_TASKS_BEGIN,
|
||||||
BATCH_ARCHIVE_SCHEDULED_TASKS_ERROR,
|
BATCH_ARCHIVE_SCHEDULED_TASKS_ERROR,
|
||||||
BATCH_ARCHIVE_SCHEDULED_TASKS_SUCCESS,
|
BATCH_ARCHIVE_SCHEDULED_TASKS_SUCCESS,
|
||||||
ARCHIVE_RETRY_TASK_BEGIN,
|
|
||||||
ARCHIVE_RETRY_TASK_SUCCESS,
|
|
||||||
ARCHIVE_RETRY_TASK_ERROR,
|
|
||||||
ARCHIVE_ALL_RETRY_TASKS_BEGIN,
|
|
||||||
ARCHIVE_ALL_RETRY_TASKS_SUCCESS,
|
|
||||||
ARCHIVE_ALL_RETRY_TASKS_ERROR,
|
|
||||||
BATCH_ARCHIVE_RETRY_TASKS_SUCCESS,
|
|
||||||
BATCH_ARCHIVE_RETRY_TASKS_BEGIN,
|
|
||||||
BATCH_ARCHIVE_RETRY_TASKS_ERROR,
|
|
||||||
BATCH_CANCEL_ACTIVE_TASKS_BEGIN,
|
BATCH_CANCEL_ACTIVE_TASKS_BEGIN,
|
||||||
BATCH_CANCEL_ACTIVE_TASKS_SUCCESS,
|
|
||||||
BATCH_CANCEL_ACTIVE_TASKS_ERROR,
|
BATCH_CANCEL_ACTIVE_TASKS_ERROR,
|
||||||
CANCEL_ALL_ACTIVE_TASKS_BEGIN,
|
BATCH_CANCEL_ACTIVE_TASKS_SUCCESS,
|
||||||
CANCEL_ALL_ACTIVE_TASKS_SUCCESS,
|
BATCH_DELETE_AGGREGATING_TASKS_BEGIN,
|
||||||
CANCEL_ALL_ACTIVE_TASKS_ERROR,
|
BATCH_DELETE_AGGREGATING_TASKS_ERROR,
|
||||||
ARCHIVE_PENDING_TASK_BEGIN,
|
BATCH_DELETE_AGGREGATING_TASKS_SUCCESS,
|
||||||
DELETE_PENDING_TASK_BEGIN,
|
BATCH_DELETE_ARCHIVED_TASKS_BEGIN,
|
||||||
ARCHIVE_PENDING_TASK_SUCCESS,
|
BATCH_DELETE_ARCHIVED_TASKS_ERROR,
|
||||||
DELETE_PENDING_TASK_SUCCESS,
|
BATCH_DELETE_ARCHIVED_TASKS_SUCCESS,
|
||||||
ARCHIVE_PENDING_TASK_ERROR,
|
|
||||||
DELETE_PENDING_TASK_ERROR,
|
|
||||||
ARCHIVE_ALL_PENDING_TASKS_BEGIN,
|
|
||||||
DELETE_ALL_PENDING_TASKS_BEGIN,
|
|
||||||
ARCHIVE_ALL_PENDING_TASKS_SUCCESS,
|
|
||||||
DELETE_ALL_PENDING_TASKS_SUCCESS,
|
|
||||||
ARCHIVE_ALL_PENDING_TASKS_ERROR,
|
|
||||||
DELETE_ALL_PENDING_TASKS_ERROR,
|
|
||||||
BATCH_ARCHIVE_PENDING_TASKS_BEGIN,
|
|
||||||
BATCH_DELETE_PENDING_TASKS_BEGIN,
|
|
||||||
BATCH_ARCHIVE_PENDING_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_PENDING_TASKS_SUCCESS,
|
|
||||||
BATCH_ARCHIVE_PENDING_TASKS_ERROR,
|
|
||||||
BATCH_DELETE_PENDING_TASKS_ERROR,
|
|
||||||
GET_TASK_INFO_BEGIN,
|
|
||||||
GET_TASK_INFO_ERROR,
|
|
||||||
GET_TASK_INFO_SUCCESS,
|
|
||||||
DELETE_COMPLETED_TASK_BEGIN,
|
|
||||||
DELETE_COMPLETED_TASK_ERROR,
|
|
||||||
DELETE_COMPLETED_TASK_SUCCESS,
|
|
||||||
DELETE_ALL_COMPLETED_TASKS_BEGIN,
|
|
||||||
DELETE_ALL_COMPLETED_TASKS_ERROR,
|
|
||||||
DELETE_ALL_COMPLETED_TASKS_SUCCESS,
|
|
||||||
BATCH_DELETE_COMPLETED_TASKS_BEGIN,
|
BATCH_DELETE_COMPLETED_TASKS_BEGIN,
|
||||||
BATCH_DELETE_COMPLETED_TASKS_ERROR,
|
BATCH_DELETE_COMPLETED_TASKS_ERROR,
|
||||||
BATCH_DELETE_COMPLETED_TASKS_SUCCESS,
|
BATCH_DELETE_COMPLETED_TASKS_SUCCESS,
|
||||||
LIST_AGGREGATING_TASKS_BEGIN,
|
BATCH_DELETE_PENDING_TASKS_BEGIN,
|
||||||
LIST_AGGREGATING_TASKS_SUCCESS,
|
BATCH_DELETE_PENDING_TASKS_ERROR,
|
||||||
LIST_AGGREGATING_TASKS_ERROR,
|
BATCH_DELETE_PENDING_TASKS_SUCCESS,
|
||||||
DELETE_ALL_AGGREGATING_TASKS_BEGIN,
|
BATCH_DELETE_RETRY_TASKS_BEGIN,
|
||||||
DELETE_ALL_AGGREGATING_TASKS_SUCCESS,
|
BATCH_DELETE_RETRY_TASKS_ERROR,
|
||||||
DELETE_ALL_AGGREGATING_TASKS_ERROR,
|
BATCH_DELETE_RETRY_TASKS_SUCCESS,
|
||||||
ARCHIVE_ALL_AGGREGATING_TASKS_BEGIN,
|
BATCH_DELETE_SCHEDULED_TASKS_BEGIN,
|
||||||
ARCHIVE_ALL_AGGREGATING_TASKS_SUCCESS,
|
BATCH_DELETE_SCHEDULED_TASKS_ERROR,
|
||||||
ARCHIVE_ALL_AGGREGATING_TASKS_ERROR,
|
BATCH_DELETE_SCHEDULED_TASKS_SUCCESS,
|
||||||
RUN_ALL_AGGREGATING_TASKS_BEGIN,
|
|
||||||
RUN_ALL_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
RUN_ALL_AGGREGATING_TASKS_ERROR,
|
|
||||||
BATCH_ARCHIVE_AGGREGATING_TASKS_BEGIN,
|
|
||||||
BATCH_RUN_AGGREGATING_TASKS_BEGIN,
|
BATCH_RUN_AGGREGATING_TASKS_BEGIN,
|
||||||
BATCH_DELETE_AGGREGATING_TASKS_BEGIN,
|
|
||||||
BATCH_RUN_AGGREGATING_TASKS_ERROR,
|
BATCH_RUN_AGGREGATING_TASKS_ERROR,
|
||||||
BATCH_ARCHIVE_AGGREGATING_TASKS_ERROR,
|
|
||||||
BATCH_DELETE_AGGREGATING_TASKS_ERROR,
|
|
||||||
BATCH_DELETE_AGGREGATING_TASKS_SUCCESS,
|
|
||||||
BATCH_RUN_AGGREGATING_TASKS_SUCCESS,
|
BATCH_RUN_AGGREGATING_TASKS_SUCCESS,
|
||||||
BATCH_ARCHIVE_AGGREGATING_TASKS_SUCCESS,
|
BATCH_RUN_ARCHIVED_TASKS_BEGIN,
|
||||||
RUN_AGGREGATING_TASK_BEGIN,
|
BATCH_RUN_ARCHIVED_TASKS_ERROR,
|
||||||
ARCHIVE_AGGREGATING_TASK_BEGIN,
|
BATCH_RUN_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_RETRY_TASKS_BEGIN,
|
||||||
|
BATCH_RUN_RETRY_TASKS_ERROR,
|
||||||
|
BATCH_RUN_RETRY_TASKS_SUCCESS,
|
||||||
|
BATCH_RUN_SCHEDULED_TASKS_BEGIN,
|
||||||
|
BATCH_RUN_SCHEDULED_TASKS_ERROR,
|
||||||
|
BATCH_RUN_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
CANCEL_ACTIVE_TASK_BEGIN,
|
||||||
|
CANCEL_ACTIVE_TASK_ERROR,
|
||||||
|
CANCEL_ACTIVE_TASK_SUCCESS,
|
||||||
|
CANCEL_ALL_ACTIVE_TASKS_BEGIN,
|
||||||
|
CANCEL_ALL_ACTIVE_TASKS_ERROR,
|
||||||
|
CANCEL_ALL_ACTIVE_TASKS_SUCCESS,
|
||||||
DELETE_AGGREGATING_TASK_BEGIN,
|
DELETE_AGGREGATING_TASK_BEGIN,
|
||||||
RUN_AGGREGATING_TASK_ERROR,
|
|
||||||
ARCHIVE_AGGREGATING_TASK_ERROR,
|
|
||||||
DELETE_AGGREGATING_TASK_ERROR,
|
DELETE_AGGREGATING_TASK_ERROR,
|
||||||
RUN_AGGREGATING_TASK_SUCCESS,
|
|
||||||
ARCHIVE_AGGREGATING_TASK_SUCCESS,
|
|
||||||
DELETE_AGGREGATING_TASK_SUCCESS,
|
DELETE_AGGREGATING_TASK_SUCCESS,
|
||||||
|
DELETE_ALL_AGGREGATING_TASKS_BEGIN,
|
||||||
|
DELETE_ALL_AGGREGATING_TASKS_ERROR,
|
||||||
|
DELETE_ALL_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_ARCHIVED_TASKS_BEGIN,
|
||||||
|
DELETE_ALL_ARCHIVED_TASKS_ERROR,
|
||||||
|
DELETE_ALL_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_COMPLETED_TASKS_BEGIN,
|
||||||
|
DELETE_ALL_COMPLETED_TASKS_ERROR,
|
||||||
|
DELETE_ALL_COMPLETED_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_PENDING_TASKS_BEGIN,
|
||||||
|
DELETE_ALL_PENDING_TASKS_ERROR,
|
||||||
|
DELETE_ALL_PENDING_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_RETRY_TASKS_BEGIN,
|
||||||
|
DELETE_ALL_RETRY_TASKS_ERROR,
|
||||||
|
DELETE_ALL_RETRY_TASKS_SUCCESS,
|
||||||
|
DELETE_ALL_SCHEDULED_TASKS_BEGIN,
|
||||||
|
DELETE_ALL_SCHEDULED_TASKS_ERROR,
|
||||||
|
DELETE_ALL_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
DELETE_ARCHIVED_TASK_BEGIN,
|
||||||
|
DELETE_ARCHIVED_TASK_ERROR,
|
||||||
|
DELETE_ARCHIVED_TASK_SUCCESS,
|
||||||
|
DELETE_COMPLETED_TASK_BEGIN,
|
||||||
|
DELETE_COMPLETED_TASK_ERROR,
|
||||||
|
DELETE_COMPLETED_TASK_SUCCESS,
|
||||||
|
DELETE_PENDING_TASK_BEGIN,
|
||||||
|
DELETE_PENDING_TASK_ERROR,
|
||||||
|
DELETE_PENDING_TASK_SUCCESS,
|
||||||
|
DELETE_RETRY_TASK_BEGIN,
|
||||||
|
DELETE_RETRY_TASK_ERROR,
|
||||||
|
DELETE_RETRY_TASK_SUCCESS,
|
||||||
|
DELETE_SCHEDULED_TASK_BEGIN,
|
||||||
|
DELETE_SCHEDULED_TASK_ERROR,
|
||||||
|
DELETE_SCHEDULED_TASK_SUCCESS,
|
||||||
|
GET_TASK_INFO_BEGIN,
|
||||||
|
GET_TASK_INFO_ERROR,
|
||||||
|
GET_TASK_INFO_SUCCESS,
|
||||||
|
LIST_ACTIVE_TASKS_BEGIN,
|
||||||
|
LIST_ACTIVE_TASKS_ERROR,
|
||||||
|
LIST_ACTIVE_TASKS_SUCCESS,
|
||||||
|
LIST_AGGREGATING_TASKS_BEGIN,
|
||||||
|
LIST_AGGREGATING_TASKS_ERROR,
|
||||||
|
LIST_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
LIST_ARCHIVED_TASKS_BEGIN,
|
||||||
|
LIST_ARCHIVED_TASKS_ERROR,
|
||||||
|
LIST_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
LIST_COMPLETED_TASKS_BEGIN,
|
||||||
|
LIST_COMPLETED_TASKS_ERROR,
|
||||||
|
LIST_COMPLETED_TASKS_SUCCESS,
|
||||||
|
LIST_PENDING_TASKS_BEGIN,
|
||||||
|
LIST_PENDING_TASKS_ERROR,
|
||||||
|
LIST_PENDING_TASKS_SUCCESS,
|
||||||
|
LIST_RETRY_TASKS_BEGIN,
|
||||||
|
LIST_RETRY_TASKS_ERROR,
|
||||||
|
LIST_RETRY_TASKS_SUCCESS,
|
||||||
|
LIST_SCHEDULED_TASKS_BEGIN,
|
||||||
|
LIST_SCHEDULED_TASKS_ERROR,
|
||||||
|
LIST_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
RUN_AGGREGATING_TASK_BEGIN,
|
||||||
|
RUN_AGGREGATING_TASK_ERROR,
|
||||||
|
RUN_AGGREGATING_TASK_SUCCESS,
|
||||||
|
RUN_ALL_AGGREGATING_TASKS_BEGIN,
|
||||||
|
RUN_ALL_AGGREGATING_TASKS_ERROR,
|
||||||
|
RUN_ALL_AGGREGATING_TASKS_SUCCESS,
|
||||||
|
RUN_ALL_ARCHIVED_TASKS_BEGIN,
|
||||||
|
RUN_ALL_ARCHIVED_TASKS_ERROR,
|
||||||
|
RUN_ALL_ARCHIVED_TASKS_SUCCESS,
|
||||||
|
RUN_ALL_RETRY_TASKS_BEGIN,
|
||||||
|
RUN_ALL_RETRY_TASKS_ERROR,
|
||||||
|
RUN_ALL_RETRY_TASKS_SUCCESS,
|
||||||
|
RUN_ALL_SCHEDULED_TASKS_BEGIN,
|
||||||
|
RUN_ALL_SCHEDULED_TASKS_ERROR,
|
||||||
|
RUN_ALL_SCHEDULED_TASKS_SUCCESS,
|
||||||
|
RUN_ARCHIVED_TASK_BEGIN,
|
||||||
|
RUN_ARCHIVED_TASK_ERROR,
|
||||||
|
RUN_ARCHIVED_TASK_SUCCESS,
|
||||||
|
RUN_RETRY_TASK_BEGIN,
|
||||||
|
RUN_RETRY_TASK_ERROR,
|
||||||
|
RUN_RETRY_TASK_SUCCESS,
|
||||||
|
RUN_SCHEDULED_TASK_BEGIN,
|
||||||
|
RUN_SCHEDULED_TASK_ERROR,
|
||||||
|
RUN_SCHEDULED_TASK_SUCCESS,
|
||||||
|
TasksActionTypes,
|
||||||
} from "../actions/tasksActions";
|
} from "../actions/tasksActions";
|
||||||
import { TaskInfo } from "../api";
|
import { TaskInfo } from "../api";
|
||||||
|
import { filterReducer } from "./filterReducer";
|
||||||
|
|
||||||
export interface TaskInfoExtended extends TaskInfo {
|
export interface TaskInfoExtended extends TaskInfo {
|
||||||
// Indicates that a request has been sent for this
|
// Indicates that a request has been sent for this
|
||||||
@ -174,7 +175,7 @@ export interface TaskInfoExtended extends TaskInfo {
|
|||||||
canceling?: boolean;
|
canceling?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TasksState {
|
export interface TasksState {
|
||||||
activeTasks: {
|
activeTasks: {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
batchActionPending: boolean;
|
batchActionPending: boolean;
|
||||||
@ -230,6 +231,12 @@ interface TasksState {
|
|||||||
error: string;
|
error: string;
|
||||||
data?: TaskInfo;
|
data?: TaskInfo;
|
||||||
};
|
};
|
||||||
|
filterOp?: {
|
||||||
|
done: boolean;
|
||||||
|
processedTasks: number;
|
||||||
|
result: TaskInfo[];
|
||||||
|
error?: string;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: TasksState = {
|
const initialState: TasksState = {
|
||||||
@ -293,6 +300,7 @@ function tasksReducer(
|
|||||||
state = initialState,
|
state = initialState,
|
||||||
action: TasksActionTypes
|
action: TasksActionTypes
|
||||||
): TasksState {
|
): TasksState {
|
||||||
|
state = filterReducer(state, action);
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case GET_TASK_INFO_BEGIN:
|
case GET_TASK_INFO_BEGIN:
|
||||||
return {
|
return {
|
||||||
|
@ -32,6 +32,12 @@ export type AppState = ReturnType<typeof rootReducer>;
|
|||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: rootReducer,
|
reducer: rootReducer,
|
||||||
preloadedState,
|
preloadedState,
|
||||||
|
middleware: (getDefaultMiddleware) =>
|
||||||
|
// Disable debug middleware for tasks.filterOp as it may contain large state
|
||||||
|
getDefaultMiddleware({
|
||||||
|
immutableCheck: { ignoredPaths: ["tasks.filterOp"] },
|
||||||
|
serializableCheck: { ignoredPaths: ["tasks.filterOp"] },
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user