ui-soxo-bootstrap-core 2.4.25-dev.33 → 2.4.25-dev.35
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.
|
@@ -18,64 +18,69 @@ import { message } from 'antd';
|
|
|
18
18
|
* @param {Function} onMaximize - Callback when window is maximized
|
|
19
19
|
*/
|
|
20
20
|
export function ExternalWindow({
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
21
|
+
children,
|
|
22
|
+
onClose,
|
|
23
|
+
onWindowReady,
|
|
24
|
+
title = 'New Window',
|
|
25
|
+
width = 600,
|
|
26
|
+
height = 400,
|
|
27
|
+
left,
|
|
28
|
+
top,
|
|
29
|
+
copyStyles = true,
|
|
30
|
+
centerScreen = false,
|
|
31
|
+
shortcuts = {},
|
|
32
|
+
onMinimize,
|
|
33
|
+
onMaximize,
|
|
33
34
|
}) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
35
|
+
const [container, setContainer] = useState(null);
|
|
36
|
+
const windowRef = useRef(null);
|
|
37
|
+
|
|
38
|
+
// Default shortcuts
|
|
39
|
+
const defaultShortcuts = useMemo(
|
|
40
|
+
() => ({
|
|
41
|
+
close: 'Escape',
|
|
42
|
+
focus: 'Ctrl+Shift+F',
|
|
43
|
+
...shortcuts,
|
|
44
|
+
}),
|
|
45
|
+
[shortcuts]
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
// Calculate window position
|
|
49
|
+
const { posX, posY } = useMemo(() => {
|
|
50
|
+
let posX = left;
|
|
51
|
+
let posY = top;
|
|
52
|
+
|
|
53
|
+
if (centerScreen) {
|
|
54
|
+
const screenWidth = window.screen.availWidth;
|
|
55
|
+
const screenHeight = window.screen.availHeight;
|
|
56
|
+
|
|
57
|
+
if (centerScreen === 'both' || centerScreen === 'horizontal') {
|
|
58
|
+
posX = (screenWidth - width) / 2;
|
|
59
|
+
}
|
|
60
|
+
if (centerScreen === 'both' || centerScreen === 'vertical') {
|
|
61
|
+
posY = (screenHeight - height) / 2;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return {
|
|
66
|
+
posX: posX ?? 200,
|
|
67
|
+
posY: posY ?? 200,
|
|
68
|
+
};
|
|
69
|
+
}, [left, top, width, height, centerScreen]);
|
|
70
|
+
|
|
71
|
+
const windowFeatures = useMemo(
|
|
72
|
+
() => `width=${width},height=${height},left=${posX},top=${posY},resizable=yes,scrollbars=yes`,
|
|
73
|
+
[width, height, posX, posY]
|
|
74
|
+
);
|
|
75
|
+
|
|
76
|
+
// Initialize window document
|
|
77
|
+
const initializeWindow = useCallback(
|
|
78
|
+
(win) => {
|
|
79
|
+
const doc = win.document;
|
|
80
|
+
|
|
81
|
+
// Write minimal HTML structure (like your working example)
|
|
82
|
+
doc.open();
|
|
83
|
+
doc.write(`
|
|
79
84
|
<!DOCTYPE html>
|
|
80
85
|
<html>
|
|
81
86
|
<head>
|
|
@@ -112,114 +117,120 @@ export function ExternalWindow({
|
|
|
112
117
|
</body>
|
|
113
118
|
</html>
|
|
114
119
|
`);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
},
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
// Keyboard shortcut handler
|
|
152
|
-
const createKeyDownHandler = useCallback((win) => (e) => {
|
|
153
|
-
const key = [
|
|
154
|
-
e.ctrlKey && 'Ctrl',
|
|
155
|
-
e.shiftKey && 'Shift',
|
|
156
|
-
e.altKey && 'Alt',
|
|
157
|
-
e.metaKey && 'Meta',
|
|
158
|
-
e.key
|
|
159
|
-
].filter(Boolean).join('+');
|
|
160
|
-
|
|
161
|
-
if (defaultShortcuts.close && key === defaultShortcuts.close) {
|
|
162
|
-
e.preventDefault();
|
|
163
|
-
onClose();
|
|
164
|
-
} else if (defaultShortcuts.focus && key === defaultShortcuts.focus) {
|
|
165
|
-
e.preventDefault();
|
|
166
|
-
win.focus();
|
|
167
|
-
} else if (defaultShortcuts.minimize && key === defaultShortcuts.minimize) {
|
|
168
|
-
e.preventDefault();
|
|
169
|
-
win.blur();
|
|
170
|
-
onMinimize?.();
|
|
171
|
-
} else if (defaultShortcuts.maximize && key === defaultShortcuts.maximize) {
|
|
172
|
-
e.preventDefault();
|
|
173
|
-
win.moveTo(0, 0);
|
|
174
|
-
win.resizeTo(screen.availWidth, screen.availHeight);
|
|
175
|
-
onMaximize?.();
|
|
120
|
+
doc.close();
|
|
121
|
+
|
|
122
|
+
return doc.getElementById('root');
|
|
123
|
+
},
|
|
124
|
+
[title]
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// Copy styles from parent window
|
|
128
|
+
const copyStylesToWindow = useCallback(
|
|
129
|
+
(targetWindow) => {
|
|
130
|
+
if (!copyStyles) return;
|
|
131
|
+
|
|
132
|
+
const fragment = targetWindow.document.createDocumentFragment();
|
|
133
|
+
|
|
134
|
+
// Copy all stylesheets from parent
|
|
135
|
+
Array.from(document.styleSheets).forEach((sheet) => {
|
|
136
|
+
try {
|
|
137
|
+
if (sheet.href) {
|
|
138
|
+
// External stylesheet
|
|
139
|
+
const link = targetWindow.document.createElement('link');
|
|
140
|
+
link.rel = 'stylesheet';
|
|
141
|
+
link.href = sheet.href;
|
|
142
|
+
fragment.appendChild(link);
|
|
143
|
+
} else if (sheet.cssRules) {
|
|
144
|
+
// Inline stylesheet
|
|
145
|
+
const style = targetWindow.document.createElement('style');
|
|
146
|
+
const cssText = Array.from(sheet.cssRules)
|
|
147
|
+
.map((r) => r.cssText)
|
|
148
|
+
.join('\n');
|
|
149
|
+
style.textContent = cssText;
|
|
150
|
+
fragment.appendChild(style);
|
|
151
|
+
}
|
|
152
|
+
} catch (e) {
|
|
153
|
+
// Silently ignore cross-origin stylesheet errors
|
|
154
|
+
console.warn('Could not copy stylesheet:', e);
|
|
176
155
|
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
targetWindow.document.head.appendChild(fragment);
|
|
159
|
+
},
|
|
160
|
+
[copyStyles]
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
// Keyboard shortcut handler
|
|
164
|
+
const createKeyDownHandler = useCallback(
|
|
165
|
+
(win) => (e) => {
|
|
166
|
+
const key = [e.ctrlKey && 'Ctrl', e.shiftKey && 'Shift', e.altKey && 'Alt', e.metaKey && 'Meta', e.key].filter(Boolean).join('+');
|
|
167
|
+
|
|
168
|
+
if (defaultShortcuts.close && key === defaultShortcuts.close) {
|
|
169
|
+
e.preventDefault();
|
|
170
|
+
onClose();
|
|
171
|
+
} else if (defaultShortcuts.focus && key === defaultShortcuts.focus) {
|
|
172
|
+
e.preventDefault();
|
|
173
|
+
win.focus();
|
|
174
|
+
} else if (defaultShortcuts.minimize && key === defaultShortcuts.minimize) {
|
|
175
|
+
e.preventDefault();
|
|
176
|
+
win.blur();
|
|
177
|
+
onMinimize?.();
|
|
178
|
+
} else if (defaultShortcuts.maximize && key === defaultShortcuts.maximize) {
|
|
179
|
+
e.preventDefault();
|
|
180
|
+
win.moveTo(0, 0);
|
|
181
|
+
win.resizeTo(screen.availWidth, screen.availHeight);
|
|
182
|
+
onMaximize?.();
|
|
183
|
+
}
|
|
184
|
+
},
|
|
185
|
+
[defaultShortcuts, onClose, onMinimize, onMaximize]
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
useEffect(() => {
|
|
189
|
+
const win = window.open('', '', windowFeatures);
|
|
190
|
+
|
|
191
|
+
if (!win) {
|
|
192
|
+
message.error('Please allow popups for this site');
|
|
193
|
+
onClose();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
// 🔥 GIVE PARENT ACCESS + FOCUS
|
|
197
|
+
onWindowReady?.(win);
|
|
198
|
+
|
|
199
|
+
windowRef.current = win;
|
|
200
|
+
|
|
201
|
+
// Initialize window document structure
|
|
202
|
+
const root = initializeWindow(win);
|
|
203
|
+
|
|
204
|
+
// Copy styles from parent (async to not block)
|
|
205
|
+
requestAnimationFrame(() => {
|
|
206
|
+
copyStylesToWindow(win);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Set container for portal immediately
|
|
210
|
+
setContainer(root);
|
|
211
|
+
|
|
212
|
+
// Setup keyboard shortcuts
|
|
213
|
+
const handleKeyDown = createKeyDownHandler(win);
|
|
214
|
+
window.addEventListener('keydown', handleKeyDown);
|
|
215
|
+
win.addEventListener('keydown', handleKeyDown);
|
|
216
|
+
|
|
217
|
+
// Handle window close
|
|
218
|
+
const handleClose = () => {
|
|
219
|
+
setContainer(null);
|
|
220
|
+
onClose();
|
|
221
|
+
};
|
|
222
|
+
win.addEventListener('beforeunload', handleClose);
|
|
223
|
+
|
|
224
|
+
// Cleanup
|
|
225
|
+
return () => {
|
|
226
|
+
window.removeEventListener('keydown', handleKeyDown);
|
|
227
|
+
win.removeEventListener('keydown', handleKeyDown);
|
|
228
|
+
win.removeEventListener('beforeunload', handleClose);
|
|
229
|
+
if (!win.closed) win.close();
|
|
230
|
+
};
|
|
231
|
+
// Only re-run if window features change, not on every render
|
|
232
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
233
|
+
}, [windowFeatures]);
|
|
234
|
+
|
|
235
|
+
return container ? createPortal(children, container) : null;
|
|
236
|
+
}
|
|
@@ -39,40 +39,54 @@ function CollapsedIconMenu({ menu, collapsed, icon, caption }) {
|
|
|
39
39
|
// Import t and i18n from useTranslation
|
|
40
40
|
const { t, i18n } = useTranslation();
|
|
41
41
|
const { state } = useContext(GlobalContext);
|
|
42
|
+
const [isMobile, setIsMobile] = useState(false);
|
|
43
|
+
|
|
44
|
+
useEffect(() => {
|
|
45
|
+
const handleResize = () => {
|
|
46
|
+
setIsMobile(window.innerWidth < 768); // Common breakpoint for mobile
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
handleResize(); // Set initial value
|
|
50
|
+
window.addEventListener('resize', handleResize);
|
|
51
|
+
|
|
52
|
+
return () => window.removeEventListener('resize', handleResize);
|
|
53
|
+
}, []);
|
|
54
|
+
|
|
42
55
|
const menuText = t(caption);
|
|
43
|
-
|
|
56
|
+
const menuContent = (
|
|
44
57
|
<>
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
<div
|
|
49
|
-
<
|
|
50
|
-
{menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
|
|
51
|
-
</div>
|
|
52
|
-
|
|
53
|
-
<div style={{ color: state.theme.colors.leftSectionColor }}>
|
|
54
|
-
<span className="caption">
|
|
55
|
-
{/* {caption} */}
|
|
56
|
-
{/* <Trans i18nKey="Appointments"></Trans> */}
|
|
57
|
-
{t(`${caption}`)}
|
|
58
|
-
</span>
|
|
59
|
-
</div>
|
|
58
|
+
{/* If value of collapsed is false show caption & icon else hiding caption and showing only icon*/}
|
|
59
|
+
{!collapsed ? (
|
|
60
|
+
<div className="menu-collapsed">
|
|
61
|
+
<div>
|
|
62
|
+
{menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
|
|
60
63
|
</div>
|
|
61
|
-
) : (
|
|
62
|
-
<div className="menu-collapsed">
|
|
63
|
-
<span className="anticon">
|
|
64
|
-
{menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
|
|
65
|
-
</span>
|
|
66
64
|
|
|
67
|
-
|
|
68
|
-
|
|
65
|
+
<div style={{ color: state.theme.colors.leftSectionColor }}>
|
|
66
|
+
<span className="caption">
|
|
67
|
+
{/* {caption} */}
|
|
68
|
+
{/* <Trans i18nKey="Appointments"></Trans> */}
|
|
69
69
|
{t(`${caption}`)}
|
|
70
70
|
</span>
|
|
71
71
|
</div>
|
|
72
|
-
|
|
73
|
-
|
|
72
|
+
</div>
|
|
73
|
+
) : (
|
|
74
|
+
<div className="menu-collapsed">
|
|
75
|
+
<span className="anticon">
|
|
76
|
+
{menu && menu.image_path ? <img style={{ width: '25px' }} src={menu.image_path}></img> : <i className={`fa-solid fas ${icon}`} />}
|
|
77
|
+
</span>
|
|
78
|
+
|
|
79
|
+
<span style={{ color: state.theme.colors.colorPrimaryText, paddingLeft: '6px' }}>
|
|
80
|
+
{/* <>{caption}</> */}
|
|
81
|
+
{t(`${caption}`)}
|
|
82
|
+
</span>
|
|
83
|
+
</div>
|
|
84
|
+
)}
|
|
74
85
|
</>
|
|
75
86
|
);
|
|
87
|
+
|
|
88
|
+
// On mobile, or when the menu is collapsed (based on original logic), don't show the popover tooltip.
|
|
89
|
+
return isMobile || collapsed ? menuContent : <Popover content={menuText} placement="right">{menuContent}</Popover>;
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
export default function SideMenu({ loading, modules = [], callback, appSettings, collapsed }) {
|
|
@@ -45,6 +45,8 @@ export default function AssignRole() {
|
|
|
45
45
|
const [loadingUser, setLoadingUser] = useState(false);
|
|
46
46
|
const [loadingRoles, setLoadingRoles] = useState(false);
|
|
47
47
|
const [loadingMenus, setLoadingMenus] = useState(false);
|
|
48
|
+
// for save
|
|
49
|
+
const [saving, setSaving] = useState(false);
|
|
48
50
|
|
|
49
51
|
const [selectedMenus, setSelectedMenus] = useState([]);
|
|
50
52
|
const [search, setSearch] = useState('');
|
|
@@ -133,13 +135,9 @@ export default function AssignRole() {
|
|
|
133
135
|
* @returns {Array<Object>}
|
|
134
136
|
*/
|
|
135
137
|
|
|
136
|
-
const filterAndSortMenus = (menus, allowedIds) => {
|
|
137
|
-
return
|
|
138
|
-
|
|
139
|
-
.map((m) => ({
|
|
140
|
-
...m,
|
|
141
|
-
sub_menus: filterAndSortMenus(m.sub_menus || [], allowedIds),
|
|
142
|
-
}));
|
|
138
|
+
const filterAndSortMenus = (menus, allowedIds = []) => {
|
|
139
|
+
if (!Array.isArray(menus) || !Array.isArray(allowedIds)) return [];
|
|
140
|
+
return menus.filter((m) => allowedIds.includes(m.id)).map((m) => ({ ...m, sub_menus: filterAndSortMenus(m.sub_menus || [], allowedIds) }));
|
|
143
141
|
};
|
|
144
142
|
|
|
145
143
|
/**
|
|
@@ -180,7 +178,7 @@ export default function AssignRole() {
|
|
|
180
178
|
/** Filtered roles */
|
|
181
179
|
const filteredRoles = useMemo(() => {
|
|
182
180
|
if (!search) return roles;
|
|
183
|
-
return roles.filter((r) => r
|
|
181
|
+
return roles.filter((r) => r?.name?.toLowerCase().includes(search.toLowerCase()));
|
|
184
182
|
}, [roles, search]);
|
|
185
183
|
|
|
186
184
|
/**
|
|
@@ -203,28 +201,30 @@ export default function AssignRole() {
|
|
|
203
201
|
*/
|
|
204
202
|
const handleSaveUserRole = async () => {
|
|
205
203
|
if (!id || !selectedRoles.length) {
|
|
206
|
-
message.warning('
|
|
204
|
+
message.warning('No roles selected to save');
|
|
207
205
|
return;
|
|
208
206
|
}
|
|
207
|
+
// start button spinner
|
|
208
|
+
setSaving(true);
|
|
209
209
|
|
|
210
210
|
try {
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
// Save all roles in parallel
|
|
213
212
|
await Promise.all(
|
|
214
213
|
selectedRoles.map((roleId) =>
|
|
215
214
|
UserRolesAPI.addUserRole({
|
|
216
|
-
values: {
|
|
217
|
-
user_id: id,
|
|
218
|
-
role_id: roleId,
|
|
219
|
-
},
|
|
215
|
+
values: { user_id: id, role_id: roleId },
|
|
220
216
|
})
|
|
221
217
|
)
|
|
222
218
|
);
|
|
223
219
|
|
|
224
|
-
|
|
220
|
+
// Only show success AFTER all saves
|
|
221
|
+
message.success('User roles saved successfully');
|
|
225
222
|
} catch (err) {
|
|
226
223
|
console.error(err);
|
|
227
|
-
message.error(
|
|
224
|
+
message.error('Failed to save user roles');
|
|
225
|
+
} finally {
|
|
226
|
+
// stop button spinner
|
|
227
|
+
setSaving(false);
|
|
228
228
|
}
|
|
229
229
|
};
|
|
230
230
|
|
|
@@ -233,7 +233,7 @@ export default function AssignRole() {
|
|
|
233
233
|
{/* LEFT PANEL */}
|
|
234
234
|
<Card className="left-panel" bodyStyle={{ padding: 12 }}>
|
|
235
235
|
<div size="small" className="user-card">
|
|
236
|
-
<Avatar size={40}>{user?.name[0]}</Avatar>
|
|
236
|
+
<Avatar size={40}>{user?.name?.[0] || ''}</Avatar>
|
|
237
237
|
|
|
238
238
|
<div className="user-info">
|
|
239
239
|
<div>{user?.name || '--'}</div>
|
|
@@ -264,7 +264,7 @@ export default function AssignRole() {
|
|
|
264
264
|
/>,
|
|
265
265
|
]}
|
|
266
266
|
>
|
|
267
|
-
<List.Item.Meta avatar={<Avatar size={28}>{role
|
|
267
|
+
<List.Item.Meta avatar={<Avatar size={28}>{role?.name?.[0] || '-'}</Avatar>} title={role.name} description={role.description} />
|
|
268
268
|
</List.Item>
|
|
269
269
|
)}
|
|
270
270
|
/>
|
|
@@ -288,7 +288,7 @@ export default function AssignRole() {
|
|
|
288
288
|
</div>
|
|
289
289
|
|
|
290
290
|
<div className="footer-actions">
|
|
291
|
-
<Button type="primary" onClick={handleSaveUserRole}>
|
|
291
|
+
<Button type="primary" onClick={handleSaveUserRole} loading={saving}>
|
|
292
292
|
Save
|
|
293
293
|
</Button>
|
|
294
294
|
</div>
|
|
@@ -34,6 +34,8 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
34
34
|
const [processTimings, setProcessTimings] = useState([]);
|
|
35
35
|
const [timelineCollapsed, setTimelineCollapsed] = useState(true);
|
|
36
36
|
const [showExternalWindow, setShowExternalWindow] = useState(false);
|
|
37
|
+
const [externalWin, setExternalWin] = useState(null);
|
|
38
|
+
|
|
37
39
|
const urlParams = Location.search();
|
|
38
40
|
let processId = urlParams.processId;
|
|
39
41
|
const [currentProcessId, setCurrentProcessId] = useState(processId);
|
|
@@ -245,25 +247,60 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
245
247
|
* - Prevents navigation beyond step boundaries.
|
|
246
248
|
* - Cleans up event listeners on unmount.
|
|
247
249
|
*/
|
|
250
|
+
// useEffect(() => {
|
|
251
|
+
// const handleKeyDown = (event) => {
|
|
252
|
+
// // Handle Left Arrow key press to go to the previous step
|
|
253
|
+
// if (event.key === 'ArrowLeft') {
|
|
254
|
+
// if (activeStep > 0) {
|
|
255
|
+
// handlePrevious();
|
|
256
|
+
// }
|
|
257
|
+
// }
|
|
258
|
+
// // Handle Right Arrow key press to go to the next step
|
|
259
|
+
// else if (event.key === 'ArrowRight') {
|
|
260
|
+
// if (activeStep < steps.length - 1) {
|
|
261
|
+
// handleNext();
|
|
262
|
+
// }
|
|
263
|
+
// }
|
|
264
|
+
// };
|
|
265
|
+
|
|
266
|
+
// // externalWin?.addEventListener('keydown', handleKeyDown);
|
|
267
|
+
// if (externalWin instanceof Window) {
|
|
268
|
+
// console.log("********************************",externalWin.addEventListener)
|
|
269
|
+
// externalWin.addEventListener('keydown', handleKeyDown);
|
|
270
|
+
// }
|
|
271
|
+
|
|
272
|
+
// // showExternalWindow.addEventListener('keydown', handleKeyDown);
|
|
273
|
+
// console.log("LLLLLLLLLLLLLL",window.addEventListener)
|
|
274
|
+
// window.addEventListener('keydown', handleKeyDown);
|
|
275
|
+
// return () => window.removeEventListener('keydown', handleKeyDown);
|
|
276
|
+
// }, [activeStep, steps, handlePrevious, handleNext, externalWin]);
|
|
248
277
|
useEffect(() => {
|
|
249
278
|
const handleKeyDown = (event) => {
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
if (activeStep > 0) {
|
|
253
|
-
handlePrevious();
|
|
254
|
-
}
|
|
279
|
+
if (event.key === 'ArrowLeft' && activeStep > 0) {
|
|
280
|
+
handlePrevious();
|
|
255
281
|
}
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
handleNext();
|
|
260
|
-
}
|
|
282
|
+
|
|
283
|
+
if (event.key === 'ArrowRight' && activeStep < steps.length - 1) {
|
|
284
|
+
handleNext();
|
|
261
285
|
}
|
|
262
286
|
};
|
|
263
287
|
|
|
264
|
-
window
|
|
265
|
-
|
|
266
|
-
|
|
288
|
+
// main window (document!)
|
|
289
|
+
document.addEventListener('keydown', handleKeyDown);
|
|
290
|
+
|
|
291
|
+
// external window (document!)
|
|
292
|
+
if (externalWin && externalWin.document) {
|
|
293
|
+
externalWin.document.addEventListener('keydown', handleKeyDown);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
return () => {
|
|
297
|
+
document.removeEventListener('keydown', handleKeyDown);
|
|
298
|
+
|
|
299
|
+
if (externalWin && externalWin.document) {
|
|
300
|
+
externalWin.document.removeEventListener('keydown', handleKeyDown);
|
|
301
|
+
}
|
|
302
|
+
};
|
|
303
|
+
}, [activeStep, steps, externalWin]);
|
|
267
304
|
|
|
268
305
|
/**
|
|
269
306
|
* Renders the main process UI including timeline, step details,
|
|
@@ -317,6 +354,11 @@ export default function ProcessStepsPage({ match, CustomComponents = {}, ...prop
|
|
|
317
354
|
return (
|
|
318
355
|
<>
|
|
319
356
|
<ExternalWindow
|
|
357
|
+
onWindowReady={(win) => {
|
|
358
|
+
console.log('>>>>>>>>>>>>>>>>>>>>>', win.addEventListener);
|
|
359
|
+
setExternalWin(win);
|
|
360
|
+
win.focus();
|
|
361
|
+
}}
|
|
320
362
|
title={steps[activeStep]?.step_name || 'Process Step'}
|
|
321
363
|
onClose={() => setShowExternalWindow(false)}
|
|
322
364
|
// left={window.screenX + window.outerWidth}
|