- {rowToUse.process_instance_id}
+ {row.process_instance_id}
|
- {rowToUse.process_model_display_name}
+ {row.process_model_display_name}
|
- {rowToUse.task_title}
+ {row.task_title}
|
- {showStartedBy ? {rowToUse.username} | : ''}
- {showWaitingOn ? {rowToUse.group_identifier || '-'} | : ''}
+ {showStartedBy ? {row.process_initiator_username} | : ''}
+ {showWaitingOn ? {getWaitingForTableCellComponent(row)} | : ''}
- {convertSecondsToFormattedDateTime(
- rowToUse.created_at_in_seconds
- ) || '-'}
+ {convertSecondsToFormattedDateTime(row.created_at_in_seconds) ||
+ '-'}
|
diff --git a/spiffworkflow-frontend/src/config.tsx b/spiffworkflow-frontend/src/config.tsx
index b0816a39d..abaadd5ef 100644
--- a/spiffworkflow-frontend/src/config.tsx
+++ b/spiffworkflow-frontend/src/config.tsx
@@ -1,11 +1,23 @@
-const host = window.location.hostname;
-let hostAndPort = `api.${host}`;
+const { port, hostname } = window.location;
+let hostAndPort = `api.${hostname}`;
let protocol = 'https';
-if (/^\d+\./.test(host) || host === 'localhost') {
- hostAndPort = `${host}:7000`;
+
+if (/^\d+\./.test(hostname) || hostname === 'localhost') {
+ let serverPort = 7000;
+ if (!Number.isNaN(Number(port))) {
+ serverPort = Number(port) - 1;
+ }
+ hostAndPort = `${hostname}:${serverPort}`;
protocol = 'http';
}
-export const BACKEND_BASE_URL = `${protocol}://${hostAndPort}/v1.0`;
+
+let url = `${protocol}://${hostAndPort}/v1.0`;
+// Allow overriding the backend base url with an environment variable at build time.
+if (process.env.REACT_APP_BACKEND_BASE_URL) {
+ url = process.env.REACT_APP_BACKEND_BASE_URL;
+}
+
+export const BACKEND_BASE_URL = url;
export const PROCESS_STATUSES = [
'not_started',
diff --git a/spiffworkflow-frontend/src/helpers.test.tsx b/spiffworkflow-frontend/src/helpers.test.tsx
index 5a0352b82..660f65f67 100644
--- a/spiffworkflow-frontend/src/helpers.test.tsx
+++ b/spiffworkflow-frontend/src/helpers.test.tsx
@@ -1,4 +1,8 @@
-import { convertSecondsToFormattedDateString, slugifyString } from './helpers';
+import {
+ convertSecondsToFormattedDateString,
+ slugifyString,
+ underscorizeString,
+} from './helpers';
test('it can slugify a string', () => {
expect(slugifyString('hello---world_ and then Some such-')).toEqual(
@@ -6,6 +10,12 @@ test('it can slugify a string', () => {
);
});
+test('it can underscorize a string', () => {
+ expect(underscorizeString('hello---world_ and then Some such-')).toEqual(
+ 'hello_world_and_then_some_such'
+ );
+});
+
test('it can keep the correct date when converting seconds to date', () => {
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 8f6255335..d91f05439 100644
--- a/spiffworkflow-frontend/src/helpers.tsx
+++ b/spiffworkflow-frontend/src/helpers.tsx
@@ -8,6 +8,7 @@ import {
DEFAULT_PER_PAGE,
DEFAULT_PAGE,
} from './components/PaginationForTable';
+import { ErrorForDisplay } from './interfaces';
// https://www.30secondsofcode.org/js/s/slugify
export const slugifyString = (str: any) => {
@@ -20,6 +21,10 @@ export const slugifyString = (str: any) => {
.replace(/-+$/g, '');
};
+export const underscorizeString = (inputString: string) => {
+ return slugifyString(inputString).replace(/-/g, '_');
+};
+
export const capitalizeFirstLetter = (string: any) => {
return string.charAt(0).toUpperCase() + string.slice(1);
};
@@ -234,3 +239,17 @@ export const getBpmnProcessIdentifiers = (rootBpmnElement: any) => {
childProcesses.push(rootBpmnElement.businessObject.id);
return childProcesses;
};
+
+// Setting the error message state to the same string is still considered a change
+// and re-renders the page so check the message first to avoid that.
+export const setErrorMessageSafely = (
+ newErrorMessageString: string,
+ oldErrorMessage: ErrorForDisplay,
+ errorMessageSetter: any
+) => {
+ if (oldErrorMessage && oldErrorMessage.message === newErrorMessageString) {
+ return null;
+ }
+ errorMessageSetter({ message: newErrorMessageString });
+ return null;
+};
diff --git a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx
index 574eb4e9e..f8e5f07f8 100644
--- a/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx
+++ b/spiffworkflow-frontend/src/hooks/UriListForPermissions.tsx
@@ -16,7 +16,10 @@ export const useUriListForPermissions = () => {
processInstanceReportListPath: '/v1.0/process-instances/reports',
processInstanceResumePath: `/v1.0/process-instance-resume/${params.process_model_id}/${params.process_instance_id}`,
processInstanceSuspendPath: `/v1.0/process-instance-suspend/${params.process_model_id}/${params.process_instance_id}`,
+ processInstanceResetPath: `/v1.0/process-instance-reset/${params.process_model_id}/${params.process_instance_id}`,
processInstanceTaskListDataPath: `/v1.0/task-data/${params.process_model_id}/${params.process_instance_id}`,
+ processInstanceSendEventPath: `/v1.0/send-event/${params.process_model_id}/${params.process_instance_id}`,
+ processInstanceCompleteTaskPath: `/v1.0/complete-task/${params.process_model_id}/${params.process_instance_id}`,
processInstanceTaskListPath: `/v1.0/process-instances/${params.process_model_id}/${params.process_instance_id}/task-info`,
processInstanceTaskListForMePath: `/v1.0/process-instances/for-me/${params.process_model_id}/${params.process_instance_id}/task-info`,
processInstanceTerminatePath: `/v1.0/process-instance-terminate/${params.process_model_id}/${params.process_instance_id}`,
diff --git a/spiffworkflow-frontend/src/index.css b/spiffworkflow-frontend/src/index.css
index 248a23d7d..08e8341cf 100644
--- a/spiffworkflow-frontend/src/index.css
+++ b/spiffworkflow-frontend/src/index.css
@@ -355,8 +355,8 @@ svg.notification-icon {
word-break: normal;
}
-.combo-box-in-modal {
- height: 300px;
+.vertical-spacer-to-allow-combo-box-to-expand-in-modal {
+ height: 250px;
}
.cds--btn.narrow-button {
diff --git a/spiffworkflow-frontend/src/interfaces.ts b/spiffworkflow-frontend/src/interfaces.ts
index 7805249be..630db8da1 100644
--- a/spiffworkflow-frontend/src/interfaces.ts
+++ b/spiffworkflow-frontend/src/interfaces.ts
@@ -1,3 +1,8 @@
+export interface User {
+ id: number;
+ username: string;
+}
+
export interface Secret {
id: number;
key: string;
@@ -5,6 +10,11 @@ export interface Secret {
creator_user_id: string;
}
+export interface ProcessData {
+ process_data_identifier: string;
+ process_data_value: any;
+}
+
export interface RecentProcessModel {
processGroupIdentifier?: string;
processModelIdentifier: string;
@@ -12,16 +22,23 @@ export interface RecentProcessModel {
}
export interface ProcessInstanceTask {
- id: string;
+ id: number;
+ task_id: string;
+ process_instance_id: number;
process_model_display_name: string;
process_model_identifier: string;
task_title: string;
lane_assignment_id: string;
- process_instance_status: number;
- updated_at_in_seconds: number;
+ process_instance_status: string;
state: string;
process_identifier: string;
name: string;
+ process_initiator_username: string;
+ assigned_user_group_identifier: string;
+ created_at_in_seconds: number;
+ updated_at_in_seconds: number;
+ current_user_is_potential_owner: number;
+ potential_owner_usernames: string;
}
export interface ProcessReference {
@@ -153,6 +170,10 @@ export type HotCrumbItem = HotCrumbItemArray | HotCrumbItemObject;
export interface ErrorForDisplay {
message: string;
sentry_link?: string;
+ task_name?: string;
+ task_id?: string;
+ line_number?: number;
+ file_name?: string;
}
export interface AuthenticationParam {
diff --git a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx
index d24c2b6e2..2d61439bf 100644
--- a/spiffworkflow-frontend/src/routes/AdminRoutes.tsx
+++ b/spiffworkflow-frontend/src/routes/AdminRoutes.tsx
@@ -22,14 +22,15 @@ import ProcessInstanceLogList from './ProcessInstanceLogList';
import MessageInstanceList from './MessageInstanceList';
import Configuration from './Configuration';
import JsonSchemaFormBuilder from './JsonSchemaFormBuilder';
+import ProcessModelNewExperimental from './ProcessModelNewExperimental';
export default function AdminRoutes() {
const location = useLocation();
- const setErrorMessage = (useContext as any)(ErrorContext)[1];
+ const setErrorObject = (useContext as any)(ErrorContext)[1];
useEffect(() => {
- setErrorMessage(null);
- }, [location, setErrorMessage]);
+ setErrorObject(null);
+ }, [location, setErrorObject]);
if (UserService.hasRole(['admin'])) {
return (
@@ -50,6 +51,10 @@ export default function AdminRoutes() {
path="process-models/:process_group_id/new"
element={}
/>
+ }
+ />
}
diff --git a/spiffworkflow-frontend/src/routes/AuthenticationList.tsx b/spiffworkflow-frontend/src/routes/AuthenticationList.tsx
index 4f320df4b..a0d151018 100644
--- a/spiffworkflow-frontend/src/routes/AuthenticationList.tsx
+++ b/spiffworkflow-frontend/src/routes/AuthenticationList.tsx
@@ -7,7 +7,7 @@ import HttpService from '../services/HttpService';
import UserService from '../services/UserService';
export default function AuthenticationList() {
- const setErrorMessage = (useContext as any)(ErrorContext)[1];
+ const setErrorObject = (useContext as any)(ErrorContext)[1];
const [authenticationList, setAuthenticationList] = useState<
AuthenticationItem[] | null
@@ -26,9 +26,9 @@ export default function AuthenticationList() {
HttpService.makeCallToBackend({
path: `/authentications`,
successCallback: processResult,
- failureCallback: setErrorMessage,
+ failureCallback: setErrorObject,
});
- }, [setErrorMessage]);
+ }, [setErrorObject]);
const buildTable = () => {
if (authenticationList) {
diff --git a/spiffworkflow-frontend/src/routes/Configuration.tsx b/spiffworkflow-frontend/src/routes/Configuration.tsx
index b2e30416d..bd9e59c50 100644
--- a/spiffworkflow-frontend/src/routes/Configuration.tsx
+++ b/spiffworkflow-frontend/src/routes/Configuration.tsx
@@ -14,7 +14,7 @@ import { usePermissionFetcher } from '../hooks/PermissionService';
export default function Configuration() {
const location = useLocation();
- const setErrorMessage = (useContext as any)(ErrorContext)[1];
+ const setErrorObject = (useContext as any)(ErrorContext)[1];
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const navigate = useNavigate();
@@ -26,13 +26,13 @@ export default function Configuration() {
const { ability } = usePermissionFetcher(permissionRequestData);
useEffect(() => {
- setErrorMessage(null);
+ setErrorObject(null);
let newSelectedTabIndex = 0;
if (location.pathname.match(/^\/admin\/configuration\/authentications\b/)) {
newSelectedTabIndex = 1;
}
setSelectedTabIndex(newSelectedTabIndex);
- }, [location, setErrorMessage]);
+ }, [location, setErrorObject]);
return (
<>
diff --git a/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx b/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx
index 872a7a69c..0475d4c75 100644
--- a/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx
+++ b/spiffworkflow-frontend/src/routes/HomePageRoutes.tsx
@@ -11,12 +11,12 @@ import CreateNewInstance from './CreateNewInstance';
export default function HomePageRoutes() {
const location = useLocation();
- const setErrorMessage = (useContext as any)(ErrorContext)[1];
+ const setErrorObject = (useContext as any)(ErrorContext)[1];
const [selectedTabIndex, setSelectedTabIndex] = useState(0);
const navigate = useNavigate();
useEffect(() => {
- setErrorMessage(null);
+ setErrorObject(null);
let newSelectedTabIndex = 0;
if (location.pathname.match(/^\/tasks\/completed-instances\b/)) {
newSelectedTabIndex = 1;
@@ -24,7 +24,7 @@ export default function HomePageRoutes() {
newSelectedTabIndex = 2;
}
setSelectedTabIndex(newSelectedTabIndex);
- }, [location, setErrorMessage]);
+ }, [location, setErrorObject]);
const renderTabs = () => {
if (location.pathname.match(/^\/tasks\/\d+\/\b/)) {
diff --git a/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx b/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx
index 6d1011014..d4a9c2b44 100644
--- a/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx
+++ b/spiffworkflow-frontend/src/routes/JsonSchemaFormBuilder.tsx
@@ -3,7 +3,11 @@ import { useEffect, useState } from 'react';
import { Button, Select, SelectItem, TextInput } from '@carbon/react';
import { useParams } from 'react-router-dom';
import { FormField } from '../interfaces';
-import { modifyProcessIdentifierForPathParam, slugifyString } from '../helpers';
+import {
+ modifyProcessIdentifierForPathParam,
+ slugifyString,
+ underscorizeString,
+} from '../helpers';
import HttpService from '../services/HttpService';
export default function JsonSchemaFormBuilder() {
@@ -75,7 +79,7 @@ export default function JsonSchemaFormBuilder() {
formFieldIdHasBeenUpdatedByUser
);
if (!formFieldIdHasBeenUpdatedByUser) {
- setFormFieldId(slugifyString(newFormFieldTitle));
+ setFormFieldId(underscorizeString(newFormFieldTitle));
}
setFormFieldTitle(newFormFieldTitle);
};
diff --git a/spiffworkflow-frontend/src/routes/MyTasks.tsx b/spiffworkflow-frontend/src/routes/MyTasks.tsx
index 4c1cbc9bf..3daaaef6a 100644
--- a/spiffworkflow-frontend/src/routes/MyTasks.tsx
+++ b/spiffworkflow-frontend/src/routes/MyTasks.tsx
@@ -2,6 +2,7 @@ import { useEffect, useState } from 'react';
// @ts-ignore
import { Button, Table } from '@carbon/react';
import { Link, useSearchParams } from 'react-router-dom';
+import { Notification } from '../components/Notification';
import PaginationForTable from '../components/PaginationForTable';
import {
getPageInfoFromSearchParams,
@@ -51,20 +52,19 @@ export default function MyTasks() {
const processInstanceRunResultTag = () => {
if (processInstance) {
return (
-
-
- Process Instance {processInstance.id} kicked off (
-
- view
-
- ).
-
-
+ setProcessInstance(null)}
+ >
+
+ view
+
+
);
}
return null;
diff --git a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx
index d9ceaf597..7dee4f203 100644
--- a/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessGroupList.tsx
@@ -39,7 +39,7 @@ export default function ProcessGroupList() {
};
// for search box
HttpService.makeCallToBackend({
- path: `/process-models?per_page=1000&recursive=true`,
+ path: `/process-models?per_page=1000&recursive=true&include_parent_groups=true`,
successCallback: processResultForProcessModels,
});
}, [searchParams]);
diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx
index 51c099815..a18f48c80 100644
--- a/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessInstanceList.tsx
@@ -67,6 +67,7 @@ export default function ProcessInstanceList({ variant }: OwnProps) {
{
navigate('/admin/process-instances/for-me');
}}
@@ -76,6 +77,7 @@ export default function ProcessInstanceList({ variant }: OwnProps) {
{
navigate('/admin/process-instances/all');
}}
diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx
index 37ef5519c..b4a4f683a 100644
--- a/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessInstanceLogList.tsx
@@ -45,7 +45,7 @@ export default function ProcessInstanceLogList() {
const rowToUse = row as any;
return (
- {rowToUse.id} |
+ {rowToUse.id} |
{rowToUse.message} |
{rowToUse.bpmn_task_name} |
{isDetailedView && (
@@ -114,6 +114,7 @@ export default function ProcessInstanceLogList() {
{
searchParams.set('detailed', 'false');
setSearchParams(searchParams);
@@ -123,6 +124,7 @@ export default function ProcessInstanceLogList() {
{
searchParams.set('detailed', 'true');
setSearchParams(searchParams);
diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx
index 906fb3142..b753d3074 100644
--- a/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessInstanceReportList.tsx
@@ -31,9 +31,7 @@ export default function ProcessInstanceReportList() {
return (
-
+
{rowToUse.identifier}
|
diff --git a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx
index e4e1ffa23..678ebdf2a 100644
--- a/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx
+++ b/spiffworkflow-frontend/src/routes/ProcessInstanceShow.tsx
@@ -25,6 +25,7 @@ import {
ButtonSet,
Tag,
Modal,
+ Dropdown,
Stack,
// @ts-ignore
} from '@carbon/react';
@@ -41,6 +42,7 @@ import ErrorContext from '../contexts/ErrorContext';
import { useUriListForPermissions } from '../hooks/UriListForPermissions';
import {
PermissionsToCheck,
+ ProcessData,
ProcessInstance,
ProcessInstanceTask,
} from '../interfaces';
@@ -62,9 +64,16 @@ export default function ProcessInstanceShow({ variant }: OwnProps) {
const [tasksCallHadError, setTasksCallHadError] = useState(false);
const [taskToDisplay, setTaskToDisplay] = useState |