Merge pull request #48 from sartography/feature/add_times_to_instance_filter
Feature/add times to instance filter
This commit is contained in:
commit
4a9e4c820c
|
@ -24,7 +24,6 @@
|
||||||
"@rjsf/mui": "^5.0.0-beta.13",
|
"@rjsf/mui": "^5.0.0-beta.13",
|
||||||
"@rjsf/utils": "^5.0.0-beta.13",
|
"@rjsf/utils": "^5.0.0-beta.13",
|
||||||
"@rjsf/validator-ajv6": "^5.0.0-beta.13",
|
"@rjsf/validator-ajv6": "^5.0.0-beta.13",
|
||||||
"@rjsf/validator-ajv8": "^5.0.0-beta.13",
|
|
||||||
"@tanstack/react-table": "^8.2.2",
|
"@tanstack/react-table": "^8.2.2",
|
||||||
"@testing-library/jest-dom": "^5.16.4",
|
"@testing-library/jest-dom": "^5.16.4",
|
||||||
"@testing-library/react": "^13.3.0",
|
"@testing-library/react": "^13.3.0",
|
||||||
|
@ -4950,23 +4949,6 @@
|
||||||
"@rjsf/utils": "^5.0.0-beta.1"
|
"@rjsf/utils": "^5.0.0-beta.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@rjsf/validator-ajv8": {
|
|
||||||
"version": "5.0.0-beta.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.0.0-beta.13.tgz",
|
|
||||||
"integrity": "sha512-/hrYbiwgCvEqw1Z7YZTWvd+ZAiX5vSN0WAI2hJTJTqKuCTcIH0fqNDCaOg3FBR38BL7seZrUmibIUcPU66iJ1w==",
|
|
||||||
"dependencies": {
|
|
||||||
"ajv-formats": "^2.1.1",
|
|
||||||
"ajv8": "npm:ajv@^8.11.0",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"lodash-es": "^4.17.15"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=14"
|
|
||||||
},
|
|
||||||
"peerDependencies": {
|
|
||||||
"@rjsf/utils": "^5.0.0-beta.12"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/@rollup/plugin-babel": {
|
"node_modules/@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
|
@ -6840,27 +6822,6 @@
|
||||||
"ajv": "^6.9.1"
|
"ajv": "^6.9.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/ajv8": {
|
|
||||||
"name": "ajv",
|
|
||||||
"version": "8.11.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
|
|
||||||
"integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
|
|
||||||
"dependencies": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"funding": {
|
|
||||||
"type": "github",
|
|
||||||
"url": "https://github.com/sponsors/epoberezkin"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/ajv8/node_modules/json-schema-traverse": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
|
||||||
},
|
|
||||||
"node_modules/ansi-align": {
|
"node_modules/ansi-align": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
|
@ -34883,17 +34844,6 @@
|
||||||
"lodash-es": "^4.17.15"
|
"lodash-es": "^4.17.15"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@rjsf/validator-ajv8": {
|
|
||||||
"version": "5.0.0-beta.13",
|
|
||||||
"resolved": "https://registry.npmjs.org/@rjsf/validator-ajv8/-/validator-ajv8-5.0.0-beta.13.tgz",
|
|
||||||
"integrity": "sha512-/hrYbiwgCvEqw1Z7YZTWvd+ZAiX5vSN0WAI2hJTJTqKuCTcIH0fqNDCaOg3FBR38BL7seZrUmibIUcPU66iJ1w==",
|
|
||||||
"requires": {
|
|
||||||
"ajv-formats": "^2.1.1",
|
|
||||||
"ajv8": "npm:ajv@^8.11.0",
|
|
||||||
"lodash": "^4.17.15",
|
|
||||||
"lodash-es": "^4.17.15"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"@rollup/plugin-babel": {
|
"@rollup/plugin-babel": {
|
||||||
"version": "5.3.1",
|
"version": "5.3.1",
|
||||||
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz",
|
||||||
|
@ -36367,24 +36317,6 @@
|
||||||
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
"integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
|
||||||
"requires": {}
|
"requires": {}
|
||||||
},
|
},
|
||||||
"ajv8": {
|
|
||||||
"version": "npm:ajv@8.11.2",
|
|
||||||
"resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz",
|
|
||||||
"integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==",
|
|
||||||
"requires": {
|
|
||||||
"fast-deep-equal": "^3.1.1",
|
|
||||||
"json-schema-traverse": "^1.0.0",
|
|
||||||
"require-from-string": "^2.0.2",
|
|
||||||
"uri-js": "^4.2.2"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"json-schema-traverse": {
|
|
||||||
"version": "1.0.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
|
|
||||||
"integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"ansi-align": {
|
"ansi-align": {
|
||||||
"version": "3.0.1",
|
"version": "3.0.1",
|
||||||
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
"resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz",
|
||||||
|
|
|
@ -20,12 +20,16 @@ import {
|
||||||
TableHeader,
|
TableHeader,
|
||||||
TableHead,
|
TableHead,
|
||||||
TableRow,
|
TableRow,
|
||||||
|
TimePicker,
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
} from '@carbon/react';
|
} from '@carbon/react';
|
||||||
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
|
import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config';
|
||||||
import {
|
import {
|
||||||
convertDateStringToSeconds,
|
convertDateAndTimeStringsToSeconds,
|
||||||
convertSecondsToFormattedDate,
|
convertDateObjectToFormattedHoursMinutes,
|
||||||
|
convertSecondsToFormattedDateString,
|
||||||
|
convertSecondsToFormattedDateTime,
|
||||||
|
convertSecondsToFormattedTimeHoursMinutes,
|
||||||
getPageInfoFromSearchParams,
|
getPageInfoFromSearchParams,
|
||||||
getProcessModelFullIdentifierFromSearchParams,
|
getProcessModelFullIdentifierFromSearchParams,
|
||||||
modifyProcessModelPath,
|
modifyProcessModelPath,
|
||||||
|
@ -49,6 +53,10 @@ type OwnProps = {
|
||||||
perPageOptions?: number[];
|
perPageOptions?: number[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
interface dateParameters {
|
||||||
|
[key: string]: ((..._args: any[]) => any)[];
|
||||||
|
}
|
||||||
|
|
||||||
export default function ProcessInstanceListTable({
|
export default function ProcessInstanceListTable({
|
||||||
filtersEnabled = true,
|
filtersEnabled = true,
|
||||||
processModelFullIdentifier,
|
processModelFullIdentifier,
|
||||||
|
@ -66,11 +74,20 @@ export default function ProcessInstanceListTable({
|
||||||
|
|
||||||
const oneHourInSeconds = 3600;
|
const oneHourInSeconds = 3600;
|
||||||
const oneMonthInSeconds = oneHourInSeconds * 24 * 30;
|
const oneMonthInSeconds = oneHourInSeconds * 24 * 30;
|
||||||
const [startFrom, setStartFrom] = useState<string>('');
|
const [startFromDate, setStartFromDate] = useState<string>('');
|
||||||
const [startTo, setStartTo] = useState<string>('');
|
const [startToDate, setStartToDate] = useState<string>('');
|
||||||
const [endFrom, setEndFrom] = useState<string>('');
|
const [endFromDate, setEndFromDate] = useState<string>('');
|
||||||
const [endTo, setEndTo] = useState<string>('');
|
const [endToDate, setEndToDate] = useState<string>('');
|
||||||
|
const [startFromTime, setStartFromTime] = useState<string>('');
|
||||||
|
const [startToTime, setStartToTime] = useState<string>('');
|
||||||
|
const [endFromTime, setEndFromTime] = useState<string>('');
|
||||||
|
const [endToTime, setEndToTime] = useState<string>('');
|
||||||
const [showFilterOptions, setShowFilterOptions] = useState<boolean>(false);
|
const [showFilterOptions, setShowFilterOptions] = useState<boolean>(false);
|
||||||
|
const [startFromTimeInvalid, setStartFromTimeInvalid] =
|
||||||
|
useState<boolean>(false);
|
||||||
|
const [startToTimeInvalid, setStartToTimeInvalid] = useState<boolean>(false);
|
||||||
|
const [endFromTimeInvalid, setEndFromTimeInvalid] = useState<boolean>(false);
|
||||||
|
const [endToTimeInvalid, setEndToTimeInvalid] = useState<boolean>(false);
|
||||||
|
|
||||||
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
const setErrorMessage = (useContext as any)(ErrorContext)[1];
|
||||||
|
|
||||||
|
@ -86,14 +103,23 @@ export default function ProcessInstanceListTable({
|
||||||
const [processModelSelection, setProcessModelSelection] =
|
const [processModelSelection, setProcessModelSelection] =
|
||||||
useState<ProcessModel | null>(null);
|
useState<ProcessModel | null>(null);
|
||||||
|
|
||||||
const parametersToAlwaysFilterBy = useMemo(() => {
|
const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
start_from: setStartFrom,
|
start_from: [setStartFromDate, setStartFromTime],
|
||||||
start_to: setStartTo,
|
start_to: [setStartToDate, setStartToTime],
|
||||||
end_from: setEndFrom,
|
end_from: [setEndFromDate, setEndFromTime],
|
||||||
end_to: setEndTo,
|
end_to: [setEndToDate, setEndToTime],
|
||||||
};
|
};
|
||||||
}, [setStartFrom, setStartTo, setEndFrom, setEndTo]);
|
}, [
|
||||||
|
setStartFromDate,
|
||||||
|
setStartFromTime,
|
||||||
|
setStartToDate,
|
||||||
|
setStartToTime,
|
||||||
|
setEndFromDate,
|
||||||
|
setEndFromTime,
|
||||||
|
setEndToDate,
|
||||||
|
setEndToTime,
|
||||||
|
]);
|
||||||
|
|
||||||
const parametersToGetFromSearchParams = useMemo(() => {
|
const parametersToGetFromSearchParams = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
|
@ -130,19 +156,27 @@ export default function ProcessInstanceListTable({
|
||||||
queryParamString += `&user_filter=${userAppliedFilter}`;
|
queryParamString += `&user_filter=${userAppliedFilter}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => {
|
Object.keys(dateParametersToAlwaysFilterBy).forEach(
|
||||||
// @ts-expect-error TS(7053) FIXME:
|
(paramName: string) => {
|
||||||
const functionToCall = parametersToAlwaysFilterBy[paramName];
|
const dateFunctionToCall =
|
||||||
const searchParamValue = searchParams.get(paramName);
|
dateParametersToAlwaysFilterBy[paramName][0];
|
||||||
if (searchParamValue) {
|
const timeFunctionToCall =
|
||||||
queryParamString += `&${paramName}=${searchParamValue}`;
|
dateParametersToAlwaysFilterBy[paramName][1];
|
||||||
const dateString = convertSecondsToFormattedDate(
|
const searchParamValue = searchParams.get(paramName);
|
||||||
searchParamValue as any
|
if (searchParamValue) {
|
||||||
);
|
queryParamString += `&${paramName}=${searchParamValue}`;
|
||||||
functionToCall(dateString);
|
const dateString = convertSecondsToFormattedDateString(
|
||||||
setShowFilterOptions(true);
|
searchParamValue as any
|
||||||
|
);
|
||||||
|
dateFunctionToCall(dateString);
|
||||||
|
const timeString = convertSecondsToFormattedTimeHoursMinutes(
|
||||||
|
searchParamValue as any
|
||||||
|
);
|
||||||
|
timeFunctionToCall(timeString);
|
||||||
|
setShowFilterOptions(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
Object.keys(parametersToGetFromSearchParams).forEach(
|
Object.keys(parametersToGetFromSearchParams).forEach(
|
||||||
(paramName: string) => {
|
(paramName: string) => {
|
||||||
|
@ -211,7 +245,7 @@ export default function ProcessInstanceListTable({
|
||||||
params,
|
params,
|
||||||
oneMonthInSeconds,
|
oneMonthInSeconds,
|
||||||
oneHourInSeconds,
|
oneHourInSeconds,
|
||||||
parametersToAlwaysFilterBy,
|
dateParametersToAlwaysFilterBy,
|
||||||
parametersToGetFromSearchParams,
|
parametersToGetFromSearchParams,
|
||||||
filtersEnabled,
|
filtersEnabled,
|
||||||
paginationQueryParamPrefix,
|
paginationQueryParamPrefix,
|
||||||
|
@ -219,16 +253,25 @@ export default function ProcessInstanceListTable({
|
||||||
perPageOptions,
|
perPageOptions,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
// This sets the filter data using the saved reports returned from the initial instance_list query.
|
||||||
|
// This could probably be merged into the main useEffect but it works here now.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const filters = processInstanceFilters as any;
|
const filters = processInstanceFilters as any;
|
||||||
Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => {
|
Object.keys(dateParametersToAlwaysFilterBy).forEach((paramName: string) => {
|
||||||
// @ts-expect-error TS(7053) FIXME:
|
const dateFunctionToCall = dateParametersToAlwaysFilterBy[paramName][0];
|
||||||
const functionToCall = parametersToAlwaysFilterBy[paramName];
|
const timeFunctionToCall = dateParametersToAlwaysFilterBy[paramName][1];
|
||||||
const paramValue = filters[paramName];
|
const paramValue = filters[paramName];
|
||||||
functionToCall('');
|
dateFunctionToCall('');
|
||||||
|
timeFunctionToCall('');
|
||||||
if (paramValue) {
|
if (paramValue) {
|
||||||
const dateString = convertSecondsToFormattedDate(paramValue as any);
|
const dateString = convertSecondsToFormattedDateString(
|
||||||
functionToCall(dateString);
|
paramValue as any
|
||||||
|
);
|
||||||
|
dateFunctionToCall(dateString);
|
||||||
|
const timeString = convertSecondsToFormattedTimeHoursMinutes(
|
||||||
|
paramValue as any
|
||||||
|
);
|
||||||
|
timeFunctionToCall(timeString);
|
||||||
setShowFilterOptions(true);
|
setShowFilterOptions(true);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -253,7 +296,7 @@ export default function ProcessInstanceListTable({
|
||||||
setProcessStatusSelection(processStatusSelectedArray);
|
setProcessStatusSelection(processStatusSelectedArray);
|
||||||
}, [
|
}, [
|
||||||
processInstanceFilters,
|
processInstanceFilters,
|
||||||
parametersToAlwaysFilterBy,
|
dateParametersToAlwaysFilterBy,
|
||||||
parametersToGetFromSearchParams,
|
parametersToGetFromSearchParams,
|
||||||
processModelAvailableItems,
|
processModelAvailableItems,
|
||||||
]);
|
]);
|
||||||
|
@ -285,10 +328,22 @@ export default function ProcessInstanceListTable({
|
||||||
);
|
);
|
||||||
let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`;
|
let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`;
|
||||||
|
|
||||||
const startFromSeconds = convertDateStringToSeconds(startFrom);
|
const startFromSeconds = convertDateAndTimeStringsToSeconds(
|
||||||
const endFromSeconds = convertDateStringToSeconds(endFrom);
|
startFromDate,
|
||||||
const startToSeconds = convertDateStringToSeconds(startTo);
|
startFromTime
|
||||||
const endToSeconds = convertDateStringToSeconds(endTo);
|
);
|
||||||
|
const startToSeconds = convertDateAndTimeStringsToSeconds(
|
||||||
|
startToDate,
|
||||||
|
startToTime
|
||||||
|
);
|
||||||
|
const endFromSeconds = convertDateAndTimeStringsToSeconds(
|
||||||
|
endFromDate,
|
||||||
|
endFromTime
|
||||||
|
);
|
||||||
|
const endToSeconds = convertDateAndTimeStringsToSeconds(
|
||||||
|
endToDate,
|
||||||
|
endToTime
|
||||||
|
);
|
||||||
if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
|
if (isTrueComparison(startFromSeconds, '>', startToSeconds)) {
|
||||||
setErrorMessage({
|
setErrorMessage({
|
||||||
message: '"Start date from" cannot be after "start date to"',
|
message: '"Start date from" cannot be after "start date to"',
|
||||||
|
@ -342,24 +397,50 @@ export default function ProcessInstanceListTable({
|
||||||
labelString: any,
|
labelString: any,
|
||||||
name: any,
|
name: any,
|
||||||
initialDate: any,
|
initialDate: any,
|
||||||
onChangeFunction: any
|
initialTime: string,
|
||||||
|
onChangeDateFunction: any,
|
||||||
|
onChangeTimeFunction: any,
|
||||||
|
timeInvalid: boolean,
|
||||||
|
setTimeInvalid: any
|
||||||
) => {
|
) => {
|
||||||
return (
|
return (
|
||||||
<DatePicker dateFormat={DATE_FORMAT_CARBON} datePickerType="single">
|
<>
|
||||||
<DatePickerInput
|
<DatePicker dateFormat={DATE_FORMAT_CARBON} datePickerType="single">
|
||||||
id={`date-picker-${name}`}
|
<DatePickerInput
|
||||||
placeholder={DATE_FORMAT}
|
id={`date-picker-${name}`}
|
||||||
labelText={labelString}
|
placeholder={DATE_FORMAT}
|
||||||
type="text"
|
labelText={labelString}
|
||||||
size="md"
|
type="text"
|
||||||
autocomplete="off"
|
size="md"
|
||||||
allowInput={false}
|
autocomplete="off"
|
||||||
onChange={(dateChangeEvent: any) => {
|
allowInput={false}
|
||||||
onChangeFunction(dateChangeEvent.srcElement.value);
|
onChange={(dateChangeEvent: any) => {
|
||||||
|
if (!initialDate && !initialTime) {
|
||||||
|
onChangeTimeFunction(
|
||||||
|
convertDateObjectToFormattedHoursMinutes(new Date())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
onChangeDateFunction(dateChangeEvent.srcElement.value);
|
||||||
|
}}
|
||||||
|
value={initialDate}
|
||||||
|
/>
|
||||||
|
</DatePicker>
|
||||||
|
<TimePicker
|
||||||
|
invalid={timeInvalid}
|
||||||
|
id="time-picker"
|
||||||
|
labelText="Select a time"
|
||||||
|
pattern="^([01]\d|2[0-3]):?([0-5]\d)$"
|
||||||
|
value={initialTime}
|
||||||
|
onChange={(event: any) => {
|
||||||
|
if (event.srcElement.validity.valid) {
|
||||||
|
setTimeInvalid(false);
|
||||||
|
} else {
|
||||||
|
setTimeInvalid(true);
|
||||||
|
}
|
||||||
|
onChangeTimeFunction(event.srcElement.value);
|
||||||
}}
|
}}
|
||||||
value={initialDate}
|
|
||||||
/>
|
/>
|
||||||
</DatePicker>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -386,10 +467,14 @@ export default function ProcessInstanceListTable({
|
||||||
const clearFilters = () => {
|
const clearFilters = () => {
|
||||||
setProcessModelSelection(null);
|
setProcessModelSelection(null);
|
||||||
setProcessStatusSelection([]);
|
setProcessStatusSelection([]);
|
||||||
setStartFrom('');
|
setStartFromDate('');
|
||||||
setStartTo('');
|
setStartFromTime('');
|
||||||
setEndFrom('');
|
setStartToDate('');
|
||||||
setEndTo('');
|
setStartToTime('');
|
||||||
|
setEndFromDate('');
|
||||||
|
setEndFromTime('');
|
||||||
|
setEndToDate('');
|
||||||
|
setEndToTime('');
|
||||||
};
|
};
|
||||||
|
|
||||||
const filterOptions = () => {
|
const filterOptions = () => {
|
||||||
|
@ -415,18 +500,49 @@ export default function ProcessInstanceListTable({
|
||||||
{dateComponent(
|
{dateComponent(
|
||||||
'Start date from',
|
'Start date from',
|
||||||
'start-from',
|
'start-from',
|
||||||
startFrom,
|
startFromDate,
|
||||||
setStartFrom
|
startFromTime,
|
||||||
|
setStartFromDate,
|
||||||
|
setStartFromTime,
|
||||||
|
startFromTimeInvalid,
|
||||||
|
setStartFromTimeInvalid
|
||||||
)}
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
<Column md={4}>
|
<Column md={4}>
|
||||||
{dateComponent('Start date to', 'start-to', startTo, setStartTo)}
|
{dateComponent(
|
||||||
|
'Start date to',
|
||||||
|
'start-to',
|
||||||
|
startToDate,
|
||||||
|
startToTime,
|
||||||
|
setStartToDate,
|
||||||
|
setStartToTime,
|
||||||
|
startToTimeInvalid,
|
||||||
|
setStartToTimeInvalid
|
||||||
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
<Column md={4}>
|
<Column md={4}>
|
||||||
{dateComponent('End date from', 'end-from', endFrom, setEndFrom)}
|
{dateComponent(
|
||||||
|
'End date from',
|
||||||
|
'end-from',
|
||||||
|
endFromDate,
|
||||||
|
endFromTime,
|
||||||
|
setEndFromDate,
|
||||||
|
setEndFromTime,
|
||||||
|
endFromTimeInvalid,
|
||||||
|
setEndFromTimeInvalid
|
||||||
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
<Column md={4}>
|
<Column md={4}>
|
||||||
{dateComponent('End date to', 'end-to', endTo, setEndTo)}
|
{dateComponent(
|
||||||
|
'End date to',
|
||||||
|
'end-to',
|
||||||
|
endToDate,
|
||||||
|
endToTime,
|
||||||
|
setEndToDate,
|
||||||
|
setEndToTime,
|
||||||
|
endToTimeInvalid,
|
||||||
|
setEndToTimeInvalid
|
||||||
|
)}
|
||||||
</Column>
|
</Column>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid fullWidth className="with-bottom-margin">
|
<Grid fullWidth className="with-bottom-margin">
|
||||||
|
@ -493,7 +609,7 @@ export default function ProcessInstanceListTable({
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
const formatSecondsForDisplay = (_row: any, seconds: any) => {
|
const formatSecondsForDisplay = (_row: any, seconds: any) => {
|
||||||
return convertSecondsToFormattedDate(seconds) || '-';
|
return convertSecondsToFormattedDateTime(seconds) || '-';
|
||||||
};
|
};
|
||||||
const defaultFormatter = (_row: any, value: any) => {
|
const defaultFormatter = (_row: any, value: any) => {
|
||||||
return value;
|
return value;
|
||||||
|
|
|
@ -18,5 +18,6 @@ export const PROCESS_STATUSES = [
|
||||||
|
|
||||||
// with time: yyyy-MM-dd HH:mm:ss
|
// with time: yyyy-MM-dd HH:mm:ss
|
||||||
export const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
|
export const DATE_TIME_FORMAT = 'yyyy-MM-dd HH:mm:ss';
|
||||||
|
export const TIME_FORMAT_HOURS_MINUTES = 'HH:mm';
|
||||||
export const DATE_FORMAT = 'yyyy-MM-dd';
|
export const DATE_FORMAT = 'yyyy-MM-dd';
|
||||||
export const DATE_FORMAT_CARBON = 'Y-m-d';
|
export const DATE_FORMAT_CARBON = 'Y-m-d';
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { convertSecondsToFormattedDate, slugifyString } from './helpers';
|
import { convertSecondsToFormattedDateString, slugifyString } from './helpers';
|
||||||
|
|
||||||
test('it can slugify a string', () => {
|
test('it can slugify a string', () => {
|
||||||
expect(slugifyString('hello---world_ and then Some such-')).toEqual(
|
expect(slugifyString('hello---world_ and then Some such-')).toEqual(
|
||||||
|
@ -7,6 +7,6 @@ test('it can slugify a string', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('it can keep the correct date when converting seconds to date', () => {
|
test('it can keep the correct date when converting seconds to date', () => {
|
||||||
const dateString = convertSecondsToFormattedDate(1666325400);
|
const dateString = convertSecondsToFormattedDateString(1666325400);
|
||||||
expect(dateString).toEqual('2022-10-21');
|
expect(dateString).toEqual('2022-10-21');
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import { format } from 'date-fns';
|
import { format } from 'date-fns';
|
||||||
import { DATE_TIME_FORMAT, DATE_FORMAT } from './config';
|
import {
|
||||||
|
DATE_TIME_FORMAT,
|
||||||
|
DATE_FORMAT,
|
||||||
|
TIME_FORMAT_HOURS_MINUTES,
|
||||||
|
} from './config';
|
||||||
import {
|
import {
|
||||||
DEFAULT_PER_PAGE,
|
DEFAULT_PER_PAGE,
|
||||||
DEFAULT_PAGE,
|
DEFAULT_PAGE,
|
||||||
|
@ -42,27 +46,72 @@ export const convertDateToSeconds = (
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const convertDateObjectToFormattedString = (dateObject: Date) => {
|
||||||
|
if (dateObject) {
|
||||||
|
return format(dateObject, DATE_FORMAT);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertDateAndTimeStringsToDate = (
|
||||||
|
dateString: string,
|
||||||
|
timeString: string
|
||||||
|
) => {
|
||||||
|
if (dateString && timeString) {
|
||||||
|
return new Date(`${dateString}T${timeString}`);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertDateAndTimeStringsToSeconds = (
|
||||||
|
dateString: string,
|
||||||
|
timeString: string
|
||||||
|
) => {
|
||||||
|
const dateObject = convertDateAndTimeStringsToDate(dateString, timeString);
|
||||||
|
if (dateObject) {
|
||||||
|
return convertDateToSeconds(dateObject);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
export const convertStringToDate = (dateString: string) => {
|
export const convertStringToDate = (dateString: string) => {
|
||||||
if (dateString) {
|
return convertDateAndTimeStringsToSeconds(dateString, '00:10:00');
|
||||||
// add midnight time to the date so it c uses the correct date
|
};
|
||||||
// after converting to timezone
|
|
||||||
return new Date(`${dateString}T00:10:00`);
|
export const convertSecondsToDateObject = (seconds: number) => {
|
||||||
|
if (seconds) {
|
||||||
|
return new Date(seconds * 1000);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const convertSecondsToFormattedDateTime = (seconds: number) => {
|
export const convertSecondsToFormattedDateTime = (seconds: number) => {
|
||||||
if (seconds) {
|
const dateObject = convertSecondsToDateObject(seconds);
|
||||||
const dateObject = new Date(seconds * 1000);
|
if (dateObject) {
|
||||||
return format(dateObject, DATE_TIME_FORMAT);
|
return format(dateObject, DATE_TIME_FORMAT);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const convertSecondsToFormattedDate = (seconds: number) => {
|
export const convertDateObjectToFormattedHoursMinutes = (dateObject: Date) => {
|
||||||
if (seconds) {
|
if (dateObject) {
|
||||||
const dateObject = new Date(seconds * 1000);
|
return format(dateObject, TIME_FORMAT_HOURS_MINUTES);
|
||||||
return format(dateObject, DATE_FORMAT);
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertSecondsToFormattedTimeHoursMinutes = (seconds: number) => {
|
||||||
|
const dateObject = convertSecondsToDateObject(seconds);
|
||||||
|
if (dateObject) {
|
||||||
|
return convertDateObjectToFormattedHoursMinutes(dateObject);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const convertSecondsToFormattedDateString = (seconds: number) => {
|
||||||
|
const dateObject = convertSecondsToDateObject(seconds);
|
||||||
|
if (dateObject) {
|
||||||
|
return convertDateObjectToFormattedString(dateObject);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
|
@ -5,7 +5,7 @@ import { Link, useParams, useSearchParams } from 'react-router-dom';
|
||||||
import PaginationForTable from '../components/PaginationForTable';
|
import PaginationForTable from '../components/PaginationForTable';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import {
|
import {
|
||||||
convertSecondsToFormattedDate,
|
convertSecondsToFormattedDateString,
|
||||||
getPageInfoFromSearchParams,
|
getPageInfoFromSearchParams,
|
||||||
modifyProcessModelPath,
|
modifyProcessModelPath,
|
||||||
unModifyProcessModelPath,
|
unModifyProcessModelPath,
|
||||||
|
@ -68,7 +68,9 @@ export default function MessageInstanceList() {
|
||||||
<td>{rowToUse.failure_cause || '-'}</td>
|
<td>{rowToUse.failure_cause || '-'}</td>
|
||||||
<td>{rowToUse.status}</td>
|
<td>{rowToUse.status}</td>
|
||||||
<td>
|
<td>
|
||||||
{convertSecondsToFormattedDate(rowToUse.created_at_in_seconds)}
|
{convertSecondsToFormattedDateString(
|
||||||
|
rowToUse.created_at_in_seconds
|
||||||
|
)}
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
);
|
);
|
||||||
|
|
|
@ -6,7 +6,7 @@ import PaginationForTable from '../components/PaginationForTable';
|
||||||
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
import ProcessBreadcrumb from '../components/ProcessBreadcrumb';
|
||||||
import {
|
import {
|
||||||
getPageInfoFromSearchParams,
|
getPageInfoFromSearchParams,
|
||||||
convertSecondsToFormattedDate,
|
convertSecondsToFormattedDateString,
|
||||||
modifyProcessModelPath,
|
modifyProcessModelPath,
|
||||||
unModifyProcessModelPath,
|
unModifyProcessModelPath,
|
||||||
} from '../helpers';
|
} from '../helpers';
|
||||||
|
@ -49,7 +49,7 @@ export default function ProcessInstanceLogList() {
|
||||||
data-qa="process-instance-show-link"
|
data-qa="process-instance-show-link"
|
||||||
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`}
|
to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`}
|
||||||
>
|
>
|
||||||
{convertSecondsToFormattedDate(rowToUse.timestamp)}
|
{convertSecondsToFormattedDateString(rowToUse.timestamp)}
|
||||||
</Link>
|
</Link>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { useContext, useEffect, useState } from 'react';
|
import { useContext, useEffect, useState } from 'react';
|
||||||
import { Link, useNavigate, useParams } from 'react-router-dom';
|
import { useNavigate, useParams } from 'react-router-dom';
|
||||||
|
|
||||||
// FIXME: npm install @rjsf/validator-ajv8 and use it as soon as
|
// FIXME: npm install @rjsf/validator-ajv8 and use it as soon as
|
||||||
// rawErrors is fixed.
|
// rawErrors is fixed.
|
||||||
|
@ -9,8 +9,14 @@ import { Link, useNavigate, useParams } from 'react-router-dom';
|
||||||
// https://github.com/rjsf-team/react-jsonschema-form/blob/main/docs/api-reference/uiSchema.md talks about rawErrors
|
// https://github.com/rjsf-team/react-jsonschema-form/blob/main/docs/api-reference/uiSchema.md talks about rawErrors
|
||||||
import validator from '@rjsf/validator-ajv6';
|
import validator from '@rjsf/validator-ajv6';
|
||||||
|
|
||||||
// @ts-ignore
|
import {
|
||||||
import { Button, Stack } from '@carbon/react';
|
TabList,
|
||||||
|
Tab,
|
||||||
|
Tabs,
|
||||||
|
Grid,
|
||||||
|
Column,
|
||||||
|
// @ts-ignore
|
||||||
|
} from '@carbon/react';
|
||||||
|
|
||||||
import ReactMarkdown from 'react-markdown';
|
import ReactMarkdown from 'react-markdown';
|
||||||
import remarkGfm from 'remark-gfm';
|
import remarkGfm from 'remark-gfm';
|
||||||
|
@ -73,39 +79,52 @@ export default function TaskShow() {
|
||||||
|
|
||||||
const buildTaskNavigation = () => {
|
const buildTaskNavigation = () => {
|
||||||
let userTasksElement;
|
let userTasksElement;
|
||||||
|
let selectedTabIndex = 0;
|
||||||
if (userTasks) {
|
if (userTasks) {
|
||||||
userTasksElement = (userTasks as any).map(function getUserTasksElement(
|
userTasksElement = (userTasks as any).map(function getUserTasksElement(
|
||||||
userTask: any
|
userTask: any,
|
||||||
|
index: number
|
||||||
) {
|
) {
|
||||||
const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`;
|
const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`;
|
||||||
if (userTask.id === params.task_id) {
|
if (userTask.id === params.task_id) {
|
||||||
return <span>{userTask.name}</span>;
|
selectedTabIndex = index;
|
||||||
|
return <Tab selected>{userTask.title}</Tab>;
|
||||||
}
|
}
|
||||||
if (userTask.state === 'COMPLETED') {
|
if (userTask.state === 'COMPLETED') {
|
||||||
return (
|
return (
|
||||||
<Link to={taskUrl} data-qa={`form-nav-${userTask.name}`}>
|
<Tab
|
||||||
{userTask.name}
|
onClick={() => navigate(taskUrl)}
|
||||||
</Link>
|
data-qa={`form-nav-${userTask.name}`}
|
||||||
|
>
|
||||||
|
{userTask.title}
|
||||||
|
</Tab>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (userTask.state === 'FUTURE') {
|
if (userTask.state === 'FUTURE') {
|
||||||
return <span style={{ color: 'red' }}>{userTask.name}</span>;
|
return <Tab disabled>{userTask.title}</Tab>;
|
||||||
}
|
}
|
||||||
if (userTask.state === 'READY') {
|
if (userTask.state === 'READY') {
|
||||||
return (
|
return (
|
||||||
<Link to={taskUrl} data-qa={`form-nav-${userTask.name}`}>
|
<Tab
|
||||||
{userTask.name} - Current
|
onClick={() => navigate(taskUrl)}
|
||||||
</Link>
|
data-qa={`form-nav-${userTask.name}`}
|
||||||
|
>
|
||||||
|
{userTask.title}
|
||||||
|
</Tab>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<Stack orientation="horizontal" gap={3}>
|
<Tabs
|
||||||
<Button href="/tasks">Go Back To List</Button>
|
title="Steps in this process instance involving people"
|
||||||
{userTasksElement}
|
selectedIndex={selectedTabIndex}
|
||||||
</Stack>
|
>
|
||||||
|
<TabList aria-label="List of tabs" contained>
|
||||||
|
{userTasksElement}
|
||||||
|
</TabList>
|
||||||
|
</Tabs>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -149,15 +168,19 @@ export default function TaskShow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Form
|
<Grid fullWidth condensed>
|
||||||
formData={taskData}
|
<Column md={5} lg={8} sm={4}>
|
||||||
onSubmit={handleFormSubmit}
|
<Form
|
||||||
schema={jsonSchema}
|
formData={taskData}
|
||||||
uiSchema={formUiSchema}
|
onSubmit={handleFormSubmit}
|
||||||
validator={validator}
|
schema={jsonSchema}
|
||||||
>
|
uiSchema={formUiSchema}
|
||||||
{reactFragmentToHideSubmitButton}
|
validator={validator}
|
||||||
</Form>
|
>
|
||||||
|
{reactFragmentToHideSubmitButton}
|
||||||
|
</Form>
|
||||||
|
</Column>
|
||||||
|
</Grid>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -175,7 +198,7 @@ export default function TaskShow() {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (task) {
|
if (task && userTasks) {
|
||||||
const taskToUse = task as any;
|
const taskToUse = task as any;
|
||||||
let statusString = '';
|
let statusString = '';
|
||||||
if (taskToUse.state !== 'READY') {
|
if (taskToUse.state !== 'READY') {
|
||||||
|
|
|
@ -82,6 +82,9 @@ export default function BaseInputTemplate<
|
||||||
} else if (schema && schema.title) {
|
} else if (schema && schema.title) {
|
||||||
labelToUse = schema.title;
|
labelToUse = schema.title;
|
||||||
}
|
}
|
||||||
|
if (required) {
|
||||||
|
labelToUse = `${labelToUse}*`;
|
||||||
|
}
|
||||||
|
|
||||||
let invalid = false;
|
let invalid = false;
|
||||||
let errorMessageForField = null;
|
let errorMessageForField = null;
|
||||||
|
|
Loading…
Reference in New Issue