diff --git a/spiffworkflow-frontend/src/components/PaginationForTable.tsx b/spiffworkflow-frontend/src/components/PaginationForTable.tsx
index 21813952c..19d098ffd 100644
--- a/spiffworkflow-frontend/src/components/PaginationForTable.tsx
+++ b/spiffworkflow-frontend/src/components/PaginationForTable.tsx
@@ -1,4 +1,4 @@
-import { useNavigate, useSearchParams } from 'react-router-dom';
+import { useSearchParams } from 'react-router-dom';
// @ts-ignore
import { Pagination } from '@carbon/react';
diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx
index f09ae7113..81a416f30 100644
--- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx
+++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx
@@ -41,9 +41,18 @@ import 'react-bootstrap-typeahead/css/Typeahead.css';
import 'react-bootstrap-typeahead/css/Typeahead.bs5.css';
import { PaginationObject, ProcessModel } from '../interfaces';
import ProcessModelSearch from './ProcessModelSearch';
-import ProcessBreadcrumb from './ProcessBreadcrumb';
-export default function ProcessInstanceList() {
+type OwnProps = {
+ filtersEnabled?: boolean;
+ processModelFullIdentifier?: string;
+ paginationQueryParamPrefix?: string;
+};
+
+export default function ProcessInstanceListTable({
+ filtersEnabled = true,
+ processModelFullIdentifier,
+ paginationQueryParamPrefix,
+}: OwnProps) {
const params = useParams();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
@@ -99,7 +108,12 @@ export default function ProcessInstanceList() {
setPagination(result.pagination);
}
function getProcessInstances() {
- const { page, perPage } = getPageInfoFromSearchParams(searchParams);
+ const { page, perPage } = getPageInfoFromSearchParams(
+ searchParams,
+ undefined,
+ undefined,
+ paginationQueryParamPrefix
+ );
let queryParamString = `per_page=${perPage}&page=${page}`;
Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => {
@@ -118,7 +132,12 @@ export default function ProcessInstanceList() {
Object.keys(parametersToGetFromSearchParams).forEach(
(paramName: string) => {
- if (searchParams.get(paramName)) {
+ if (
+ paramName === 'process_model_identifier' &&
+ processModelFullIdentifier
+ ) {
+ queryParamString += `&process_model_identifier=${processModelFullIdentifier}`;
+ } else if (searchParams.get(paramName)) {
// @ts-expect-error TS(7053) FIXME:
const functionToCall = parametersToGetFromSearchParams[paramName];
queryParamString += `&${paramName}=${searchParams.get(paramName)}`;
@@ -129,18 +148,19 @@ export default function ProcessInstanceList() {
}
}
);
+
HttpService.makeCallToBackend({
path: `/process-instances?${queryParamString}`,
successCallback: setProcessInstancesFromResult,
});
}
function processResultForProcessModels(result: any) {
- const processModelFullIdentifier =
+ const processModelFullIdentifierFromSearchParams =
getProcessModelFullIdentifierFromSearchParams(searchParams);
const selectionArray = result.results.map((item: any) => {
const label = `${item.id}`;
Object.assign(item, { label });
- if (label === processModelFullIdentifier) {
+ if (label === processModelFullIdentifierFromSearchParams) {
setProcessModelSelection(item);
}
return item;
@@ -163,11 +183,15 @@ export default function ProcessInstanceList() {
getProcessInstances();
}
- // populate process model selection
- HttpService.makeCallToBackend({
- path: `/process-models?per_page=1000`,
- successCallback: processResultForProcessModels,
- });
+ if (filtersEnabled) {
+ // populate process model selection
+ HttpService.makeCallToBackend({
+ path: `/process-models?per_page=1000`,
+ successCallback: processResultForProcessModels,
+ });
+ } else {
+ getProcessInstances();
+ }
}, [
searchParams,
params,
@@ -175,6 +199,7 @@ export default function ProcessInstanceList() {
oneHourInSeconds,
parametersToAlwaysFilterBy,
parametersToGetFromSearchParams,
+ filtersEnabled,
]);
// does the comparison, but also returns false if either argument
@@ -196,7 +221,12 @@ export default function ProcessInstanceList() {
const applyFilter = (event: any) => {
event.preventDefault();
- const { page, perPage } = getPageInfoFromSearchParams(searchParams);
+ const { page, perPage } = getPageInfoFromSearchParams(
+ searchParams,
+ undefined,
+ undefined,
+ paginationQueryParamPrefix
+ );
let queryParamString = `per_page=${perPage}&page=${page}`;
const startFromSeconds = convertDateStringToSeconds(startFrom);
@@ -472,41 +502,16 @@ export default function ProcessInstanceList() {
);
};
- const processInstanceBreadcrumbElement = () => {
- const processModelFullIdentifier =
- getProcessModelFullIdentifierFromSearchParams(searchParams);
- if (processModelFullIdentifier === null) {
- return null;
- }
-
- return (
-
- );
- };
-
- const processInstanceTitleElement = () => {
- return
Process Instances
;
- };
-
const toggleShowFilterOptions = () => {
setShowFilterOptions(!showFilterOptions);
};
- if (pagination) {
- const { page, perPage } = getPageInfoFromSearchParams(searchParams);
+ const filterComponent = () => {
+ if (!filtersEnabled) {
+ return null;
+ }
return (
<>
- {processInstanceBreadcrumbElement()}
- {processInstanceTitleElement()}
{filterOptions()}
+ >
+ );
+ };
+
+ if (pagination) {
+ const { page, perPage } = getPageInfoFromSearchParams(
+ searchParams,
+ undefined,
+ undefined,
+ paginationQueryParamPrefix
+ );
+ return (
+ <>
+ {filterComponent()}
>
);
diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx
index a09efebf0..b6c08b213 100644
--- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx
@@ -1,482 +1,15 @@
-import { useContext, useEffect, useMemo, useState } from 'react';
-import {
- Link,
- useNavigate,
- useParams,
- useSearchParams,
-} from 'react-router-dom';
+import { useSearchParams } from 'react-router-dom';
-// @ts-ignore
-import { Filter } from '@carbon/icons-react';
-import {
- Button,
- ButtonSet,
- DatePicker,
- DatePickerInput,
- Table,
- Grid,
- Column,
- MultiSelect,
- TableHeader,
- TableHead,
- TableRow,
- // @ts-ignore
-} from '@carbon/react';
-import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
-import {
- convertDateStringToSeconds,
- convertSecondsToFormattedDate,
- getPageInfoFromSearchParams,
- getProcessModelFullIdentifierFromSearchParams,
- modifyProcessModelPath,
-} from '../helpers';
-
-import PaginationForTable from '../components/PaginationForTable';
import 'react-datepicker/dist/react-datepicker.css';
-import ErrorContext from '../contexts/ErrorContext';
-import HttpService from '../services/HttpService';
-
import 'react-bootstrap-typeahead/css/Typeahead.css';
import 'react-bootstrap-typeahead/css/Typeahead.bs5.css';
-import { PaginationObject, ProcessModel } from '../interfaces';
-import ProcessModelSearch from '../components/ProcessModelSearch';
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
+import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
+import { getProcessModelFullIdentifierFromSearchParams } from '../helpers';
export default function ProcessInstanceList() {
- const params = useParams();
const [searchParams] = useSearchParams();
- const navigate = useNavigate();
-
- const [processInstances, setProcessInstances] = useState([]);
- const [reportMetadata, setReportMetadata] = useState({});
- const [pagination, setPagination] = useState(null);
-
- const oneHourInSeconds = 3600;
- const oneMonthInSeconds = oneHourInSeconds * 24 * 30;
- const [startFrom, setStartFrom] = useState('');
- const [startTo, setStartTo] = useState('');
- const [endFrom, setEndFrom] = useState('');
- const [endTo, setEndTo] = useState('');
- const [showFilterOptions, setShowFilterOptions] = useState(false);
-
- const setErrorMessage = (useContext as any)(ErrorContext)[1];
-
- const [processStatusAllOptions, setProcessStatusAllOptions] = useState(
- []
- );
- const [processStatusSelection, setProcessStatusSelection] = useState<
- string[]
- >([]);
- const [processModelAvailableItems, setProcessModelAvailableItems] = useState<
- ProcessModel[]
- >([]);
- const [processModelSelection, setProcessModelSelection] =
- useState(null);
-
- const parametersToAlwaysFilterBy = useMemo(() => {
- return {
- start_from: setStartFrom,
- start_to: setStartTo,
- end_from: setEndFrom,
- end_to: setEndTo,
- };
- }, [setStartFrom, setStartTo, setEndFrom, setEndTo]);
-
- const parametersToGetFromSearchParams = useMemo(() => {
- return {
- process_model_identifier: null,
- process_status: null,
- };
- }, []);
-
- // eslint-disable-next-line sonarjs/cognitive-complexity
- useEffect(() => {
- function setProcessInstancesFromResult(result: any) {
- const processInstancesFromApi = result.results;
- setProcessInstances(processInstancesFromApi);
- setReportMetadata(result.report_metadata);
- setPagination(result.pagination);
- }
- function getProcessInstances() {
- const { page, perPage } = getPageInfoFromSearchParams(searchParams);
- let queryParamString = `per_page=${perPage}&page=${page}`;
-
- Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => {
- // @ts-expect-error TS(7053) FIXME:
- const functionToCall = parametersToAlwaysFilterBy[paramName];
- const searchParamValue = searchParams.get(paramName);
- if (searchParamValue) {
- queryParamString += `&${paramName}=${searchParamValue}`;
- const dateString = convertSecondsToFormattedDate(
- searchParamValue as any
- );
- functionToCall(dateString);
- setShowFilterOptions(true);
- }
- });
-
- Object.keys(parametersToGetFromSearchParams).forEach(
- (paramName: string) => {
- if (searchParams.get(paramName)) {
- // @ts-expect-error TS(7053) FIXME:
- const functionToCall = parametersToGetFromSearchParams[paramName];
- queryParamString += `&${paramName}=${searchParams.get(paramName)}`;
- if (functionToCall !== null) {
- functionToCall(searchParams.get(paramName) || '');
- }
- setShowFilterOptions(true);
- }
- }
- );
- HttpService.makeCallToBackend({
- path: `/process-instances?${queryParamString}`,
- successCallback: setProcessInstancesFromResult,
- });
- }
- function processResultForProcessModels(result: any) {
- const processModelFullIdentifier =
- getProcessModelFullIdentifierFromSearchParams(searchParams);
- const selectionArray = result.results.map((item: any) => {
- const label = `${item.id}`;
- Object.assign(item, { label });
- if (label === processModelFullIdentifier) {
- setProcessModelSelection(item);
- }
- return item;
- });
- setProcessModelAvailableItems(selectionArray);
-
- const processStatusSelectedArray: string[] = [];
- const processStatusAllOptionsArray = PROCESS_STATUSES.map(
- (processStatusOption: any) => {
- const regex = new RegExp(`\\b${processStatusOption}\\b`);
- if ((searchParams.get('process_status') || '').match(regex)) {
- processStatusSelectedArray.push(processStatusOption);
- }
- return processStatusOption;
- }
- );
- setProcessStatusSelection(processStatusSelectedArray);
- setProcessStatusAllOptions(processStatusAllOptionsArray);
-
- getProcessInstances();
- }
-
- // populate process model selection
- HttpService.makeCallToBackend({
- path: `/process-models?per_page=1000`,
- successCallback: processResultForProcessModels,
- });
- }, [
- searchParams,
- params,
- oneMonthInSeconds,
- oneHourInSeconds,
- parametersToAlwaysFilterBy,
- parametersToGetFromSearchParams,
- ]);
-
- // does the comparison, but also returns false if either argument
- // is not truthy and therefore not comparable.
- const isTrueComparison = (param1: any, operation: any, param2: any) => {
- if (param1 && param2) {
- switch (operation) {
- case '<':
- return param1 < param2;
- case '>':
- return param1 > param2;
- default:
- return false;
- }
- } else {
- return false;
- }
- };
-
- const applyFilter = (event: any) => {
- event.preventDefault();
- const { page, perPage } = getPageInfoFromSearchParams(searchParams);
- let queryParamString = `per_page=${perPage}&page=${page}`;
-
- const startFromSeconds = convertDateStringToSeconds(startFrom);
- const endFromSeconds = convertDateStringToSeconds(endFrom);
- const startToSeconds = convertDateStringToSeconds(startTo);
- const endToSeconds = convertDateStringToSeconds(endTo);
- if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
- setErrorMessage({
- message: '"Start date from" cannot be after "start date to"',
- });
- return;
- }
- if (isTrueComparison(endFromSeconds, '>', endToSeconds)) {
- setErrorMessage({
- message: '"End date from" cannot be after "end date to"',
- });
- return;
- }
- if (isTrueComparison(startFromSeconds, '>', endFromSeconds)) {
- setErrorMessage({
- message: '"Start date from" cannot be after "end date from"',
- });
- return;
- }
- if (isTrueComparison(startToSeconds, '>', endToSeconds)) {
- setErrorMessage({
- message: '"Start date to" cannot be after "end date to"',
- });
- return;
- }
-
- if (startFromSeconds) {
- queryParamString += `&start_from=${startFromSeconds}`;
- }
- if (startToSeconds) {
- queryParamString += `&start_to=${startToSeconds}`;
- }
- if (endFromSeconds) {
- queryParamString += `&end_from=${endFromSeconds}`;
- }
- if (endToSeconds) {
- queryParamString += `&end_to=${endToSeconds}`;
- }
- if (processStatusSelection.length > 0) {
- queryParamString += `&process_status=${processStatusSelection}`;
- }
-
- if (processModelSelection) {
- queryParamString += `&process_model_identifier=${processModelSelection.id}`;
- }
-
- setErrorMessage(null);
- navigate(`/admin/process-instances?${queryParamString}`);
- };
-
- const dateComponent = (
- labelString: any,
- name: any,
- initialDate: any,
- onChangeFunction: any
- ) => {
- return (
-
- {
- onChangeFunction(dateChangeEvent.srcElement.value);
- }}
- value={initialDate}
- />
-
- );
- };
- const processStatusSearch = () => {
- return (
- {
- setProcessStatusSelection(selection.selectedItems);
- }}
- itemToString={(item: any) => {
- return item || '';
- }}
- selectionFeedback="top-after-reopen"
- selectedItems={processStatusSelection}
- />
- );
- };
- const clearFilters = () => {
- setProcessModelSelection(null);
- setProcessStatusSelection([]);
- setStartFrom('');
- setStartTo('');
- setEndFrom('');
- setEndTo('');
- };
- const filterOptions = () => {
- if (!showFilterOptions) {
- return null;
- }
- return (
- <>
-
-
-
- setProcessModelSelection(selection.selectedItem)
- }
- processModels={processModelAvailableItems}
- selectedItem={processModelSelection}
- />
-
- {processStatusSearch()}
-
-
-
- {dateComponent(
- 'Start date from',
- 'start-from',
- startFrom,
- setStartFrom
- )}
-
-
- {dateComponent('Start date to', 'start-to', startTo, setStartTo)}
-
-
- {dateComponent('End date from', 'end-from', endFrom, setEndFrom)}
-
-
- {dateComponent('End date to', 'end-to', endTo, setEndTo)}
-
-
-
-
-
-
-
-
-
-
- >
- );
- };
- const toggleShowFilterOptions = () => {
- setShowFilterOptions(!showFilterOptions);
- };
- const filterComponent = () => {
- return (
- <>
-
-
-
-
-
- {filterOptions()}
- >
- );
- };
-
- const buildTable = () => {
- const headerLabels: Record = {
- id: 'Process Instance Id',
- process_model_identifier: 'Process Model',
- start_in_seconds: 'Start Time',
- end_in_seconds: 'End Time',
- status: 'Status',
- spiff_step: 'SpiffWorkflow Step',
- };
- const getHeaderLabel = (header: string) => {
- return headerLabels[header] ?? header;
- };
- const headers = (reportMetadata as any).columns.map((column: any) => {
- // return {getHeaderLabel((column as any).Header)} | ;
- return getHeaderLabel((column as any).Header);
- });
-
- const formatProcessInstanceId = (row: any, id: any) => {
- const modifiedProcessModelId: String = modifyProcessModelPath(
- row.process_model_identifier
- );
- return (
-
- {id}
-
- );
- };
- const formatProcessModelIdentifier = (_row: any, identifier: any) => {
- return (
-
- {identifier}
-
- );
- };
- const formatSecondsForDisplay = (_row: any, seconds: any) => {
- return convertSecondsToFormattedDate(seconds) || '-';
- };
- const defaultFormatter = (_row: any, value: any) => {
- return value;
- };
-
- const columnFormatters: Record = {
- id: formatProcessInstanceId,
- process_model_identifier: formatProcessModelIdentifier,
- start_in_seconds: formatSecondsForDisplay,
- end_in_seconds: formatSecondsForDisplay,
- };
- const formattedColumn = (row: any, column: any) => {
- const formatter = columnFormatters[column.accessor] ?? defaultFormatter;
- const value = row[column.accessor];
- if (column.accessor === 'status') {
- return (
-
- {formatter(row, value)}
- |
- );
- }
- return {formatter(row, value)} | ;
- };
-
- const rows = processInstances.map((row: any) => {
- const currentRow = (reportMetadata as any).columns.map((column: any) => {
- return formattedColumn(row, column);
- });
- return {currentRow}
;
- });
-
- return (
-
-
-
- {headers.map((header: any) => (
- {header}
- ))}
-
-
- {rows}
-
- );
- };
-
const processInstanceBreadcrumbElement = () => {
const processModelFullIdentifier =
getProcessModelFullIdentifierFromSearchParams(searchParams);
@@ -498,47 +31,14 @@ export default function ProcessInstanceList() {
);
};
- const getSearchParamsAsQueryString = () => {
- let queryParamString = '';
- Object.keys(parametersToAlwaysFilterBy).forEach((paramName) => {
- const searchParamValue = searchParams.get(paramName);
- if (searchParamValue) {
- queryParamString += `&${paramName}=${searchParamValue}`;
- }
- });
-
- Object.keys(parametersToGetFromSearchParams).forEach(
- (paramName: string) => {
- if (searchParams.get(paramName)) {
- queryParamString += `&${paramName}=${searchParams.get(paramName)}`;
- }
- }
- );
- return queryParamString;
- };
-
const processInstanceTitleElement = () => {
return Process Instances
;
};
-
- if (pagination) {
- const { page, perPage } = getPageInfoFromSearchParams(searchParams);
- return (
- <>
- {processInstanceBreadcrumbElement()}
- {processInstanceTitleElement()}
- {filterComponent()}
-
-
- >
- );
- }
-
- return null;
+ return (
+ <>
+ {processInstanceBreadcrumbElement()}
+ {processInstanceTitleElement()}
+
+ >
+ );
}
diff --git a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx
index d49cec494..21ba8f48f 100644
--- a/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessModelShow.tsx
@@ -33,6 +33,7 @@ import ErrorContext from '../contexts/ErrorContext';
import { modifyProcessModelPath, unModifyProcessModelPath } from '../helpers';
import { ProcessFile, ProcessModel, RecentProcessModel } from '../interfaces';
import ButtonWithConfirmation from '../components/ButtonWithConfirmation';
+import ProcessInstanceListTable from '../components/ProcessInstanceListTable';
const storeRecentProcessModelInLocalStorage = (
processModelForStorage: ProcessModel
@@ -538,6 +539,11 @@ export default function ProcessModelShow() {
{processInstancesUl()}
{processModelButtons()}
+
+
>
);
}