mirror of
https://github.com/sartography/spiffworkflow-frontend.git
synced 2025-02-23 19:58:25 +00:00
refactored pagination table to allow prefixing page options w/ burnettk
This commit is contained in:
parent
902c3b7e32
commit
75a198995c
@ -1,4 +1,4 @@
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useNavigate, useSearchParams } from 'react-router-dom';
|
||||
|
||||
// @ts-ignore
|
||||
import { Pagination } from '@carbon/react';
|
||||
@ -14,7 +14,7 @@ type OwnProps = {
|
||||
pagination: PaginationObject | null;
|
||||
tableToDisplay: any;
|
||||
queryParamString?: string;
|
||||
path: string;
|
||||
paginationQueryParamPrefix?: string;
|
||||
};
|
||||
|
||||
export default function PaginationForTable({
|
||||
@ -23,16 +23,21 @@ export default function PaginationForTable({
|
||||
perPageOptions,
|
||||
pagination,
|
||||
tableToDisplay,
|
||||
queryParamString = '',
|
||||
path,
|
||||
paginationQueryParamPrefix,
|
||||
}: OwnProps) {
|
||||
const PER_PAGE_OPTIONS = [2, 10, 50, 100];
|
||||
const navigate = useNavigate();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const paginationQueryParamPrefixToUse = paginationQueryParamPrefix
|
||||
? `${paginationQueryParamPrefix}_`
|
||||
: '';
|
||||
|
||||
const updateRows = (args: any) => {
|
||||
const newPage = args.page;
|
||||
const { pageSize } = args;
|
||||
navigate(`${path}?page=${newPage}&per_page=${pageSize}${queryParamString}`);
|
||||
|
||||
searchParams.set(`${paginationQueryParamPrefixToUse}page`, newPage);
|
||||
searchParams.set(`${paginationQueryParamPrefixToUse}per_page`, pageSize);
|
||||
setSearchParams(searchParams);
|
||||
};
|
||||
|
||||
if (pagination) {
|
||||
|
541
src/components/ProcessInstanceListTable.tsx
Normal file
541
src/components/ProcessInstanceListTable.tsx
Normal file
@ -0,0 +1,541 @@
|
||||
import { useContext, useEffect, useMemo, useState } from 'react';
|
||||
import {
|
||||
Link,
|
||||
useNavigate,
|
||||
useParams,
|
||||
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 './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 './ProcessModelSearch';
|
||||
import ProcessBreadcrumb from './ProcessBreadcrumb';
|
||||
|
||||
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<PaginationObject | null>(null);
|
||||
|
||||
const oneHourInSeconds = 3600;
|
||||
const oneMonthInSeconds = oneHourInSeconds * 24 * 30;
|
||||
const [startFrom, setStartFrom] = useState<string>('');
|
||||
const [startTo, setStartTo] = useState<string>('');
|
||||
const [endFrom, setEndFrom] = useState<string>('');
|
||||
const [endTo, setEndTo] = useState<string>('');
|
||||
const [showFilterOptions, setShowFilterOptions] = useState<boolean>(false);
|
||||
|
||||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||
|
||||
const [processStatusAllOptions, setProcessStatusAllOptions] = useState<any[]>(
|
||||
[]
|
||||
);
|
||||
const [processStatusSelection, setProcessStatusSelection] = useState<
|
||||
string[]
|
||||
>([]);
|
||||
const [processModelAvailableItems, setProcessModelAvailableItems] = useState<
|
||||
ProcessModel[]
|
||||
>([]);
|
||||
const [processModelSelection, setProcessModelSelection] =
|
||||
useState<ProcessModel | null>(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 (
|
||||
<DatePicker dateFormat={DATE_FORMAT_CARBON} datePickerType="single">
|
||||
<DatePickerInput
|
||||
id={`date-picker-${name}`}
|
||||
placeholder={DATE_FORMAT}
|
||||
labelText={labelString}
|
||||
type="text"
|
||||
size="md"
|
||||
autocomplete="off"
|
||||
allowInput={false}
|
||||
onChange={(dateChangeEvent: any) => {
|
||||
onChangeFunction(dateChangeEvent.srcElement.value);
|
||||
}}
|
||||
value={initialDate}
|
||||
/>
|
||||
</DatePicker>
|
||||
);
|
||||
};
|
||||
|
||||
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 processStatusSearch = () => {
|
||||
return (
|
||||
<MultiSelect
|
||||
label="Choose Status"
|
||||
className="our-class"
|
||||
id="process-instance-status-select"
|
||||
titleText="Status"
|
||||
items={processStatusAllOptions}
|
||||
onChange={(selection: any) => {
|
||||
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 (
|
||||
<>
|
||||
<Grid fullWidth className="with-bottom-margin">
|
||||
<Column md={8}>
|
||||
<ProcessModelSearch
|
||||
onChange={(selection: any) =>
|
||||
setProcessModelSelection(selection.selectedItem)
|
||||
}
|
||||
processModels={processModelAvailableItems}
|
||||
selectedItem={processModelSelection}
|
||||
/>
|
||||
</Column>
|
||||
<Column md={8}>{processStatusSearch()}</Column>
|
||||
</Grid>
|
||||
<Grid fullWidth className="with-bottom-margin">
|
||||
<Column md={4}>
|
||||
{dateComponent(
|
||||
'Start date from',
|
||||
'start-from',
|
||||
startFrom,
|
||||
setStartFrom
|
||||
)}
|
||||
</Column>
|
||||
<Column md={4}>
|
||||
{dateComponent('Start date to', 'start-to', startTo, setStartTo)}
|
||||
</Column>
|
||||
<Column md={4}>
|
||||
{dateComponent('End date from', 'end-from', endFrom, setEndFrom)}
|
||||
</Column>
|
||||
<Column md={4}>
|
||||
{dateComponent('End date to', 'end-to', endTo, setEndTo)}
|
||||
</Column>
|
||||
</Grid>
|
||||
<Grid fullWidth className="with-bottom-margin">
|
||||
<Column md={4}>
|
||||
<ButtonSet>
|
||||
<Button
|
||||
kind=""
|
||||
className="button-white-background"
|
||||
onClick={clearFilters}
|
||||
>
|
||||
Clear
|
||||
</Button>
|
||||
<Button
|
||||
kind="secondary"
|
||||
onClick={applyFilter}
|
||||
data-qa="filter-button"
|
||||
>
|
||||
Filter
|
||||
</Button>
|
||||
</ButtonSet>
|
||||
</Column>
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const buildTable = () => {
|
||||
const headerLabels: Record<string, string> = {
|
||||
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 <th>{getHeaderLabel((column as any).Header)}</th>;
|
||||
return getHeaderLabel((column as any).Header);
|
||||
});
|
||||
|
||||
const formatProcessInstanceId = (row: any, id: any) => {
|
||||
const modifiedProcessModelId: String = modifyProcessModelPath(
|
||||
row.process_model_identifier
|
||||
);
|
||||
return (
|
||||
<Link
|
||||
data-qa="process-instance-show-link"
|
||||
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${row.id}`}
|
||||
>
|
||||
{id}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
const formatProcessModelIdentifier = (_row: any, identifier: any) => {
|
||||
return (
|
||||
<Link
|
||||
to={`/admin/process-models/${modifyProcessModelPath(identifier)}`}
|
||||
>
|
||||
{identifier}
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
const formatSecondsForDisplay = (_row: any, seconds: any) => {
|
||||
return convertSecondsToFormattedDate(seconds) || '-';
|
||||
};
|
||||
const defaultFormatter = (_row: any, value: any) => {
|
||||
return value;
|
||||
};
|
||||
|
||||
const columnFormatters: Record<string, any> = {
|
||||
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 (
|
||||
<td data-qa={`process-instance-status-${value}`}>
|
||||
{formatter(row, value)}
|
||||
</td>
|
||||
);
|
||||
}
|
||||
return <td>{formatter(row, value)}</td>;
|
||||
};
|
||||
|
||||
const rows = processInstances.map((row: any) => {
|
||||
const currentRow = (reportMetadata as any).columns.map((column: any) => {
|
||||
return formattedColumn(row, column);
|
||||
});
|
||||
return <tr key={row.id}>{currentRow}</tr>;
|
||||
});
|
||||
|
||||
return (
|
||||
<Table size="lg">
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
{headers.map((header: any) => (
|
||||
<TableHeader key={header}>{header}</TableHeader>
|
||||
))}
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<tbody>{rows}</tbody>
|
||||
</Table>
|
||||
);
|
||||
};
|
||||
|
||||
const processInstanceBreadcrumbElement = () => {
|
||||
const processModelFullIdentifier =
|
||||
getProcessModelFullIdentifierFromSearchParams(searchParams);
|
||||
if (processModelFullIdentifier === null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<ProcessBreadcrumb
|
||||
hotCrumbs={[
|
||||
['Process Groups', '/admin'],
|
||||
[
|
||||
`Process Model: ${processModelFullIdentifier}`,
|
||||
`process_model:${processModelFullIdentifier}:link`,
|
||||
],
|
||||
['Process Instances'],
|
||||
]}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const processInstanceTitleElement = () => {
|
||||
return <h1>Process Instances</h1>;
|
||||
};
|
||||
|
||||
const toggleShowFilterOptions = () => {
|
||||
setShowFilterOptions(!showFilterOptions);
|
||||
};
|
||||
|
||||
if (pagination) {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(searchParams);
|
||||
return (
|
||||
<>
|
||||
{processInstanceBreadcrumbElement()}
|
||||
{processInstanceTitleElement()}
|
||||
<Grid fullWidth>
|
||||
<Column
|
||||
sm={{ span: 1, offset: 3 }}
|
||||
md={{ span: 1, offset: 7 }}
|
||||
lg={{ span: 1, offset: 15 }}
|
||||
>
|
||||
<Button
|
||||
data-qa="filter-section-expand-toggle"
|
||||
kind="ghost"
|
||||
renderIcon={Filter}
|
||||
iconDescription="Filter Options"
|
||||
hasIconOnly
|
||||
size="lg"
|
||||
onClick={toggleShowFilterOptions}
|
||||
/>
|
||||
</Column>
|
||||
</Grid>
|
||||
{filterOptions()}
|
||||
<br />
|
||||
<PaginationForTable
|
||||
page={page}
|
||||
perPage={perPage}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
queryParamString={getSearchParamsAsQueryString()}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
@ -12,6 +12,7 @@ import HttpService from '../services/HttpService';
|
||||
import { PaginationObject } from '../interfaces';
|
||||
|
||||
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
||||
const paginationQueryParamPrefix = 'tasks_for_my_open_processes';
|
||||
|
||||
export default function MyOpenProcesses() {
|
||||
const [searchParams] = useSearchParams();
|
||||
@ -21,7 +22,9 @@ export default function MyOpenProcesses() {
|
||||
useEffect(() => {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
|
||||
undefined,
|
||||
paginationQueryParamPrefix
|
||||
);
|
||||
const setTasksFromResult = (result: any) => {
|
||||
setTasks(result.results);
|
||||
@ -113,7 +116,9 @@ export default function MyOpenProcesses() {
|
||||
}
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
|
||||
undefined,
|
||||
paginationQueryParamPrefix
|
||||
);
|
||||
return (
|
||||
<>
|
||||
@ -124,7 +129,7 @@ export default function MyOpenProcesses() {
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/tasks/grouped"
|
||||
paginationQueryParamPrefix={paginationQueryParamPrefix}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -21,7 +21,9 @@ export default function TasksWaitingForMe() {
|
||||
useEffect(() => {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
|
||||
undefined,
|
||||
'tasks_waiting_for_me'
|
||||
);
|
||||
const setTasksFromResult = (result: any) => {
|
||||
setTasks(result.results);
|
||||
@ -115,7 +117,9 @@ export default function TasksWaitingForMe() {
|
||||
}
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
|
||||
undefined,
|
||||
'tasks_waiting_for_me'
|
||||
);
|
||||
return (
|
||||
<>
|
||||
@ -126,7 +130,7 @@ export default function TasksWaitingForMe() {
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/tasks/grouped"
|
||||
paginationQueryParamPrefix="tasks_waiting_for_me"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -12,6 +12,7 @@ import HttpService from '../services/HttpService';
|
||||
import { PaginationObject } from '../interfaces';
|
||||
|
||||
const PER_PAGE_FOR_TASKS_ON_HOME_PAGE = 5;
|
||||
const paginationQueryParamPrefix = 'tasks_waiting_for_my_groups';
|
||||
|
||||
export default function TasksForWaitingForMyGroups() {
|
||||
const [searchParams] = useSearchParams();
|
||||
@ -21,7 +22,9 @@ export default function TasksForWaitingForMyGroups() {
|
||||
useEffect(() => {
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
|
||||
undefined,
|
||||
paginationQueryParamPrefix
|
||||
);
|
||||
const setTasksFromResult = (result: any) => {
|
||||
setTasks(result.results);
|
||||
@ -115,7 +118,9 @@ export default function TasksForWaitingForMyGroups() {
|
||||
}
|
||||
const { page, perPage } = getPageInfoFromSearchParams(
|
||||
searchParams,
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE
|
||||
PER_PAGE_FOR_TASKS_ON_HOME_PAGE,
|
||||
undefined,
|
||||
paginationQueryParamPrefix
|
||||
);
|
||||
return (
|
||||
<>
|
||||
@ -126,7 +131,7 @@ export default function TasksForWaitingForMyGroups() {
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/tasks/grouped"
|
||||
paginationQueryParamPrefix={paginationQueryParamPrefix}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -79,11 +79,20 @@ export const objectIsEmpty = (obj: object) => {
|
||||
export const getPageInfoFromSearchParams = (
|
||||
searchParams: any,
|
||||
defaultPerPage: string | number = DEFAULT_PER_PAGE,
|
||||
defaultPage: string | number = DEFAULT_PAGE
|
||||
defaultPage: string | number = DEFAULT_PAGE,
|
||||
paginationQueryParamPrefix: string | null = null
|
||||
) => {
|
||||
const page = parseInt(searchParams.get('page') || defaultPage.toString(), 10);
|
||||
const paginationQueryParamPrefixToUse = paginationQueryParamPrefix
|
||||
? `${paginationQueryParamPrefix}_`
|
||||
: '';
|
||||
const page = parseInt(
|
||||
searchParams.get(`${paginationQueryParamPrefixToUse}page`) ||
|
||||
defaultPage.toString(),
|
||||
10
|
||||
);
|
||||
const perPage = parseInt(
|
||||
searchParams.get('per_page') || defaultPerPage.toString(),
|
||||
searchParams.get(`${paginationQueryParamPrefixToUse}per_page`) ||
|
||||
defaultPerPage.toString(),
|
||||
10
|
||||
);
|
||||
|
||||
|
3
src/routes/CompletedInstances.tsx
Normal file
3
src/routes/CompletedInstances.tsx
Normal file
@ -0,0 +1,3 @@
|
||||
export default function CompletedInstances() {
|
||||
return <></>;
|
||||
}
|
@ -6,6 +6,7 @@ import TaskShow from './TaskShow';
|
||||
import ErrorContext from '../contexts/ErrorContext';
|
||||
import MyTasks from './MyTasks';
|
||||
import GroupedTasks from './GroupedTasks';
|
||||
import CompletedInstances from './CompletedInstances';
|
||||
|
||||
export default function HomePageRoutes() {
|
||||
const location = useLocation();
|
||||
@ -18,6 +19,8 @@ export default function HomePageRoutes() {
|
||||
let newSelectedTabIndex = 0;
|
||||
if (location.pathname.match(/^\/tasks\/grouped\b/)) {
|
||||
newSelectedTabIndex = 1;
|
||||
} else if (location.pathname.match(/^\/tasks\/completed-instances\b/)) {
|
||||
newSelectedTabIndex = 2;
|
||||
}
|
||||
setSelectedTabIndex(newSelectedTabIndex);
|
||||
}, [location, setErrorMessage]);
|
||||
@ -28,6 +31,9 @@ export default function HomePageRoutes() {
|
||||
<TabList aria-label="List of tabs">
|
||||
<Tab onClick={() => navigate('/tasks/my-tasks')}>My Tasks</Tab>
|
||||
<Tab onClick={() => navigate('/tasks/grouped')}>Grouped Tasks</Tab>
|
||||
<Tab onClick={() => navigate('/tasks/completed-instances')}>
|
||||
Completed Instances
|
||||
</Tab>
|
||||
</TabList>
|
||||
</Tabs>
|
||||
<br />
|
||||
@ -36,6 +42,7 @@ export default function HomePageRoutes() {
|
||||
<Route path="my-tasks" element={<MyTasks />} />
|
||||
<Route path=":process_instance_id/:task_id" element={<TaskShow />} />
|
||||
<Route path="grouped" element={<GroupedTasks />} />
|
||||
<Route path="completed-instances" element={<CompletedInstances />} />
|
||||
</Routes>
|
||||
</>
|
||||
);
|
||||
|
@ -133,7 +133,6 @@ export default function MessageInstanceList() {
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
queryParamString={queryParamString}
|
||||
path="/admin/messages"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -152,7 +152,6 @@ export default function MyTasks() {
|
||||
perPageOptions={[2, PER_PAGE_FOR_TASKS_ON_HOME_PAGE, 25]}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/tasks"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -109,7 +109,6 @@ export default function ProcessGroupList() {
|
||||
perPage={perPage}
|
||||
pagination={pagination as any}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/admin/process-groups"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -170,7 +170,6 @@ export default function ProcessGroupShow() {
|
||||
perPage={perPage}
|
||||
pagination={modelPagination}
|
||||
tableToDisplay={buildModelTable()}
|
||||
path={`/admin/process-groups/${processGroup.id}`}
|
||||
/>
|
||||
)}
|
||||
<br />
|
||||
@ -182,7 +181,6 @@ export default function ProcessGroupShow() {
|
||||
perPage={perPage}
|
||||
pagination={groupPagination}
|
||||
tableToDisplay={buildGroupTable()}
|
||||
path={`/admin/process-groups/${processGroup.id}`}
|
||||
/>
|
||||
)}
|
||||
</ul>
|
||||
|
@ -535,7 +535,6 @@ export default function ProcessInstanceList() {
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
queryParamString={getSearchParamsAsQueryString()}
|
||||
path="/admin/process-instances"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
@ -99,7 +99,6 @@ export default function ProcessInstanceLogList() {
|
||||
perPage={perPage}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path={`/admin/process-models/${modifiedProcessModelId}/process-instances/${params.process_instance_id}/logs`}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
|
@ -91,7 +91,6 @@ export default function ProcessInstanceReport() {
|
||||
perPage={perPage}
|
||||
pagination={pagination}
|
||||
tableToDisplay={buildTable()}
|
||||
path={`/admin/process-models/${params.process_group_id}/${params.process_model_id}/process-instances/report`}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
|
@ -83,7 +83,6 @@ export default function SecretList() {
|
||||
perPage={perPage}
|
||||
pagination={pagination as any}
|
||||
tableToDisplay={buildTable()}
|
||||
path="/admin/secrets"
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
Loading…
x
Reference in New Issue
Block a user