ui-soxo-bootstrap-core 2.5.0 → 2.5.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/core/components/external-window/external-window.js +178 -167
- package/core/components/extra-info/extra-info-details.js +109 -126
- package/core/lib/components/sidemenu/sidemenu.js +193 -238
- package/core/lib/components/sidemenu/sidemenu.scss +39 -26
- package/core/lib/pages/profile/theme-config.js +1 -1
- package/core/modules/index.js +0 -4
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +53 -13
- package/core/modules/steps/action-buttons.js +29 -41
- package/core/modules/steps/action-buttons.scss +16 -0
- package/core/modules/steps/steps.js +71 -57
- package/core/modules/steps/steps.scss +4 -3
- package/package.json +1 -1
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
width: 17%;
|
|
44
44
|
background: #fff;
|
|
45
45
|
// border-bottom: 1.5px solid #24aeb8;
|
|
46
|
-
&.close{
|
|
47
|
-
width:6% !important
|
|
46
|
+
&.close {
|
|
47
|
+
width: 6% !important;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
.logo-wrapper {
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
|
|
71
71
|
@media only screen and (max-width: 500px) {
|
|
72
72
|
margin-right: 0px;
|
|
73
|
-
// margin: 10px;
|
|
73
|
+
// margin: 10px;
|
|
74
74
|
width: 150px;
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -160,7 +160,7 @@
|
|
|
160
160
|
|
|
161
161
|
/* Pseudo-element (as before) */
|
|
162
162
|
.menu-collapsed::after {
|
|
163
|
-
content:
|
|
163
|
+
content: '';
|
|
164
164
|
position: absolute;
|
|
165
165
|
top: -100%;
|
|
166
166
|
left: 0;
|
|
@@ -175,26 +175,26 @@
|
|
|
175
175
|
// background-color: #E0EBFF;
|
|
176
176
|
// transform: scale(0.95); /* Apply scale effect on hover */
|
|
177
177
|
}
|
|
178
|
-
/* Style selected item */
|
|
179
|
-
.ant-menu{
|
|
180
|
-
|
|
181
|
-
}
|
|
182
|
-
// .ant-menu-inline .ant-menu-item::after{
|
|
183
|
-
// border-right: none;
|
|
184
|
-
// }
|
|
185
|
-
// .ant-menu-item-selected {
|
|
186
|
-
// background-color: var(--selected-bg-color) !important;
|
|
187
|
-
// color: var(--selected-text-color) !important;
|
|
188
|
-
// border-radius: 12px;
|
|
189
|
-
// // font-weight: 600;
|
|
190
|
-
// // margin: 4px 8px;
|
|
191
|
-
// }
|
|
192
|
-
|
|
193
|
-
// /* Optional: remove hover effects */
|
|
194
|
-
// .ant-menu-item:hover {
|
|
195
|
-
// background-color: transparent !important;
|
|
196
|
-
// color: transparent !important;
|
|
197
|
-
// }
|
|
178
|
+
/* Style selected item */
|
|
179
|
+
.ant-menu {
|
|
180
|
+
background-color: transparent !important;
|
|
181
|
+
}
|
|
182
|
+
// .ant-menu-inline .ant-menu-item::after{
|
|
183
|
+
// border-right: none;
|
|
184
|
+
// }
|
|
185
|
+
// .ant-menu-item-selected {
|
|
186
|
+
// background-color: var(--selected-bg-color) !important;
|
|
187
|
+
// color: var(--selected-text-color) !important;
|
|
188
|
+
// border-radius: 12px;
|
|
189
|
+
// // font-weight: 600;
|
|
190
|
+
// // margin: 4px 8px;
|
|
191
|
+
// }
|
|
192
|
+
|
|
193
|
+
// /* Optional: remove hover effects */
|
|
194
|
+
// .ant-menu-item:hover {
|
|
195
|
+
// background-color: transparent !important;
|
|
196
|
+
// color: transparent !important;
|
|
197
|
+
// }
|
|
198
198
|
|
|
199
199
|
/* River flow effect */
|
|
200
200
|
.menu-collapsed:hover::after {
|
|
@@ -248,9 +248,7 @@
|
|
|
248
248
|
padding: 0px;
|
|
249
249
|
// width: 100% !important;
|
|
250
250
|
// padding: 10px 16px;
|
|
251
|
-
|
|
252
251
|
}
|
|
253
|
-
|
|
254
252
|
|
|
255
253
|
.menu-item {
|
|
256
254
|
width: 100% !important;
|
|
@@ -262,3 +260,18 @@
|
|
|
262
260
|
}
|
|
263
261
|
}
|
|
264
262
|
}
|
|
263
|
+
|
|
264
|
+
.ant-menu-item:hover {
|
|
265
|
+
background-color: #e6f7ff !important;
|
|
266
|
+
color: #1677ff !important;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/* Tooltip styling if needed */
|
|
270
|
+
.ant-tooltip-inner {
|
|
271
|
+
max-width: 200px;
|
|
272
|
+
white-space: nowrap;
|
|
273
|
+
overflow: hidden;
|
|
274
|
+
text-overflow: ellipsis;
|
|
275
|
+
background-color: #ffffff !important;
|
|
276
|
+
color: #000000 !important;
|
|
277
|
+
}
|
package/core/modules/index.js
CHANGED
|
@@ -16,8 +16,6 @@ import PopQueryDashboard from './dashboard/components/pop-query-dashboard/pop-qu
|
|
|
16
16
|
|
|
17
17
|
import HomePageAPI from './../pages/homepage-api/homepage-api';
|
|
18
18
|
|
|
19
|
-
import { Profile, ChangePassword } from './../lib';
|
|
20
|
-
|
|
21
19
|
import ReportingDashboard from '../modules/reporting/components/reporting-dashboard/reporting-dashboard';
|
|
22
20
|
|
|
23
21
|
import ChangeInfo from './Informations/change-info/change-info';
|
|
@@ -34,9 +32,7 @@ export {
|
|
|
34
32
|
DashboardCard,
|
|
35
33
|
PopQueryDashboard,
|
|
36
34
|
HomePageAPI,
|
|
37
|
-
Profile,
|
|
38
35
|
ReportingDashboard,
|
|
39
|
-
ChangePassword,
|
|
40
36
|
ChangeInfo,
|
|
41
37
|
};
|
|
42
38
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect, useContext, useRef } from 'react';
|
|
2
2
|
|
|
3
|
-
import { Table, Skeleton, Input, Tag, Modal, message, Pagination } from 'antd';
|
|
3
|
+
import { Table, Skeleton, Input, Tag, Modal, message, Pagination, Tooltip } from 'antd';
|
|
4
4
|
|
|
5
5
|
import { QrcodeOutlined } from '@ant-design/icons';
|
|
6
6
|
|
|
@@ -106,8 +106,17 @@ export default function ReportingDashboard({
|
|
|
106
106
|
setPatients([]);
|
|
107
107
|
const fetchId = idOverride || id;
|
|
108
108
|
await CoreScripts.getRecord({ id: fetchId, dbPtr }).then(async ({ result }) => {
|
|
109
|
+
// Check if display columns are provided from backend
|
|
110
|
+
if (result.display_columns) {
|
|
111
|
+
// Parse and set columns from stored JSON
|
|
112
|
+
|
|
113
|
+
setColumns(JSON.parse(result.display_columns));
|
|
114
|
+
} else {
|
|
115
|
+
// Reset columns if no display columns exist
|
|
116
|
+
|
|
117
|
+
setColumns([]);
|
|
118
|
+
}
|
|
109
119
|
await prepareInputParameters(result);
|
|
110
|
-
setColumns(JSON.parse(result.display_columns));
|
|
111
120
|
|
|
112
121
|
if (result.summary_columns) {
|
|
113
122
|
setSummaryColumns(JSON.parse(result.summary_columns));
|
|
@@ -160,11 +169,12 @@ export default function ReportingDashboard({
|
|
|
160
169
|
if (urlParams.script_id) scriptId.current = urlParams.script_id;
|
|
161
170
|
|
|
162
171
|
let otherDetails = record.other_details1 ? JSON.parse(record.other_details1) : null;
|
|
163
|
-
|
|
172
|
+
|
|
173
|
+
parameters = record.input_parameters ? JSON.parse(record.input_parameters) : null;
|
|
164
174
|
|
|
165
175
|
let formContent = {};
|
|
166
176
|
|
|
167
|
-
parameters = await parameters
|
|
177
|
+
parameters = await parameters?.map((record) => {
|
|
168
178
|
// Only if the url params does have a matching value ,
|
|
169
179
|
// we should not consider the default value
|
|
170
180
|
|
|
@@ -289,6 +299,17 @@ export default function ReportingDashboard({
|
|
|
289
299
|
}
|
|
290
300
|
// Update patients
|
|
291
301
|
setPatients(resultDetails || []);
|
|
302
|
+
// Check if columns are not yet defined
|
|
303
|
+
if (columns.length === 0 && resultDetails.length > 0) {
|
|
304
|
+
// Create columns dynamically from resultDetails keys
|
|
305
|
+
setColumns((prev) => {
|
|
306
|
+
if (prev.length > 0) return prev;
|
|
307
|
+
return Object.keys(resultDetails[0]).map((key) => ({
|
|
308
|
+
title: key,
|
|
309
|
+
field: key,
|
|
310
|
+
}));
|
|
311
|
+
});
|
|
312
|
+
}
|
|
292
313
|
|
|
293
314
|
if (result.length) {
|
|
294
315
|
// Set Pgination data into URL
|
|
@@ -565,7 +586,6 @@ function GuestList({
|
|
|
565
586
|
attributes,
|
|
566
587
|
fetchReportData,
|
|
567
588
|
}) {
|
|
568
|
-
console.log(attributes, '_____________________________________');
|
|
569
589
|
/**
|
|
570
590
|
* @param {*} propValues
|
|
571
591
|
*/
|
|
@@ -683,7 +703,7 @@ function GuestList({
|
|
|
683
703
|
redirectLink = redirectLink.replace(new RegExp('@' + replacement.field + ';', 'g'), record[replacement.field]);
|
|
684
704
|
});
|
|
685
705
|
|
|
686
|
-
return <Link to={`${redirectLink}`}>View</Link>;
|
|
706
|
+
return <Link to={`${redirectLink}`}>{entry.display_name_link ? entry.display_name_link : 'View'}</Link>;
|
|
687
707
|
} else if (entry.field === 'custom') {
|
|
688
708
|
// Make all the components in modules available for use in custom column of core script
|
|
689
709
|
// var genericComponents = require('./../../../../../../../nura-api-new/nura-desk/src/modules');
|
|
@@ -737,7 +757,7 @@ function GuestList({
|
|
|
737
757
|
|
|
738
758
|
// If the column type is 'span', render the field inside a <span> with inline color style
|
|
739
759
|
} else if (entry.columnType === 'span') {
|
|
740
|
-
return <span style={{ color: record.color_code }}>{record[entry.field]}</span>;
|
|
760
|
+
return <span style={{ color: record.color_code, overflowWrap: 'break-word', WebkitLineClamp: 3 }}>{record[entry.field]}</span>;
|
|
741
761
|
}
|
|
742
762
|
} else {
|
|
743
763
|
/**
|
|
@@ -769,14 +789,25 @@ function GuestList({
|
|
|
769
789
|
}
|
|
770
790
|
} else {
|
|
771
791
|
//If the value is neither 'Y' nor 'N', return the actual field value
|
|
772
|
-
return <span style={{ color: textColor }}>{record[entry.field]}</span>;
|
|
792
|
+
return <span style={{ color: textColor, whiteSpace: 'pre-wrap', overflowWrap: 'break-word' }}>{record[entry.field]}</span>;
|
|
773
793
|
}
|
|
774
794
|
}
|
|
775
795
|
}
|
|
776
796
|
},
|
|
777
797
|
field: entry.field,
|
|
778
|
-
title: entry.title,
|
|
798
|
+
// title: entry.title,
|
|
799
|
+
// title: (
|
|
800
|
+
// <Tooltip title={entry.title}>
|
|
801
|
+
// {entry.title}
|
|
802
|
+
// </Tooltip>
|
|
803
|
+
// ),
|
|
804
|
+
title: (
|
|
805
|
+
<Tooltip title={entry.tooltip || entry.title}>
|
|
806
|
+
<span>{entry.title}</span>
|
|
807
|
+
</Tooltip>
|
|
808
|
+
),
|
|
779
809
|
key: entry.field,
|
|
810
|
+
width: entry.width ? parseInt(entry.width) : 160,
|
|
780
811
|
fixed: entry.isFixedColumn ? entry.isFixedColumn : null, // Conditionally setting the 'fixed' key to 'left' if 'isColumnStatic' is true; otherwise, setting it to null.
|
|
781
812
|
// Check if filtering is enabled and patients is an array
|
|
782
813
|
filters:
|
|
@@ -800,7 +831,6 @@ function GuestList({
|
|
|
800
831
|
// Return the value from record.props if it exists
|
|
801
832
|
return description && record[description] ? record[description] : null;
|
|
802
833
|
}
|
|
803
|
-
|
|
804
834
|
return record[entry.field];
|
|
805
835
|
},
|
|
806
836
|
// Add align property based on column type
|
|
@@ -884,13 +914,21 @@ function GuestList({
|
|
|
884
914
|
// setData(data);
|
|
885
915
|
|
|
886
916
|
// Define export data
|
|
887
|
-
|
|
917
|
+
// Sanitize cols for export to ensure titles are strings
|
|
918
|
+
const exportCols = cols.map((col) => {
|
|
919
|
+
if (col.title && typeof col.title === 'object' && col.title.props) {
|
|
920
|
+
return { ...col, title: col.title.props.title };
|
|
921
|
+
}
|
|
922
|
+
return col;
|
|
923
|
+
});
|
|
924
|
+
|
|
925
|
+
let exportDatas = getExportData(patients, exportCols);
|
|
888
926
|
|
|
889
927
|
if (exportDatas.exportDataColumns.length && exportDatas.exportDataHeaders.length) {
|
|
890
928
|
setExportData({ exportDatas });
|
|
891
929
|
}
|
|
892
930
|
}
|
|
893
|
-
}, [patients]);
|
|
931
|
+
}, [patients, columns]);
|
|
894
932
|
|
|
895
933
|
let filtered;
|
|
896
934
|
|
|
@@ -1042,7 +1080,7 @@ function GuestList({
|
|
|
1042
1080
|
) : (
|
|
1043
1081
|
<TableComponent
|
|
1044
1082
|
size="small"
|
|
1045
|
-
scroll={{ x:
|
|
1083
|
+
scroll={{ x: 'max-content' }}
|
|
1046
1084
|
rowKey={(record) => record.OpNo}
|
|
1047
1085
|
dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
|
|
1048
1086
|
columns={cols}
|
|
@@ -1162,3 +1200,5 @@ function GuestList({
|
|
|
1162
1200
|
// </Card>
|
|
1163
1201
|
// );
|
|
1164
1202
|
// }
|
|
1203
|
+
// );
|
|
1204
|
+
// }
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
* Handles navigation and action controls for a multi-step process,
|
|
4
4
|
* including dynamic content rendering and process completion actions.
|
|
5
5
|
*/
|
|
6
|
-
import React from 'react';
|
|
6
|
+
import React, { useState } from 'react';
|
|
7
7
|
import { Skeleton } from 'antd';
|
|
8
8
|
import { Button } from '../../lib';
|
|
9
|
+
import './action-buttons.scss';
|
|
9
10
|
|
|
10
11
|
export default function ActionButtons({
|
|
11
12
|
loading,
|
|
@@ -21,63 +22,50 @@ export default function ActionButtons({
|
|
|
21
22
|
nextProcessId,
|
|
22
23
|
timelineCollapsed,
|
|
23
24
|
}) {
|
|
25
|
+
const [showNextProcess, setShowNextProcess] = useState(false);
|
|
26
|
+
|
|
24
27
|
return (
|
|
25
28
|
<>
|
|
26
29
|
<div style={{ minHeight: 300 }}>{loading ? <Skeleton active /> : renderDynamicComponent()}</div>
|
|
27
30
|
<>
|
|
28
|
-
<div
|
|
31
|
+
<div className="action-buttons-container">
|
|
29
32
|
{/* Back button */}
|
|
30
|
-
<Button disabled={activeStep === 0} onClick={handlePrevious}
|
|
33
|
+
<Button disabled={activeStep === 0} onClick={handlePrevious}>
|
|
31
34
|
Back
|
|
32
35
|
</Button>
|
|
33
36
|
|
|
34
37
|
{/* Skip button */}
|
|
35
38
|
{steps.length > 0 && steps[activeStep]?.allow_skip === 'Y' && (
|
|
36
|
-
<Button
|
|
37
|
-
type="default"
|
|
38
|
-
onClick={handleSkip}
|
|
39
|
-
style={{
|
|
40
|
-
borderRadius: 4,
|
|
41
|
-
}}
|
|
42
|
-
disabled={activeStep === steps.length - 1}
|
|
43
|
-
>
|
|
39
|
+
<Button type="default" onClick={handleSkip} disabled={activeStep === steps.length - 1}>
|
|
44
40
|
Skip
|
|
45
41
|
</Button>
|
|
46
42
|
)}
|
|
47
43
|
|
|
48
44
|
{/* Next / Finish / Start Next */}
|
|
49
45
|
{steps[activeStep]?.order_seqtype === 'E' ? (
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
onClick={
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
46
|
+
<>
|
|
47
|
+
{!showNextProcess && (
|
|
48
|
+
<Button
|
|
49
|
+
type="primary"
|
|
50
|
+
onClick={async () => {
|
|
51
|
+
const success = await handleFinish();
|
|
52
|
+
if (success && nextProcessId?.next_process_id) {
|
|
53
|
+
setShowNextProcess(true);
|
|
54
|
+
}
|
|
55
|
+
}}
|
|
56
|
+
>
|
|
57
|
+
Finish
|
|
58
|
+
</Button>
|
|
59
|
+
)}
|
|
60
|
+
|
|
61
|
+
{showNextProcess && nextProcessId?.next_process_id && (
|
|
62
|
+
<Button type="primary" onClick={handleStartNextProcess}>
|
|
63
|
+
Start {nextProcessId.next_process_name}
|
|
64
|
+
</Button>
|
|
65
|
+
)}
|
|
66
|
+
</>
|
|
71
67
|
) : (
|
|
72
|
-
<Button
|
|
73
|
-
type="primary"
|
|
74
|
-
// shape="round"
|
|
75
|
-
style={{
|
|
76
|
-
borderRadius: 4,
|
|
77
|
-
}}
|
|
78
|
-
disabled={activeStep === steps.length - 1 || !isStepCompleted}
|
|
79
|
-
onClick={handleNext}
|
|
80
|
-
>
|
|
68
|
+
<Button type="primary" disabled={activeStep === steps.length - 1 || !isStepCompleted} onClick={handleNext}>
|
|
81
69
|
Next →
|
|
82
70
|
</Button>
|
|
83
71
|
)}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
.action-buttons-container {
|
|
2
|
+
margin-top: 38px;
|
|
3
|
+
display: flex;
|
|
4
|
+
justify-content: flex-start;
|
|
5
|
+
gap: 10px;
|
|
6
|
+
position: sticky;
|
|
7
|
+
bottom: 0;
|
|
8
|
+
z-index: 1000;
|
|
9
|
+
background: #fff;
|
|
10
|
+
padding: 10px 0;
|
|
11
|
+
border-top: 1px solid #f0f0f0;
|
|
12
|
+
|
|
13
|
+
.ant-btn {
|
|
14
|
+
border-radius: 4px;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
@@ -20,22 +20,25 @@ import './steps.scss';
|
|
|
20
20
|
import TimelinePanel from './timeline';
|
|
21
21
|
import { ExternalWindow } from '../../components';
|
|
22
22
|
|
|
23
|
-
export default function ProcessStepsPage({
|
|
23
|
+
export default function ProcessStepsPage({ match, CustomComponents = {}, ...props }) {
|
|
24
24
|
const allComponents = { ...genericComponents, ...CustomComponents };
|
|
25
25
|
|
|
26
26
|
const [loading, setLoading] = useState(false);
|
|
27
27
|
const [steps, setSteps] = useState([]);
|
|
28
28
|
const [activeStep, setActiveStep] = useState(0);
|
|
29
29
|
const [isStepCompleted, setIsStepCompleted] = useState(false);
|
|
30
|
-
|
|
30
|
+
|
|
31
31
|
const [nextProcessId, setNextProcessId] = useState(null);
|
|
32
32
|
const [stepStartTime, setStepStartTime] = useState(null);
|
|
33
33
|
const [processStartTime, setProcessStartTime] = useState(null);
|
|
34
34
|
const [processTimings, setProcessTimings] = useState([]);
|
|
35
35
|
const [timelineCollapsed, setTimelineCollapsed] = useState(true);
|
|
36
36
|
const [showExternalWindow, setShowExternalWindow] = useState(false);
|
|
37
|
-
const
|
|
37
|
+
const [externalWin, setExternalWin] = useState(null);
|
|
38
38
|
|
|
39
|
+
const urlParams = Location.search();
|
|
40
|
+
let processId = urlParams.processId;
|
|
41
|
+
const [currentProcessId, setCurrentProcessId] = useState(processId);
|
|
39
42
|
// Load process details based on the current process ID
|
|
40
43
|
useEffect(() => {
|
|
41
44
|
loadProcess(currentProcessId);
|
|
@@ -204,7 +207,9 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
|
|
|
204
207
|
*/
|
|
205
208
|
const handleFinish = async () => {
|
|
206
209
|
const final = recordStepTime();
|
|
207
|
-
|
|
210
|
+
const success = await handleProcessSubmit(final);
|
|
211
|
+
if (success && !nextProcessId) props.history?.goBack();
|
|
212
|
+
return success;
|
|
208
213
|
};
|
|
209
214
|
/**
|
|
210
215
|
* Start Next Process
|
|
@@ -236,31 +241,34 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
|
|
|
236
241
|
|
|
237
242
|
return <Component {...step.config} {...props} step={step} params={urlParams} onStepComplete={() => setIsStepCompleted(true)} />;
|
|
238
243
|
};
|
|
239
|
-
|
|
240
|
-
* Keyboard Navigation
|
|
241
|
-
* - Enables left and right arrow keys for step navigation.
|
|
242
|
-
* - Prevents navigation beyond step boundaries.
|
|
243
|
-
* - Cleans up event listeners on unmount.
|
|
244
|
-
*/
|
|
244
|
+
|
|
245
245
|
useEffect(() => {
|
|
246
246
|
const handleKeyDown = (event) => {
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
if (activeStep > 0) {
|
|
250
|
-
handlePrevious();
|
|
251
|
-
}
|
|
247
|
+
if (event.key === 'ArrowLeft' && activeStep > 0) {
|
|
248
|
+
handlePrevious();
|
|
252
249
|
}
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
handleNext();
|
|
257
|
-
}
|
|
250
|
+
|
|
251
|
+
if (event.key === 'ArrowRight' && activeStep < steps.length - 1) {
|
|
252
|
+
handleNext();
|
|
258
253
|
}
|
|
259
254
|
};
|
|
260
255
|
|
|
261
|
-
window
|
|
262
|
-
|
|
263
|
-
|
|
256
|
+
// main window (document!)
|
|
257
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
258
|
+
|
|
259
|
+
// external window (document!)
|
|
260
|
+
if (externalWin && externalWin.document) {
|
|
261
|
+
externalWin.document.addEventListener('keydown', handleKeyDown);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return () => {
|
|
265
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
266
|
+
|
|
267
|
+
if (externalWin && externalWin.document) {
|
|
268
|
+
externalWin.document.removeEventListener('keydown', handleKeyDown);
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
}, [activeStep, steps, externalWin]);
|
|
264
272
|
|
|
265
273
|
/**
|
|
266
274
|
* Renders the main process UI including timeline, step details,
|
|
@@ -268,41 +276,43 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
|
|
|
268
276
|
* and external window view.
|
|
269
277
|
*/
|
|
270
278
|
const renderContent = () => (
|
|
271
|
-
<
|
|
272
|
-
<
|
|
273
|
-
<
|
|
274
|
-
<
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
279
|
+
<div>
|
|
280
|
+
<Card>
|
|
281
|
+
<Row gutter={20}>
|
|
282
|
+
<Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6}>
|
|
283
|
+
<TimelinePanel
|
|
284
|
+
loading={loading}
|
|
285
|
+
steps={steps}
|
|
286
|
+
activeStep={activeStep}
|
|
287
|
+
timelineCollapsed={timelineCollapsed}
|
|
288
|
+
handleTimelineClick={handleTimelineClick}
|
|
289
|
+
setTimelineCollapsed={setTimelineCollapsed}
|
|
290
|
+
/>
|
|
291
|
+
</Col>
|
|
283
292
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
293
|
+
<Col xs={24} sm={24} lg={timelineCollapsed ? 21 : 18}>
|
|
294
|
+
<div style={{ marginBottom: 20 }}>
|
|
295
|
+
<h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{steps[activeStep]?.step_name}</h2>
|
|
296
|
+
<p style={{ margin: 0, color: '#666' }}>{steps[activeStep]?.step_description}</p>
|
|
297
|
+
</div>
|
|
298
|
+
<ActionButtons
|
|
299
|
+
loading={loading}
|
|
300
|
+
steps={steps}
|
|
301
|
+
activeStep={activeStep}
|
|
302
|
+
isStepCompleted={isStepCompleted}
|
|
303
|
+
renderDynamicComponent={DynamicComponent}
|
|
304
|
+
handlePrevious={handlePrevious}
|
|
305
|
+
handleNext={handleNext}
|
|
306
|
+
handleSkip={handleSkip}
|
|
307
|
+
handleFinish={handleFinish}
|
|
308
|
+
handleStartNextProcess={handleStartNextProcess}
|
|
309
|
+
nextProcessId={nextProcessId}
|
|
310
|
+
timelineCollapsed={timelineCollapsed}
|
|
311
|
+
/>
|
|
312
|
+
</Col>
|
|
313
|
+
</Row>
|
|
314
|
+
</Card>
|
|
315
|
+
</div>
|
|
306
316
|
);
|
|
307
317
|
/**
|
|
308
318
|
* Renders content in both the main window and an external window
|
|
@@ -312,6 +322,10 @@ export default function ProcessStepsPage({ processId, match, CustomComponents =
|
|
|
312
322
|
return (
|
|
313
323
|
<>
|
|
314
324
|
<ExternalWindow
|
|
325
|
+
onWindowReady={(win) => {
|
|
326
|
+
setExternalWin(win);
|
|
327
|
+
win.focus();
|
|
328
|
+
}}
|
|
315
329
|
title={steps[activeStep]?.step_name || 'Process Step'}
|
|
316
330
|
onClose={() => setShowExternalWindow(false)}
|
|
317
331
|
// left={window.screenX + window.outerWidth}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
.timeline-card .ant-card-body {
|
|
2
|
-
padding: 20px;
|
|
2
|
+
// padding: 20px;
|
|
3
|
+
border: none;
|
|
3
4
|
min-height: 400px;
|
|
4
|
-
position: fixed; /* For positioning the arrow */
|
|
5
|
+
// position: fixed; /* For positioning the arrow */
|
|
5
6
|
}
|
|
6
7
|
|
|
7
8
|
.timeline-sidebar {
|
|
@@ -46,7 +47,7 @@
|
|
|
46
47
|
|
|
47
48
|
.vertical-line {
|
|
48
49
|
width: 2px;
|
|
49
|
-
height:
|
|
50
|
+
height: 20px;
|
|
50
51
|
background: #d9d9d9;
|
|
51
52
|
}
|
|
52
53
|
|