Skip to content

Commit 3d07f56

Browse files
authored
Merge pull request #683 from PROCEED-Labs/dynamic-task-list
MS: Dynamic task data updates
2 parents 4d4c665 + 045c334 commit 3d07f56

File tree

24 files changed

+576
-349
lines changed

24 files changed

+576
-349
lines changed

src/engine/universal/system/src/data.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ class Data extends System {
213213
// generate a unique name to prevent file name collisions
214214
let name = v4();
215215
if (fileName.includes('.')) {
216-
const fileType = fileName.split('.').pop();
216+
const fileType = fileName.split('.').slice(1).join('.');
217217
name += '.' + fileType;
218218
}
219219

src/engine/universal/ui/src/__tests__/__snapshots__/module.test.js.snap

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,20 @@ exports[`UI adds a display item to the SPA (HTTP) 1`] = `
6464
xhr.setRequestHeader('Content-Type', 'application/json');
6565
xhr.send(JSON.stringify(body));
6666
});
67+
},
68+
submit: async (path, body, query) => {
69+
validateEndpointArgs('post', path, body, query);
70+
return new Promise(async (resolve, reject) => {
71+
const xhr = new XMLHttpRequest();
72+
xhr.addEventListener('loadend', (req) => {resolve(JSON.parse(req.target.responseText)); });
73+
const url = query ? path + '?' + Object.entries(query).map(([key, value]) => key + '=' + encodeURIComponent(value)).join('&') : path;
74+
xhr.open('POST', url, true);
75+
xhr.setRequestHeader('Content-Type', 'application/json');
76+
77+
const buffer = await body.arrayBuffer();
78+
79+
xhr.send(JSON.stringify(Array.from(new Uint8Array(buffer))));
80+
});
6781
}
6882
};
6983
</script>

src/engine/universal/ui/src/ui.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,20 @@ const ui = {
221221
xhr.setRequestHeader('Content-Type', 'application/json');
222222
xhr.send(JSON.stringify(body));
223223
});
224+
},
225+
submit: async (path, body, query) => {
226+
${validateEndpointArgs.name}('post', path, body, query);
227+
return new Promise(async (resolve, reject) => {
228+
const xhr = new XMLHttpRequest();
229+
xhr.addEventListener('loadend', (req) => {resolve(JSON.parse(req.target.responseText)); });
230+
const url = query ? path + '?' + Object.entries(query).map(([key, value]) => key + '=' + encodeURIComponent(value)).join('&') : path;
231+
xhr.open('POST', url, true);
232+
xhr.setRequestHeader('Content-Type', 'application/json');
233+
234+
const buffer = await body.arrayBuffer();
235+
236+
xhr.send(JSON.stringify(Array.from(new Uint8Array(buffer))));
237+
});
224238
}
225239
};
226240
</script>

src/helper-modules/user-task-helper/index.js

Lines changed: 55 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,9 @@ const script = `
142142
143143
const variableDefinitions = {%variableDefinitions%};
144144
145+
let submitting = false;
146+
const variableSubmitTimeouts = {};
147+
145148
function getValueFromCheckbox(checkbox) {
146149
if (!checkbox.defaultValue) {
147150
return !!checkbox.checked;
@@ -251,10 +254,9 @@ const script = `
251254
// if the value set for a variable is a file => upload the file and replace the variable
252255
// with a reference to the stored file
253256
if (value instanceof File) {
254-
const buffer = await value.arrayBuffer();
255-
const { path } = await window.PROCEED_DATA.post(
257+
const { path } = await window.PROCEED_DATA.submit(
256258
'/tasklist/api/variable-file',
257-
Array.from(new Uint8Array(buffer)),
259+
value,
258260
{
259261
instanceID,
260262
userTaskID,
@@ -266,14 +268,17 @@ const script = `
266268
}
267269
}
268270
271+
submitting = true;
272+
Object.values(variableSubmitTimeouts).forEach(timeout => clearTimeout(timeout));
269273
window.PROCEED_DATA.put('/tasklist/api/variable', variables, {
270274
instanceID,
271275
userTaskID,
272-
}).then(() => {
273-
window.PROCEED_DATA.post('/tasklist/api/userTask', variables, {
276+
}).then(async () => {
277+
await window.PROCEED_DATA.post('/tasklist/api/userTask', variables, {
274278
instanceID,
275279
userTaskID,
276280
});
281+
submitting = false;
277282
});
278283
}
279284
@@ -312,22 +317,24 @@ const script = `
312317
milestoneInput.nextElementSibling.value = milestoneInput.value + '%'
313318
});
314319
315-
milestoneInput.addEventListener('click', (event) => {
316-
const milestoneName = Array.from(event.target.classList)
317-
.find((className) => className.includes('milestone-'))
318-
.split('milestone-')
319-
.slice(1)
320-
.join('');
321-
322-
window.PROCEED_DATA.put(
323-
'/tasklist/api/milestone',
324-
{ [milestoneName]: parseInt(event.target.value) },
325-
{
326-
instanceID,
327-
userTaskID,
328-
}
329-
);
330-
});
320+
if (!submitting) {
321+
milestoneInput.addEventListener('click', (event) => {
322+
const milestoneName = Array.from(event.target.classList)
323+
.find((className) => className.includes('milestone-'))
324+
.split('milestone-')
325+
.slice(1)
326+
.join('');
327+
328+
window.PROCEED_DATA.put(
329+
'/tasklist/api/milestone',
330+
{ [milestoneName]: parseInt(event.target.value) },
331+
{
332+
instanceID,
333+
userTaskID,
334+
}
335+
);
336+
});
337+
}
331338
});
332339
333340
// get all input(-group)s that can be used to set the value of an input
@@ -358,31 +365,34 @@ const script = `
358365
// cancel pending updates
359366
clearTimeout(variableInputTimer);
360367
361-
if (event.target.type === 'file') {
362-
updateUploadInfo(event.target);
363-
return;
364-
}
365-
366-
// trigger a timeout for an update to commit
367-
variableInputTimer = setTimeout(() => {
368-
try {
369-
const value = getValueFromVariableElement(variableName, variableInput);
370-
371-
validateValue(variableName, value);
372-
updateValidationErrorMessage(variableName);
373-
374-
window.PROCEED_DATA.put(
375-
'/tasklist/api/variable',
376-
{ [variableName]: value },
377-
{
378-
instanceID,
379-
userTaskID,
380-
}
381-
);
382-
} catch (err) {
383-
updateValidationErrorMessage(variableName, err.message);
368+
if (!submitting) {
369+
if (event.target.type === 'file') {
370+
updateUploadInfo(event.target);
371+
return;
384372
}
385-
}, 2000)
373+
374+
// trigger a timeout for an update to commit
375+
variableInputTimer = setTimeout(() => {
376+
try {
377+
const value = getValueFromVariableElement(variableName, variableInput);
378+
379+
validateValue(variableName, value);
380+
updateValidationErrorMessage(variableName);
381+
382+
window.PROCEED_DATA.put(
383+
'/tasklist/api/variable',
384+
{ [variableName]: value },
385+
{
386+
instanceID,
387+
userTaskID,
388+
}
389+
);
390+
} catch (err) {
391+
updateValidationErrorMessage(variableName, err.message);
392+
}
393+
}, 2000)
394+
variableSubmitTimeouts[variableName] = variableInputTimer;
395+
}
386396
});
387397
});
388398
});

src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions-dashboard/use-deployments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import { useCallback } from 'react';
77
function useDeployments(entries?: string) {
88
const space = useEnvironment();
99

10-
const { data: engines } = useEngines();
10+
const { data: engines } = useEngines(space);
1111

1212
const queryFn = useCallback(async () => {
1313
if (engines) {

src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/process-deployment-view.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ export default function ProcessDeploymentView({
363363

364364
<StartFormModal
365365
html={startForm}
366-
onSubmit={(submitVariables) => {
366+
onSubmit={async (submitVariables) => {
367367
const versionId = getLatestDeployment(deploymentInfo).versionId;
368368

369369
const mappedVariables: Record<string, { value: any }> = {};
@@ -374,7 +374,7 @@ export default function ProcessDeploymentView({
374374
);
375375

376376
// start the instance with the initial variable values from the start form
377-
wrapServerCall({
377+
await wrapServerCall({
378378
fn: () => startInstance(versionId, mappedVariables),
379379

380380
onSuccess: async (instanceId) => {

src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/[processId]/start-form-modal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { UserTaskForm } from '../../../tasklist/user-task-view';
33

44
type StartFormModalProps = {
55
html?: string;
6-
onSubmit: (variables: { [key: string]: any }) => void;
6+
onSubmit: (variables: { [key: string]: any }) => Promise<void>;
77
onCancel: () => void;
88
};
99

src/management-system-v2/app/(dashboard)/[environmentId]/(automation)/executions/deployment-hook.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ const mergeDeployment = (
9090
function useDeployment(definitionId: string, initialData?: DeployedProcessInfo) {
9191
const space = useEnvironment();
9292

93-
const { data: engines } = useEngines({
93+
const { data: engines } = useEngines(space, {
9494
key: [definitionId],
9595
fn: async (engine) => {
9696
const deployments = await getDeployments([engine]);

src/management-system-v2/app/(dashboard)/[environmentId]/layout-client.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client';
22

33
import styles from './layout.module.scss';
4-
import { FC, PropsWithChildren, createContext, use, useEffect, useState } from 'react';
4+
import { FC, PropsWithChildren, createContext, useEffect, useState } from 'react';
55
import {
66
Alert,
77
Layout as AntLayout,
@@ -131,8 +131,8 @@ const Layout: FC<
131131
check(layoutMenuItems);
132132

133133
setSelected(sel);
134-
setOpen(op);
135-
}, [pathname]);
134+
if (!collapsed) setOpen(op);
135+
}, [pathname, collapsed]);
136136

137137
// remove the error message about the unknown item entries that is logged by ant-design
138138
function toMenuItem(item: ExtendedMenuItem): MenuItemType {

src/management-system-v2/app/(dashboard)/[environmentId]/layout.tsx

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ import { customLinkIcons } from '@/lib/custom-links/icons';
4242
import { CustomNavigationLink } from '@/lib/custom-links/custom-link';
4343
import { env } from '@/lib/ms-config/env-vars';
4444
import { getUserPassword } from '@/lib/data/db/iam/users';
45+
import ActiveTasksBadge from '@/components/active-tasks-badge';
4546

4647
const DashboardLayout = async (
4748
props: PropsWithChildren<{ params: Promise<{ environmentId: string }> }>,
@@ -191,14 +192,30 @@ const DashboardLayout = async (
191192
});
192193
}
193194

195+
let pollingInterval = 10000;
196+
197+
if (Number.isInteger(automationSettings.taskPollingInterval)) {
198+
pollingInterval = automationSettings.taskPollingInterval;
199+
}
200+
194201
layoutMenuItems.push({
195202
key: 'tasklist',
196203
label: (
197204
<Link style={{ color: 'inherit' }} href={spaceURL(activeEnvironment, `/tasklist`)}>
198205
My Tasks
206+
<ActiveTasksBadge activeSpace={activeEnvironment} pollingInterval={pollingInterval} />
207+
</Link>
208+
),
209+
icon: (
210+
<Link href={spaceURL(activeEnvironment, `/tasklist`)}>
211+
<CheckSquareOutlined />
212+
<ActiveTasksBadge
213+
activeSpace={activeEnvironment}
214+
onIcon
215+
pollingInterval={pollingInterval}
216+
/>
199217
</Link>
200218
),
201-
icon: <CheckSquareOutlined />,
202219
selectedRegex: '/tasklist($|/)',
203220
openRegex: childRegex,
204221
children: children.length ? children : undefined,

0 commit comments

Comments
 (0)