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.
@@ -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
- 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
- // }
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
+ }
@@ -3,7 +3,7 @@ import themes from './themes.json';
3
3
  export const THEME_GROUPS = {
4
4
  nura: ['nura', 'default'],
5
5
  purple: ['purple'],
6
- default: ['default'],
6
+ default: ['nura'],
7
7
  };
8
8
 
9
9
  const currentEnvTheme = process.env.REACT_APP_THEME?.toLowerCase() || 'default';
@@ -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
- parameters = JSON.parse(record.input_parameters);
172
+
173
+ parameters = record.input_parameters ? JSON.parse(record.input_parameters) : null;
164
174
 
165
175
  let formContent = {};
166
176
 
167
- parameters = await parameters.map((record) => {
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
- let exportDatas = getExportData(patients, cols);
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: true }}
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 style={{ marginTop: 20, display: 'flex', justifyContent: 'flex-start', gap: '10px' }}>
31
+ <div className="action-buttons-container">
29
32
  {/* Back button */}
30
- <Button disabled={activeStep === 0} onClick={handlePrevious} style={{ marginRight: 8, borderRadius: 4 }}>
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
- nextProcessId?.next_process_id ? (
51
- <Button
52
- type="primary"
53
- style={{
54
- borderRadius: 4,
55
- }}
56
- onClick={handleStartNextProcess}
57
- >
58
- Start Next {nextProcessId.next_process_name}
59
- </Button>
60
- ) : (
61
- <Button
62
- type="primary"
63
- style={{
64
- borderRadius: 4,
65
- }}
66
- onClick={handleFinish}
67
- >
68
- Finish
69
- </Button>
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({ processId, match, CustomComponents = {}, ...props }) {
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
- const [currentProcessId, setCurrentProcessId] = useState(processId);
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 urlParams = Location.search();
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
- if (await handleProcessSubmit(final)) props.history?.goBack();
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
- // Handle Left Arrow key press to go to the previous step
248
- if (event.key === 'ArrowLeft') {
249
- if (activeStep > 0) {
250
- handlePrevious();
251
- }
247
+ if (event.key === 'ArrowLeft' && activeStep > 0) {
248
+ handlePrevious();
252
249
  }
253
- // Handle Right Arrow key press to go to the next step
254
- else if (event.key === 'ArrowRight') {
255
- if (activeStep < steps.length - 1) {
256
- handleNext();
257
- }
250
+
251
+ if (event.key === 'ArrowRight' && activeStep < steps.length - 1) {
252
+ handleNext();
258
253
  }
259
254
  };
260
255
 
261
- window.addEventListener('keydown', handleKeyDown);
262
- return () => window.removeEventListener('keydown', handleKeyDown);
263
- }, [activeStep, steps, handlePrevious, handleNext]);
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
- <Card>
272
- <Row gutter={20}>
273
- <Col xs={24} sm={24} lg={timelineCollapsed ? 2 : 6}>
274
- <TimelinePanel
275
- loading={loading}
276
- steps={steps}
277
- activeStep={activeStep}
278
- timelineCollapsed={timelineCollapsed}
279
- handleTimelineClick={handleTimelineClick}
280
- setTimelineCollapsed={setTimelineCollapsed}
281
- />
282
- </Col>
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
- <Col xs={24} sm={24} lg={timelineCollapsed ? 21 : 18}>
285
- <div style={{ marginBottom: 20 }}>
286
- <h2 style={{ margin: 0, fontSize: 16, fontWeight: 600 }}>{steps[activeStep]?.step_name}</h2>
287
- <p style={{ margin: 0, color: '#666' }}>{steps[activeStep]?.step_description}</p>
288
- </div>
289
- <ActionButtons
290
- loading={loading}
291
- steps={steps}
292
- activeStep={activeStep}
293
- isStepCompleted={isStepCompleted}
294
- renderDynamicComponent={DynamicComponent}
295
- handlePrevious={handlePrevious}
296
- handleNext={handleNext}
297
- handleSkip={handleSkip}
298
- handleFinish={handleFinish}
299
- handleStartNextProcess={handleStartNextProcess}
300
- nextProcessId={nextProcessId}
301
- timelineCollapsed={timelineCollapsed}
302
- />
303
- </Col>
304
- </Row>
305
- </Card>
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: 40px;
50
+ height: 20px;
50
51
  background: #d9d9d9;
51
52
  }
52
53
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.5.0",
3
+ "version": "2.5.2",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"