From 9f2d5244475f20ddd265461f3ceb542791087570 Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 17 Nov 2022 11:52:57 -0500 Subject: [PATCH 1/4] a little cleanup to forms w/ burnettk --- spiffworkflow-frontend/package-lock.json | 68 ----------------- .../components/ProcessInstanceListTable.tsx | 43 +++++++---- .../src/routes/TaskShow.tsx | 75 ++++++++++++------- .../BaseInputTemplate/BaseInputTemplate.tsx | 3 + 4 files changed, 81 insertions(+), 108 deletions(-) diff --git a/spiffworkflow-frontend/package-lock.json b/spiffworkflow-frontend/package-lock.json index 4898b19cf..0dfe841ab 100644 --- a/spiffworkflow-frontend/package-lock.json +++ b/spiffworkflow-frontend/package-lock.json @@ -24,7 +24,6 @@ "@rjsf/mui": "^5.0.0-beta.13", "@rjsf/utils": "^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", "@testing-library/jest-dom": "^5.16.4", "@testing-library/react": "^13.3.0", @@ -4950,23 +4949,6 @@ "@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": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -6840,27 +6822,6 @@ "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": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -34883,17 +34844,6 @@ "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": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.3.1.tgz", @@ -36367,24 +36317,6 @@ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", "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": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 5c7786c00..407ac307a 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -20,12 +20,16 @@ import { TableHeader, TableHead, TableRow, + TimePicker, + TimePickerSelect, + SelectItem, // @ts-ignore } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { convertDateStringToSeconds, convertSecondsToFormattedDate, + convertSecondsToFormattedDateTime, getPageInfoFromSearchParams, getProcessModelFullIdentifierFromSearchParams, modifyProcessModelPath, @@ -345,21 +349,32 @@ export default function ProcessInstanceListTable({ onChangeFunction: any ) => { return ( - - { - onChangeFunction(dateChangeEvent.srcElement.value); + <> + + { + onChangeFunction(dateChangeEvent.srcElement.value); + }} + value={initialDate} + /> + + { + console.log('event', event); }} - value={initialDate} /> - + ); }; @@ -493,7 +508,7 @@ export default function ProcessInstanceListTable({ ); }; const formatSecondsForDisplay = (_row: any, seconds: any) => { - return convertSecondsToFormattedDate(seconds) || '-'; + return convertSecondsToFormattedDateTime(seconds) || '-'; }; const defaultFormatter = (_row: any, value: any) => { return value; diff --git a/spiffworkflow-frontend/src/routes/TaskShow.tsx b/spiffworkflow-frontend/src/routes/TaskShow.tsx index 9da0e30a7..1309b8f3a 100644 --- a/spiffworkflow-frontend/src/routes/TaskShow.tsx +++ b/spiffworkflow-frontend/src/routes/TaskShow.tsx @@ -1,5 +1,5 @@ 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 // 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 import validator from '@rjsf/validator-ajv6'; -// @ts-ignore -import { Button, Stack } from '@carbon/react'; +import { + TabList, + Tab, + Tabs, + Grid, + Column, + // @ts-ignore +} from '@carbon/react'; import ReactMarkdown from 'react-markdown'; import remarkGfm from 'remark-gfm'; @@ -73,39 +79,52 @@ export default function TaskShow() { const buildTaskNavigation = () => { let userTasksElement; + let selectedTabIndex = 0; if (userTasks) { userTasksElement = (userTasks as any).map(function getUserTasksElement( - userTask: any + userTask: any, + index: number ) { const taskUrl = `/tasks/${params.process_instance_id}/${userTask.id}`; if (userTask.id === params.task_id) { - return {userTask.name}; + selectedTabIndex = index; + return {userTask.title}; } if (userTask.state === 'COMPLETED') { return ( - - {userTask.name} - + navigate(taskUrl)} + data-qa={`form-nav-${userTask.name}`} + > + {userTask.title} + ); } if (userTask.state === 'FUTURE') { - return {userTask.name}; + return {userTask.title}; } if (userTask.state === 'READY') { return ( - - {userTask.name} - Current - + navigate(taskUrl)} + data-qa={`form-nav-${userTask.name}`} + > + {userTask.title} + ); } return null; }); } return ( - - - {userTasksElement} - + + + {userTasksElement} + + ); }; @@ -149,15 +168,19 @@ export default function TaskShow() { } return ( -
- {reactFragmentToHideSubmitButton} -
+ + +
+ {reactFragmentToHideSubmitButton} +
+
+
); }; @@ -175,7 +198,7 @@ export default function TaskShow() { ); }; - if (task) { + if (task && userTasks) { const taskToUse = task as any; let statusString = ''; if (taskToUse.state !== 'READY') { diff --git a/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx b/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx index 90cc3f0f8..0670c69d6 100644 --- a/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx +++ b/spiffworkflow-frontend/src/themes/carbon/BaseInputTemplate/BaseInputTemplate.tsx @@ -82,6 +82,9 @@ export default function BaseInputTemplate< } else if (schema && schema.title) { labelToUse = schema.title; } + if (required) { + labelToUse = `${labelToUse}*`; + } let invalid = false; let errorMessageForField = null; From f49d76429eedf1e79cb810be750d1453bb79ebac Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 17 Nov 2022 15:03:11 -0500 Subject: [PATCH 2/4] attempting to use date objects as the date states w/ burnettk --- .../components/ProcessInstanceListTable.tsx | 126 ++++++++++-------- spiffworkflow-frontend/src/helpers.test.tsx | 4 +- spiffworkflow-frontend/src/helpers.tsx | 26 +++- .../src/routes/MessageInstanceList.tsx | 6 +- .../src/routes/ProcessInstanceLogList.tsx | 4 +- 5 files changed, 95 insertions(+), 71 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 407ac307a..84c3fd62a 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -27,8 +27,11 @@ import { } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { + convertDateObjectToFormattedString, convertDateStringToSeconds, - convertSecondsToFormattedDate, + convertDateToSeconds, + convertSecondsToDateObject, + convertSecondsToFormattedDateString, convertSecondsToFormattedDateTime, getPageInfoFromSearchParams, getProcessModelFullIdentifierFromSearchParams, @@ -70,10 +73,13 @@ export default function ProcessInstanceListTable({ 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 [startFrom, setStartFrom] = useState( + null + ); + const [startTo, setStartTo] = useState(null); + const [startFromString, setStartFromString] = useState(''); + const [endFrom, setEndFrom] = useState(null); + const [endTo, setEndTo] = useState(null); const [showFilterOptions, setShowFilterOptions] = useState(false); const setErrorMessage = (useContext as any)(ErrorContext)[1]; @@ -140,10 +146,11 @@ export default function ProcessInstanceListTable({ const searchParamValue = searchParams.get(paramName); if (searchParamValue) { queryParamString += `&${paramName}=${searchParamValue}`; - const dateString = convertSecondsToFormattedDate( + const dateObject = convertSecondsToDateObject( searchParamValue as any ); - functionToCall(dateString); + console.log('dateObject1', dateObject); + functionToCall(dateObject); setShowFilterOptions(true); } }); @@ -160,6 +167,10 @@ export default function ProcessInstanceListTable({ const functionToCall = parametersToGetFromSearchParams[paramName]; queryParamString += `&${paramName}=${searchParams.get(paramName)}`; if (functionToCall !== null) { + console.log( + 'searchParams.get(paramName)', + searchParams.get(paramName) + ); functionToCall(searchParams.get(paramName) || ''); } setShowFilterOptions(true); @@ -223,45 +234,6 @@ export default function ProcessInstanceListTable({ perPageOptions, ]); - useEffect(() => { - const filters = processInstanceFilters as any; - Object.keys(parametersToAlwaysFilterBy).forEach((paramName: string) => { - // @ts-expect-error TS(7053) FIXME: - const functionToCall = parametersToAlwaysFilterBy[paramName]; - const paramValue = filters[paramName]; - functionToCall(''); - if (paramValue) { - const dateString = convertSecondsToFormattedDate(paramValue as any); - functionToCall(dateString); - setShowFilterOptions(true); - } - }); - - setProcessModelSelection(null); - processModelAvailableItems.forEach((item: any) => { - if (item.id === filters.process_model_identifier) { - setProcessModelSelection(item); - } - }); - - const processStatusSelectedArray: string[] = []; - if (filters.process_status) { - PROCESS_STATUSES.forEach((processStatusOption: any) => { - const regex = new RegExp(`\\b${processStatusOption}\\b`); - if (filters.process_status.match(regex)) { - processStatusSelectedArray.push(processStatusOption); - } - }); - setShowFilterOptions(true); - } - setProcessStatusSelection(processStatusSelectedArray); - }, [ - processInstanceFilters, - parametersToAlwaysFilterBy, - parametersToGetFromSearchParams, - processModelAvailableItems, - ]); - // 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) => { @@ -289,10 +261,10 @@ export default function ProcessInstanceListTable({ ); let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`; - const startFromSeconds = convertDateStringToSeconds(startFrom); - const endFromSeconds = convertDateStringToSeconds(endFrom); - const startToSeconds = convertDateStringToSeconds(startTo); - const endToSeconds = convertDateStringToSeconds(endTo); + const startFromSeconds = convertDateToSeconds(startFrom); + const endFromSeconds = convertDateToSeconds(endFrom); + const startToSeconds = convertDateToSeconds(startTo); + const endToSeconds = convertDateToSeconds(endTo); if (isTrueComparison(startFromSeconds, '>', startToSeconds)) { setErrorMessage({ message: '"Start date from" cannot be after "start date to"', @@ -346,8 +318,10 @@ export default function ProcessInstanceListTable({ labelString: any, name: any, initialDate: any, + initialDateString: string, onChangeFunction: any ) => { + // value={convertDateObjectToFormattedString(initialDate)} return ( <> @@ -360,9 +334,20 @@ export default function ProcessInstanceListTable({ autocomplete="off" allowInput={false} onChange={(dateChangeEvent: any) => { - onChangeFunction(dateChangeEvent.srcElement.value); + let dateTime = new Date(); + if (initialDate) { + dateTime = new Date(initialDate.getTime()); + } + + const [year, month, day] = + dateChangeEvent.srcElement.value.split('-'); + dateTime.setDate(day); + dateTime.setMonth(month - 1); + // @ts-ignore setYear does exist on Date + dateTime.setYear(year); + onChangeFunction(dateTime); }} - value={initialDate} + value={initialDateString} /> { setProcessModelSelection(null); setProcessStatusSelection([]); - setStartFrom(''); - setStartTo(''); - setEndFrom(''); - setEndTo(''); + setStartFrom(null); + setStartTo(null); + setEndFrom(null); + setEndTo(null); }; const filterOptions = () => { if (!showFilterOptions) { return null; } + console.log('startFrom', startFrom); return ( <> @@ -431,17 +417,36 @@ export default function ProcessInstanceListTable({ 'Start date from', 'start-from', startFrom, + startFromString, setStartFrom )} - {dateComponent('Start date to', 'start-to', startTo, setStartTo)} + {dateComponent( + 'Start date to', + 'start-to', + startTo, + startFromString, + setStartTo + )} - {dateComponent('End date from', 'end-from', endFrom, setEndFrom)} + {dateComponent( + 'End date from', + 'end-from', + endFrom, + startFromString, + setEndFrom + )} - {dateComponent('End date to', 'end-to', endTo, setEndTo)} + {dateComponent( + 'End date to', + 'end-to', + endTo, + startFromString, + setEndTo + )} @@ -587,6 +592,9 @@ export default function ProcessInstanceListTable({ }; if (pagination) { + setStartFromString( + convertDateObjectToFormattedString(startFrom as any) || '' + ); // eslint-disable-next-line prefer-const let { page, perPage } = getPageInfoFromSearchParams( searchParams, diff --git a/spiffworkflow-frontend/src/helpers.test.tsx b/spiffworkflow-frontend/src/helpers.test.tsx index 1031fc46b..5a0352b82 100644 --- a/spiffworkflow-frontend/src/helpers.test.tsx +++ b/spiffworkflow-frontend/src/helpers.test.tsx @@ -1,4 +1,4 @@ -import { convertSecondsToFormattedDate, slugifyString } from './helpers'; +import { convertSecondsToFormattedDateString, slugifyString } from './helpers'; test('it can slugify a string', () => { 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', () => { - const dateString = convertSecondsToFormattedDate(1666325400); + const dateString = convertSecondsToFormattedDateString(1666325400); expect(dateString).toEqual('2022-10-21'); }); diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index 37286c836..d163c00a3 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -42,6 +42,13 @@ export const convertDateToSeconds = ( return null; }; +export const convertDateObjectToFormattedString = (dateObject: Date) => { + if (dateObject) { + return format(dateObject, DATE_FORMAT); + } + return null; +}; + export const convertStringToDate = (dateString: string) => { if (dateString) { // add midnight time to the date so it c uses the correct date @@ -51,18 +58,25 @@ export const convertStringToDate = (dateString: string) => { return null; }; -export const convertSecondsToFormattedDateTime = (seconds: number) => { +export const convertSecondsToDateObject = (seconds: number) => { if (seconds) { - const dateObject = new Date(seconds * 1000); + return new Date(seconds * 1000); + } + return null; +}; + +export const convertSecondsToFormattedDateTime = (seconds: number) => { + const dateObject = convertSecondsToDateObject(seconds); + if (dateObject) { return format(dateObject, DATE_TIME_FORMAT); } return null; }; -export const convertSecondsToFormattedDate = (seconds: number) => { - if (seconds) { - const dateObject = new Date(seconds * 1000); - return format(dateObject, DATE_FORMAT); +export const convertSecondsToFormattedDateString = (seconds: number) => { + const dateObject = convertSecondsToDateObject(seconds); + if (dateObject) { + return convertDateObjectToFormattedString(dateObject); } return null; }; diff --git a/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx b/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx index 09d283167..3ead54625 100644 --- a/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx +++ b/spiffworkflow-frontend/src/routes/MessageInstanceList.tsx @@ -5,7 +5,7 @@ import { Link, useParams, useSearchParams } from 'react-router-dom'; import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import { - convertSecondsToFormattedDate, + convertSecondsToFormattedDateString, getPageInfoFromSearchParams, modifyProcessModelPath, unModifyProcessModelPath, @@ -68,7 +68,9 @@ export default function MessageInstanceList() { {rowToUse.failure_cause || '-'} {rowToUse.status} - {convertSecondsToFormattedDate(rowToUse.created_at_in_seconds)} + {convertSecondsToFormattedDateString( + rowToUse.created_at_in_seconds + )} ); diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx index 9f2d65b30..bb4468b44 100644 --- a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx +++ b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx @@ -6,7 +6,7 @@ import PaginationForTable from '../components/PaginationForTable'; import ProcessBreadcrumb from '../components/ProcessBreadcrumb'; import { getPageInfoFromSearchParams, - convertSecondsToFormattedDate, + convertSecondsToFormattedDateString, modifyProcessModelPath, unModifyProcessModelPath, } from '../helpers'; @@ -49,7 +49,7 @@ export default function ProcessInstanceLogList() { data-qa="process-instance-show-link" to={`/admin/process-models/${modifiedProcessModelId}/process-instances/${rowToUse.process_instance_id}/${rowToUse.spiff_step}`} > - {convertSecondsToFormattedDate(rowToUse.timestamp)} + {convertSecondsToFormattedDateString(rowToUse.timestamp)} From 82598a9e7ed28b66b1a0c7d8a876f79e2d42cd2c Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 17 Nov 2022 16:05:54 -0500 Subject: [PATCH 3/4] times on instance list table are working now w/ burnettk --- .../components/ProcessInstanceListTable.tsx | 201 +++++++++++------- spiffworkflow-frontend/src/config.tsx | 1 + spiffworkflow-frontend/src/helpers.tsx | 47 +++- 3 files changed, 165 insertions(+), 84 deletions(-) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 84c3fd62a..636481a42 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -21,18 +21,15 @@ import { TableHead, TableRow, TimePicker, - TimePickerSelect, - SelectItem, // @ts-ignore } from '@carbon/react'; import { PROCESS_STATUSES, DATE_FORMAT, DATE_FORMAT_CARBON } from '../config'; import { - convertDateObjectToFormattedString, - convertDateStringToSeconds, - convertDateToSeconds, - convertSecondsToDateObject, + convertDateAndTimeStringsToSeconds, + convertDateObjectToFormattedHoursMinutes, convertSecondsToFormattedDateString, convertSecondsToFormattedDateTime, + convertSecondsToFormattedTimeHoursMinutes, getPageInfoFromSearchParams, getProcessModelFullIdentifierFromSearchParams, modifyProcessModelPath, @@ -56,6 +53,10 @@ type OwnProps = { perPageOptions?: number[]; }; +interface dateParameters { + [key: string]: ((..._args: any[]) => any)[]; +} + export default function ProcessInstanceListTable({ filtersEnabled = true, processModelFullIdentifier, @@ -73,14 +74,20 @@ export default function ProcessInstanceListTable({ const oneHourInSeconds = 3600; const oneMonthInSeconds = oneHourInSeconds * 24 * 30; - const [startFrom, setStartFrom] = useState( - null - ); - const [startTo, setStartTo] = useState(null); - const [startFromString, setStartFromString] = useState(''); - const [endFrom, setEndFrom] = useState(null); - const [endTo, setEndTo] = useState(null); + const [startFromDate, setStartFromDate] = useState(''); + const [startToDate, setStartToDate] = useState(''); + const [endFromDate, setEndFromDate] = useState(''); + const [endToDate, setEndToDate] = useState(''); + const [startFromTime, setStartFromTime] = useState(''); + const [startToTime, setStartToTime] = useState(''); + const [endFromTime, setEndFromTime] = useState(''); + const [endToTime, setEndToTime] = useState(''); const [showFilterOptions, setShowFilterOptions] = useState(false); + const [startFromTimeInvalid, setStartFromTimeInvalid] = + useState(false); + const [startToTimeInvalid, setStartToTimeInvalid] = useState(false); + const [endFromTimeInvalid, setEndFromTimeInvalid] = useState(false); + const [endToTimeInvalid, setEndToTimeInvalid] = useState(false); const setErrorMessage = (useContext as any)(ErrorContext)[1]; @@ -96,14 +103,23 @@ export default function ProcessInstanceListTable({ const [processModelSelection, setProcessModelSelection] = useState(null); - const parametersToAlwaysFilterBy = useMemo(() => { + const dateParametersToAlwaysFilterBy: dateParameters = useMemo(() => { return { - start_from: setStartFrom, - start_to: setStartTo, - end_from: setEndFrom, - end_to: setEndTo, + start_from: [setStartFromDate, setStartFromTime], + start_to: [setStartToDate, setStartToTime], + end_from: [setEndFromDate, setEndFromTime], + end_to: [setEndToDate, setEndToTime], }; - }, [setStartFrom, setStartTo, setEndFrom, setEndTo]); + }, [ + setStartFromDate, + setStartFromTime, + setStartToDate, + setStartToTime, + setEndFromDate, + setEndFromTime, + setEndToDate, + setEndToTime, + ]); const parametersToGetFromSearchParams = useMemo(() => { return { @@ -140,20 +156,27 @@ export default function ProcessInstanceListTable({ queryParamString += `&user_filter=${userAppliedFilter}`; } - 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 dateObject = convertSecondsToDateObject( - searchParamValue as any - ); - console.log('dateObject1', dateObject); - functionToCall(dateObject); - setShowFilterOptions(true); + Object.keys(dateParametersToAlwaysFilterBy).forEach( + (paramName: string) => { + const dateFunctionToCall = + dateParametersToAlwaysFilterBy[paramName][0]; + const timeFunctionToCall = + dateParametersToAlwaysFilterBy[paramName][1]; + const searchParamValue = searchParams.get(paramName); + if (searchParamValue) { + queryParamString += `&${paramName}=${searchParamValue}`; + const dateString = convertSecondsToFormattedDateString( + searchParamValue as any + ); + dateFunctionToCall(dateString); + const timeString = convertSecondsToFormattedTimeHoursMinutes( + searchParamValue as any + ); + timeFunctionToCall(timeString); + setShowFilterOptions(true); + } } - }); + ); Object.keys(parametersToGetFromSearchParams).forEach( (paramName: string) => { @@ -167,10 +190,6 @@ export default function ProcessInstanceListTable({ const functionToCall = parametersToGetFromSearchParams[paramName]; queryParamString += `&${paramName}=${searchParams.get(paramName)}`; if (functionToCall !== null) { - console.log( - 'searchParams.get(paramName)', - searchParams.get(paramName) - ); functionToCall(searchParams.get(paramName) || ''); } setShowFilterOptions(true); @@ -226,7 +245,7 @@ export default function ProcessInstanceListTable({ params, oneMonthInSeconds, oneHourInSeconds, - parametersToAlwaysFilterBy, + dateParametersToAlwaysFilterBy, parametersToGetFromSearchParams, filtersEnabled, paginationQueryParamPrefix, @@ -261,10 +280,22 @@ export default function ProcessInstanceListTable({ ); let queryParamString = `per_page=${perPage}&page=${page}&user_filter=true`; - const startFromSeconds = convertDateToSeconds(startFrom); - const endFromSeconds = convertDateToSeconds(endFrom); - const startToSeconds = convertDateToSeconds(startTo); - const endToSeconds = convertDateToSeconds(endTo); + const startFromSeconds = convertDateAndTimeStringsToSeconds( + startFromDate, + startFromTime + ); + const startToSeconds = convertDateAndTimeStringsToSeconds( + startToDate, + startToTime + ); + const endFromSeconds = convertDateAndTimeStringsToSeconds( + endFromDate, + endFromTime + ); + const endToSeconds = convertDateAndTimeStringsToSeconds( + endToDate, + endToTime + ); if (isTrueComparison(startFromSeconds, '>', startToSeconds)) { setErrorMessage({ message: '"Start date from" cannot be after "start date to"', @@ -318,10 +349,12 @@ export default function ProcessInstanceListTable({ labelString: any, name: any, initialDate: any, - initialDateString: string, - onChangeFunction: any + initialTime: string, + onChangeDateFunction: any, + onChangeTimeFunction: any, + timeInvalid: boolean, + setTimeInvalid: any ) => { - // value={convertDateObjectToFormattedString(initialDate)} return ( <> @@ -334,29 +367,29 @@ export default function ProcessInstanceListTable({ autocomplete="off" allowInput={false} onChange={(dateChangeEvent: any) => { - let dateTime = new Date(); - if (initialDate) { - dateTime = new Date(initialDate.getTime()); + if (!initialDate && !initialTime) { + onChangeTimeFunction( + convertDateObjectToFormattedHoursMinutes(new Date()) + ); } - - const [year, month, day] = - dateChangeEvent.srcElement.value.split('-'); - dateTime.setDate(day); - dateTime.setMonth(month - 1); - // @ts-ignore setYear does exist on Date - dateTime.setYear(year); - onChangeFunction(dateTime); + onChangeDateFunction(dateChangeEvent.srcElement.value); }} - value={initialDateString} + value={initialDate} /> { - console.log('event', event); + if (event.srcElement.validity.valid) { + setTimeInvalid(false); + } else { + setTimeInvalid(true); + } + onChangeTimeFunction(event.srcElement.value); }} /> @@ -386,17 +419,20 @@ export default function ProcessInstanceListTable({ const clearFilters = () => { setProcessModelSelection(null); setProcessStatusSelection([]); - setStartFrom(null); - setStartTo(null); - setEndFrom(null); - setEndTo(null); + setStartFromDate(''); + setStartFromTime(''); + setStartToDate(''); + setStartToTime(''); + setEndFromDate(''); + setEndFromTime(''); + setEndToDate(''); + setEndToTime(''); }; const filterOptions = () => { if (!showFilterOptions) { return null; } - console.log('startFrom', startFrom); return ( <> @@ -416,36 +452,48 @@ export default function ProcessInstanceListTable({ {dateComponent( 'Start date from', 'start-from', - startFrom, - startFromString, - setStartFrom + startFromDate, + startFromTime, + setStartFromDate, + setStartFromTime, + startFromTimeInvalid, + setStartFromTimeInvalid )} {dateComponent( 'Start date to', 'start-to', - startTo, - startFromString, - setStartTo + startToDate, + startToTime, + setStartToDate, + setStartToTime, + startToTimeInvalid, + setStartToTimeInvalid )} {dateComponent( 'End date from', 'end-from', - endFrom, - startFromString, - setEndFrom + endFromDate, + endFromTime, + setEndFromDate, + setEndFromTime, + endFromTimeInvalid, + setEndFromTimeInvalid )} {dateComponent( 'End date to', 'end-to', - endTo, - startFromString, - setEndTo + endToDate, + endToTime, + setEndToDate, + setEndToTime, + endToTimeInvalid, + setEndToTimeInvalid )} @@ -592,9 +640,6 @@ export default function ProcessInstanceListTable({ }; if (pagination) { - setStartFromString( - convertDateObjectToFormattedString(startFrom as any) || '' - ); // eslint-disable-next-line prefer-const let { page, perPage } = getPageInfoFromSearchParams( searchParams, diff --git a/spiffworkflow-frontend/src/config.tsx b/spiffworkflow-frontend/src/config.tsx index 47ff5025a..5e7e96feb 100644 --- a/spiffworkflow-frontend/src/config.tsx +++ b/spiffworkflow-frontend/src/config.tsx @@ -18,5 +18,6 @@ export const PROCESS_STATUSES = [ // with time: 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_CARBON = 'Y-m-d'; diff --git a/spiffworkflow-frontend/src/helpers.tsx b/spiffworkflow-frontend/src/helpers.tsx index d163c00a3..964787906 100644 --- a/spiffworkflow-frontend/src/helpers.tsx +++ b/spiffworkflow-frontend/src/helpers.tsx @@ -1,5 +1,9 @@ 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 { DEFAULT_PER_PAGE, DEFAULT_PAGE, @@ -49,15 +53,31 @@ export const convertDateObjectToFormattedString = (dateObject: Date) => { return null; }; -export const convertStringToDate = (dateString: string) => { - if (dateString) { - // 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 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) => { + return convertDateAndTimeStringsToSeconds(dateString, '00:10:00'); +}; + export const convertSecondsToDateObject = (seconds: number) => { if (seconds) { return new Date(seconds * 1000); @@ -73,6 +93,21 @@ export const convertSecondsToFormattedDateTime = (seconds: number) => { return null; }; +export const convertDateObjectToFormattedHoursMinutes = (dateObject: Date) => { + if (dateObject) { + return format(dateObject, TIME_FORMAT_HOURS_MINUTES); + } + 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) { From ce74b07bc41255cc731ebff75a13f37312033fd0 Mon Sep 17 00:00:00 2001 From: jasquat Date: Thu, 17 Nov 2022 16:36:38 -0500 Subject: [PATCH 4/4] added back the useEffect for report filtering w/ burnettk --- .../components/ProcessInstanceListTable.tsx | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx index 636481a42..75de56c0a 100644 --- a/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx +++ b/spiffworkflow-frontend/src/components/ProcessInstanceListTable.tsx @@ -253,6 +253,54 @@ export default function ProcessInstanceListTable({ 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(() => { + const filters = processInstanceFilters as any; + Object.keys(dateParametersToAlwaysFilterBy).forEach((paramName: string) => { + const dateFunctionToCall = dateParametersToAlwaysFilterBy[paramName][0]; + const timeFunctionToCall = dateParametersToAlwaysFilterBy[paramName][1]; + const paramValue = filters[paramName]; + dateFunctionToCall(''); + timeFunctionToCall(''); + if (paramValue) { + const dateString = convertSecondsToFormattedDateString( + paramValue as any + ); + dateFunctionToCall(dateString); + const timeString = convertSecondsToFormattedTimeHoursMinutes( + paramValue as any + ); + timeFunctionToCall(timeString); + setShowFilterOptions(true); + } + }); + + setProcessModelSelection(null); + processModelAvailableItems.forEach((item: any) => { + if (item.id === filters.process_model_identifier) { + setProcessModelSelection(item); + } + }); + + const processStatusSelectedArray: string[] = []; + if (filters.process_status) { + PROCESS_STATUSES.forEach((processStatusOption: any) => { + const regex = new RegExp(`\\b${processStatusOption}\\b`); + if (filters.process_status.match(regex)) { + processStatusSelectedArray.push(processStatusOption); + } + }); + setShowFilterOptions(true); + } + setProcessStatusSelection(processStatusSelectedArray); + }, [ + processInstanceFilters, + dateParametersToAlwaysFilterBy, + parametersToGetFromSearchParams, + processModelAvailableItems, + ]); + // 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) => {