Skip to content

Commit 4d4c665

Browse files
authored
Merge pull request #697 from PROCEED-Labs/bpmn-editor-fixes
BPMN Editor & User Task Editor fixes
2 parents 9b634d7 + 4f12abd commit 4d4c665

File tree

19 files changed

+596
-84
lines changed

19 files changed

+596
-84
lines changed

src/management-system-v2/app/(dashboard)/[environmentId]/processes/[mode]/[processId]/modeler-toolbar.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,11 @@ const ModelerToolbar = ({
306306
);
307307
}}
308308
options={(isListView ? [] : [LATEST_VERSION])
309-
.concat(process.versions ?? [])
309+
.concat(
310+
(process.versions ?? []).sort((a, b) => {
311+
return b.createdOn.getTime() - a.createdOn.getTime();
312+
}),
313+
)
310314
.map(({ id, name }) => ({
311315
value: id,
312316
label: name,

src/management-system-v2/app/(dashboard)/[environmentId]/processes/[mode]/[processId]/planned-duration-input.tsx

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,13 @@ type PlannedDurationModalProperties = {
2828
durationValues: DurationValues;
2929
show: boolean;
3030
close: (values?: DurationValues) => void;
31+
title?: string;
3132
};
3233
export const PlannedDurationModal: React.FC<PlannedDurationModalProperties> = ({
3334
durationValues,
3435
show,
3536
close,
37+
title,
3638
}) => {
3739
const [form] = Form.useForm();
3840

@@ -64,7 +66,7 @@ export const PlannedDurationModal: React.FC<PlannedDurationModalProperties> = ({
6466

6567
return (
6668
<Modal
67-
title="Edit Planned Duration"
69+
title={title || 'Edit Planned Duration'}
6870
className={styles.PlannedDurationModal}
6971
width={getModalWidth()}
7072
style={{ maxWidth: '400px' }}
@@ -113,12 +115,14 @@ type PlannedDurationInputProperties = {
113115
timePlannedDuration: string;
114116
onChange: (changedTimePlannedDuration: string) => void;
115117
readOnly?: boolean;
118+
title?: string;
116119
};
117120

118121
const PlannedDurationInput: React.FC<PlannedDurationInputProperties> = ({
119122
timePlannedDuration,
120123
onChange,
121124
readOnly = false,
125+
title,
122126
}) => {
123127
const [isPlannedDurationModalOpen, setIsPlannedDurationModalOpen] = useState(false);
124128

@@ -167,6 +171,7 @@ const PlannedDurationInput: React.FC<PlannedDurationInputProperties> = ({
167171
<PlannedDurationModal
168172
durationValues={durationValues}
169173
show={isPlannedDurationModalOpen}
174+
title={title}
170175
close={(values) => {
171176
if (values) {
172177
const timeFormalExpression = calculateTimeFormalExpression(values);
@@ -216,9 +221,15 @@ export const TimerEventButton: React.FC<TimerEventButtonProps> = ({ element }) =
216221
}
217222
}, [element]);
218223

224+
// Determine title based on event type
225+
const isStartEvent = element && bpmnIs(element, 'bpmn:StartEvent');
226+
const title = isStartEvent
227+
? 'Edit Trigger Interval for Timer Start Event'
228+
: 'Edit Wait Duration of Timer Event';
229+
219230
return (
220231
<>
221-
<Tooltip title="Edit Wait Duration of Timer Event">
232+
<Tooltip title={title}>
222233
<Button
223234
icon={<ClockCircleOutlined />}
224235
onClick={() => setIsPlannedDurationModalOpen(true)}
@@ -227,6 +238,7 @@ export const TimerEventButton: React.FC<TimerEventButtonProps> = ({ element }) =
227238
<PlannedDurationModal
228239
durationValues={durationValues}
229240
show={isPlannedDurationModalOpen}
241+
title={title}
230242
close={async (values) => {
231243
if (modeler && element && values) {
232244
const modeling = modeler.getModeling();

src/management-system-v2/app/(dashboard)/[environmentId]/processes/[mode]/[processId]/properties-panel.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,7 @@ const PropertiesPanelContent: React.FC<PropertiesPanelContentProperties> = ({
373373
}}
374374
timePlannedDuration={timePlannedDuration || ''}
375375
readOnly={readOnly}
376+
title="Edit Planned Execution Duration"
376377
></PlannedDurationInput>
377378
)}
378379
</Space>

src/management-system-v2/app/(dashboard)/[environmentId]/processes/[mode]/[processId]/user-task-editor.tsx

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import HtmlFormEditor, { HtmlFormEditorRef } from '@/components/html-form-editor
22
import useEditorStateStore, {
33
EditorStoreProvider,
44
} from '@/components/html-form-editor/use-editor-state-store';
5-
import { Alert, App, Grid, Modal } from 'antd';
5+
import { Alert, App, Button, Grid, Modal } from 'antd';
66
import { forwardRef, useEffect, useMemo, useRef, useState } from 'react';
77

88
import { LuImage, LuMilestone } from 'react-icons/lu';
@@ -142,7 +142,7 @@ const UserTaskEditorModal: React.FC<UserTaskEditorModalProps> = ({ processId, op
142142
const editingEnabled = useCanEdit();
143143

144144
const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
145-
145+
const [modalKey, setModalKey] = useState(0);
146146
const app = App.useApp();
147147
const breakpoint = Grid.useBreakpoint();
148148

@@ -243,10 +243,31 @@ const UserTaskEditorModal: React.FC<UserTaskEditorModalProps> = ({ processId, op
243243
modalApi.confirm({
244244
title: 'You have unsaved changes!',
245245
content: 'Are you sure you want to close without saving?',
246+
okText: 'Discard Changes',
247+
okType: 'default',
248+
cancelText: 'Cancel',
246249
onOk: () => {
247250
setHasUnsavedChanges(false);
251+
// Increment key to force editor remount and discard changes
252+
setModalKey((prev) => prev + 1);
248253
onClose();
249254
},
255+
footer: (_, { OkBtn, CancelBtn }) => (
256+
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
257+
<CancelBtn />
258+
<OkBtn />
259+
260+
<Button
261+
type="primary"
262+
onClick={async () => {
263+
Modal.destroyAll();
264+
await handleSave();
265+
}}
266+
>
267+
Save
268+
</Button>
269+
</div>
270+
),
250271
});
251272
}
252273
};
@@ -275,7 +296,10 @@ const UserTaskEditorModal: React.FC<UserTaskEditorModalProps> = ({ processId, op
275296
let title = <div>Html Form</div>;
276297

277298
if (bpmnIs(affectedElement, 'bpmn:UserTask')) {
278-
title = <span>User Task Form</span>;
299+
const taskName = affectedElement?.businessObject.name
300+
? `: ${affectedElement?.businessObject.name}`
301+
: '';
302+
title = <span>User Task Form{taskName}</span>;
279303
} else if (bpmnIs(affectedElement, 'bpmn:Process')) {
280304
title = <span>Process Start Form</span>;
281305
}
@@ -310,7 +334,7 @@ const UserTaskEditorModal: React.FC<UserTaskEditorModalProps> = ({ processId, op
310334
onOk={handleSave}
311335
destroyOnHidden
312336
>
313-
<EditorStoreProvider>
337+
<EditorStoreProvider key={modalKey}>
314338
<UserTaskEditor json={json} onChange={() => setHasUnsavedChanges(true)} ref={builder} />
315339
</EditorStoreProvider>
316340
{modalElement}

src/management-system-v2/app/(dashboard)/[environmentId]/processes/[mode]/[processId]/user-task-image.tsx

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,13 @@ import { useEditor, useNode, UserComponent } from '@craftjs/core';
33
import { Button, Input, InputNumber, Select, Space, Upload } from 'antd';
44

55
import { useEffect, useRef, useState } from 'react';
6+
import { DeleteOutlined } from '@ant-design/icons';
67

78
import { useParams } from 'next/navigation';
89
import {
910
ContextMenu,
1011
Setting,
12+
useDeleteControl,
1113
VariableSelection,
1214
} from '@/components/html-form-editor/elements/utils';
1315
import { fallbackImage, useImageUpload } from '@/components/image-upload';
@@ -16,6 +18,7 @@ import { useFileManager } from '@/lib/useFileManager';
1618
import useEditorStateStore from '@/components/html-form-editor/use-editor-state-store';
1719

1820
import { tokenize } from '@proceed/user-task-helper/src/tokenize';
21+
import { DeleteButton } from '@/components/html-form-editor/DeleteButton';
1922

2023
type ImageProps = {
2124
src?: string;
@@ -53,7 +56,7 @@ export const EditImage: UserComponent<ImageProps> = ({ src, width, definitionId
5356
const imageRef = useRef<HTMLImageElement>(null);
5457

5558
const [showOverlay, setShowOverlay] = useState(false);
56-
59+
const { handleDelete } = useDeleteControl();
5760
const {
5861
connectors: { connect },
5962
actions: { setProp },
@@ -124,6 +127,7 @@ export const EditImage: UserComponent<ImageProps> = ({ src, width, definitionId
124127
onMouseOver={() => editingEnabled && setShowOverlay(true)}
125128
onMouseOut={() => editingEnabled && setShowOverlay(false)}
126129
>
130+
<DeleteButton show={editingEnabled && showOverlay} onClick={handleDelete} />
127131
<img
128132
ref={imageRef}
129133
style={{ width: width && `${width}%` }}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import React from 'react';
2+
import { DeleteOutlined } from '@ant-design/icons';
3+
4+
type DeleteButtonProps = {
5+
onClick: (e: React.MouseEvent) => void;
6+
show: boolean;
7+
};
8+
9+
export const DeleteButton: React.FC<DeleteButtonProps> = ({ onClick, show }) => {
10+
if (!show) return null;
11+
12+
return (
13+
<button className="container-element-delete-button" onClick={onClick}>
14+
<DeleteOutlined />
15+
</button>
16+
);
17+
};

src/management-system-v2/components/html-form-editor/Toolbar.tsx

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,23 @@
11
import React from 'react';
22

3-
import { Row, Button, Divider, Col, Space } from 'antd';
3+
import { Row, Button, Divider, Col, Space, Modal } from 'antd';
44

55
import {
66
DesktopOutlined,
77
MobileOutlined,
88
UndoOutlined,
99
RedoOutlined,
1010
DeleteOutlined,
11+
ReloadOutlined,
1112
} from '@ant-design/icons';
1213

1314
import styles from './index.module.scss';
1415

1516
import useEditorControls from './use-editor-controls';
1617
import useEditorStateStore from './use-editor-state-store';
17-
18+
import { useEditor } from '@craftjs/core';
19+
import { addDefaultElements } from '.';
20+
import { getDefaultStateSnapshot, extractStructure } from '.';
1821
export type EditorLayout = 'computer' | 'mobile';
1922

2023
type ToolbarProps = {
@@ -30,6 +33,36 @@ export const Toolbar: React.FC<ToolbarProps> = ({
3033
}) => {
3134
const { canUndo, canRedo, undo, redo, selected, deleteElement } = useEditorControls();
3235

36+
// Check if the changes have been made to default content to enable the button
37+
const { query, actions } = useEditor((state, query) => {
38+
// Return a trigger value that changes whenever nodes change
39+
return {
40+
nodeCount: Object.keys(state.nodes).length,
41+
};
42+
});
43+
44+
const isDefault = (() => {
45+
try {
46+
const currentState = query.serialize();
47+
const defaultStructure = getDefaultStateSnapshot();
48+
49+
if (!defaultStructure) {
50+
return false;
51+
}
52+
53+
// Extract current structure
54+
const currentStructure = extractStructure(JSON.parse(currentState));
55+
56+
// Deep compare structures
57+
const matches = JSON.stringify(currentStructure) === JSON.stringify(defaultStructure);
58+
59+
return matches;
60+
} catch (error) {
61+
console.error('Error checking isDefault:', error);
62+
return false;
63+
}
64+
})();
65+
3366
const editingEnabled = useEditorStateStore((state) => state.editingEnabled);
3467

3568
return (
@@ -79,11 +112,27 @@ export const Toolbar: React.FC<ToolbarProps> = ({
79112
<Divider type="vertical" />
80113
<Button
81114
danger
82-
disabled={!selected}
83115
type="text"
84-
icon={<DeleteOutlined />}
85-
onClick={async () => {
86-
selected && deleteElement(selected);
116+
icon={<ReloadOutlined />}
117+
disabled={isDefault}
118+
onClick={() => {
119+
Modal.confirm({
120+
title: 'Reset Form',
121+
content:
122+
'Are you sure you want to reset the form to the default template? This will replace all current elements.',
123+
okText: 'Reset',
124+
okType: 'danger',
125+
cancelText: 'Cancel',
126+
onOk: () => {
127+
// Reset to default form
128+
const rootNode = query.node('ROOT').get();
129+
rootNode.data.nodes.forEach((nodeId: string) => {
130+
actions.delete(nodeId);
131+
});
132+
// Add default elements back
133+
addDefaultElements(actions, query);
134+
},
135+
});
87136
}}
88137
/>
89138
</>

0 commit comments

Comments
 (0)