ui-soxo-bootstrap-core 2.6.1-dev.2 → 2.6.1-dev.20

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.
Files changed (66) hide show
  1. package/core/components/extra-info/extra-info-details.js +2 -2
  2. package/core/components/index.js +2 -11
  3. package/core/components/landing-api/landing-api.js +91 -15
  4. package/core/components/landing-api/landing-api.scss +22 -0
  5. package/core/components/license-management/license-alert.js +97 -0
  6. package/core/lib/Store.js +3 -3
  7. package/core/lib/components/global-header/animations.js +78 -4
  8. package/core/lib/components/global-header/global-header.js +224 -255
  9. package/core/lib/components/global-header/global-header.scss +162 -24
  10. package/core/lib/components/sidemenu/animations.js +84 -2
  11. package/core/lib/components/sidemenu/sidemenu.js +191 -65
  12. package/core/lib/components/sidemenu/sidemenu.scss +221 -14
  13. package/core/lib/elements/basic/country-phone-input/country-phone-input.js +14 -8
  14. package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
  15. package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
  16. package/core/lib/models/forms/components/form-creator/form-creator.scss +4 -3
  17. package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
  18. package/core/lib/models/process/components/process-dashboard/process-dashboard.js +469 -3
  19. package/core/lib/models/process/components/process-dashboard/process-dashboard.scss +4 -0
  20. package/core/lib/pages/change-password/change-password.js +17 -24
  21. package/core/lib/pages/change-password/change-password.scss +45 -48
  22. package/core/lib/pages/login/commnication-mode-selection.js +2 -2
  23. package/core/lib/pages/login/login.js +47 -62
  24. package/core/lib/pages/login/login.scss +9 -0
  25. package/core/lib/pages/login/reset-password.js +17 -17
  26. package/core/lib/pages/login/reset-password.scss +10 -1
  27. package/core/lib/pages/profile/themes.json +4 -4
  28. package/core/lib/utils/api/api.utils.js +30 -18
  29. package/core/lib/utils/common/common.utils.js +49 -35
  30. package/core/lib/utils/http/http.utils.js +2 -1
  31. package/core/lib/utils/index.js +4 -1
  32. package/core/models/base/base.js +7 -3
  33. package/core/models/core-scripts/core-scripts.js +134 -126
  34. package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
  35. package/core/models/menus/components/menu-add/menu-add.js +1 -1
  36. package/core/models/menus/components/menu-lists/menu-lists.js +53 -54
  37. package/core/models/menus/menus.js +27 -2
  38. package/core/models/roles/components/role-add/role-add.js +92 -59
  39. package/core/models/roles/components/role-list/role-list.js +1 -1
  40. package/core/models/staff/components/staff-add/staff-add.js +20 -32
  41. package/core/models/users/components/assign-role/assign-role.js +145 -50
  42. package/core/models/users/components/assign-role/assign-role.scss +209 -45
  43. package/core/models/users/components/assign-role/avatar-props.js +45 -0
  44. package/core/models/users/components/user-add/user-add.js +46 -55
  45. package/core/models/users/components/user-add/user-edit.js +25 -4
  46. package/core/models/users/users.js +9 -1
  47. package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
  48. package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
  49. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +147 -0
  50. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
  51. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +90 -0
  52. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
  53. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +252 -0
  54. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
  55. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +326 -436
  56. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +7 -0
  57. package/core/modules/steps/action-buttons.js +33 -15
  58. package/core/modules/steps/action-buttons.scss +55 -9
  59. package/core/modules/steps/chat-assistant.js +141 -0
  60. package/core/modules/steps/openai-realtime.js +275 -0
  61. package/core/modules/steps/readme.md +167 -0
  62. package/core/modules/steps/steps.js +1078 -57
  63. package/core/modules/steps/steps.scss +539 -90
  64. package/core/modules/steps/timeline.js +21 -19
  65. package/core/modules/steps/voice-navigation.js +709 -0
  66. package/package.json +2 -1
@@ -1,11 +1,9 @@
1
1
  import React, { useState, useEffect, useContext, useRef } from 'react';
2
2
 
3
- import { Table, Skeleton, Input, Tag, Modal, message, Pagination, Tooltip } from 'antd';
3
+ import { Table, Skeleton, Input, Modal, message, Pagination } from 'antd';
4
4
 
5
5
  import { QrcodeOutlined } from '@ant-design/icons';
6
6
 
7
- import * as Icons from '@ant-design/icons';
8
-
9
7
  import { Location, FormCreator, GlobalContext, ExportReactCSV, getExportData, Card, TableComponent, QrScanner } from './../../../../lib/';
10
8
 
11
9
  import { CoreScripts } from './../../../../models/';
@@ -14,15 +12,15 @@ import moment from 'moment-timezone';
14
12
 
15
13
  import Button from '../../../../lib/elements/basic/button/button';
16
14
 
17
- import { Link } from 'react-router-dom';
18
-
19
15
  import './reporting-dashboard.scss';
20
16
 
21
17
  // import MenuDashBoard from '../../../../pages/homepage-api/menu-dashboard';
22
18
  import MenuDashBoardComponent from '../../../../lib/elements/basic/menu-dashboard/menu-dashboard';
23
19
  import { useHistory } from 'react-router-dom';
24
-
25
20
  import * as ReportingDashboardComp from '../index';
21
+ import buildDisplayColumns from './display-columns/build-display-columns';
22
+ import { getRedirectLink } from './display-columns/display-cell-renderer';
23
+ import AdvancedSearchSelect from './adavance-search/advance-search';
26
24
 
27
25
  // import { isPdfFile } from 'pdfjs-dist';
28
26
 
@@ -64,6 +62,10 @@ export default function ReportingDashboard({
64
62
 
65
63
  const [cardLoading, setCardLoading] = useState(true);
66
64
 
65
+ const [searchParameters, setSearchParameters] = useState([]);
66
+
67
+ const [searchValues, setSearchValues] = useState({});
68
+
67
69
  const [dashboardVisible, setDashBoardVisible] = useState(false);
68
70
 
69
71
  const [formContents, setformContents] = useState({});
@@ -107,16 +109,6 @@ export default function ReportingDashboard({
107
109
  const fetchId = idOverride || id;
108
110
  await CoreScripts.getRecord({ id: fetchId, dbPtr }).then(async ({ result }) => {
109
111
  // 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
- // console.log("ssssssssssssssssssss")
116
- // // Reset columns if no display columns exist
117
-
118
- // setColumns([]);
119
- // }
120
112
  let parsedColumns = [];
121
113
 
122
114
  if (result.display_columns) {
@@ -181,6 +173,8 @@ export default function ReportingDashboard({
181
173
  parameters = record.input_parameters ? JSON.parse(record.input_parameters) : null;
182
174
 
183
175
  let formContent = {};
176
+ const searchFields = parameters.filter((p) => p.type === 'search' && p.search_enabled === 'yes');
177
+ setSearchParameters([...searchFields]);
184
178
 
185
179
  parameters = await parameters?.map((record) => {
186
180
  // Only if the url params does have a matching value ,
@@ -224,11 +218,17 @@ export default function ReportingDashboard({
224
218
  if (record.type === 'date' && !formContent[record.field]) {
225
219
  formContent[record.field] = moment().tz(process.env.REACT_APP_TIMEZONE);
226
220
  }
227
-
221
+ // if (record.type === 'search') {
222
+ // return {
223
+ // ...record,
224
+ // component: AdvancedSearchSelect,
225
+ // required: record.required,
226
+ // };
227
+ // }
228
228
  if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
229
229
  // let model = "";
230
230
  let model = CustomModels[record.modelName];
231
- formContent[record.modelName] = model;
231
+
232
232
  return {
233
233
  ...record,
234
234
  model,
@@ -256,7 +256,7 @@ export default function ReportingDashboard({
256
256
  } else {
257
257
  //// Filter the array "parameters" and keep only elements where "type" is present (truthy)
258
258
  //Keep only parameters that have a "type" → used for UI display
259
- let filter = parameters.filter((ele) => ele.type);
259
+ let filter = parameters.filter((ele) => ele.type && ele.type !== 'search');
260
260
  // Update the "details" state with the filtered results
261
261
 
262
262
  setDetails([...filter]);
@@ -269,7 +269,7 @@ export default function ReportingDashboard({
269
269
  getPatientDetails();
270
270
  }
271
271
 
272
- const fetchReportData = async (id, values, dbPtr, pagination, parsedColumns,paramsString) => {
272
+ const fetchReportData = async (id, values, dbPtr, pagination, parsedColumns) => {
273
273
  const { current, pageSize } = pagination || {};
274
274
  // If card script id is exist load that id otherwise load id
275
275
  const coreScriptId = scriptId.current ? scriptId.current : id;
@@ -278,23 +278,10 @@ export default function ReportingDashboard({
278
278
  try {
279
279
  // Prepare payload for backend: format only here
280
280
  const formattedValues = {};
281
- let inputParams = [];
282
- inputParams = JSON.parse(paramsString);
283
- // Iterate through all keys in the values object
284
-
285
- inputParams.forEach(({ field }) => {
286
- const val = values[field];
287
-
288
- // field exists in values → format & assign
289
- if (val !== undefined) {
290
- formattedValues[field] = moment.isMoment(val) ? val.format('YYYY-MM-DD') : val;
291
- }
292
- // field missing in values → set null
293
- else {
294
- formattedValues[field] = null;
295
- }
281
+ Object.keys(values).forEach((key) => {
282
+ const val = values[key];
283
+ formattedValues[key] = moment.isMoment(val) ? val.format('YYYY-MM-DD') : val;
296
284
  });
297
- // });
298
285
  // Pagination Data
299
286
  const paginationData = {
300
287
  page: pagination.current || 1,
@@ -314,13 +301,18 @@ export default function ReportingDashboard({
314
301
 
315
302
  // Fetch result
316
303
  const result = await CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr);
304
+
305
+ const apiData = Array.isArray(result) ? result : Array.isArray(result?.result) ? result.result : [];
306
+
317
307
  // Handle both result formats
318
- let resultDetails = result[0];
308
+ let resultDetails = apiData[0] || [];
319
309
  if (result?.result && result?.result[0]) {
320
310
  resultDetails = result.result[0];
321
311
  }
322
312
  // Update patients
323
313
  setPatients(resultDetails || []);
314
+ console.log(parsedColumns);
315
+
324
316
  // Check if columns are not yet defined
325
317
  if (parsedColumns.length === 0 && resultDetails.length > 0) {
326
318
  // Create columns dynamically from resultDetails keys
@@ -346,40 +338,104 @@ export default function ReportingDashboard({
346
338
  }
347
339
  } catch (error) {
348
340
  console.error('Error fetching report data:', error);
349
- message.warn('Please try again');
350
341
  } finally {
351
342
  // Always runs, success or error
352
343
  setLoading(false);
353
344
  }
354
345
  };
355
346
 
356
- // Handle Submit
357
347
  const handleSubmit = (values) => {
348
+ const hasSearchValues = Object.values(searchValues).some((v) => Array.isArray(v) && v.length > 0);
349
+
350
+ if (!hasSearchValues) {
351
+ runSubmit(values);
352
+ return;
353
+ }
354
+
355
+ // Check if form values changed (date or others)
356
+ const formChanged = Object.keys(values).some((key) => {
357
+ const newVal = values[key];
358
+ const oldVal = formContents[key];
359
+
360
+ if (moment.isMoment(newVal) && moment.isMoment(oldVal)) {
361
+ return !newVal.isSame(oldVal, 'day');
362
+ }
363
+
364
+ return newVal !== oldVal;
365
+ });
366
+
367
+ if (formChanged) {
368
+ Modal.confirm({
369
+ title: 'Filters changed',
370
+ content: 'You changed some filters. Do you want to search using these filters also?',
371
+ okText: 'Yes',
372
+ cancelText: 'No',
373
+
374
+ onOk() {
375
+ // YES → send form values + search condition
376
+ const finalValues = {
377
+ ...values,
378
+ search_values: searchValues,
379
+ };
380
+
381
+ runSubmit(finalValues);
382
+ },
383
+
384
+ onCancel() {
385
+ // NO → reset form values
386
+ const resetValues = {};
387
+ Object.keys(values).forEach((key) => {
388
+ resetValues[key] = null;
389
+ });
390
+
391
+ const finalValues = {
392
+ ...resetValues,
393
+ search_values: searchValues,
394
+ };
395
+
396
+ runSubmit(finalValues);
397
+ },
398
+ });
399
+ } else {
400
+ // no form change
401
+ const resetValues = {};
402
+ Object.keys(values).forEach((key) => {
403
+ resetValues[key] = null;
404
+ });
405
+
406
+ const finalValues = {
407
+ ...resetValues,
408
+ search_values: searchValues,
409
+ };
410
+
411
+ runSubmit(finalValues);
412
+ }
413
+ };
414
+
415
+ const runSubmit = (finalValues) => {
358
416
  const { pageSize } = pagination;
359
417
  const resetPage = 1;
360
418
 
361
- // Reset script id on Submit
362
419
  scriptId.current = null;
363
420
 
364
- // Get current query params
365
- const currentUrlParams = Location.search();
366
- const { script_id, selected_card, ...cleanParams } = currentUrlParams;
421
+ const hasSearchValues = finalValues.search_values && Object.values(finalValues.search_values).some((v) => Array.isArray(v) && v.length > 0);
367
422
 
368
- // Construct new query string
369
- const newParams = new URLSearchParams({
370
- ...cleanParams,
371
- current: resetPage,
372
- pageSize,
373
- });
423
+ if (!hasSearchValues) {
424
+ const currentUrlParams = Location.search();
425
+ const { script_id, selected_card, ...cleanParams } = currentUrlParams;
426
+
427
+ const newParams = new URLSearchParams({
428
+ ...cleanParams,
429
+ current: resetPage,
430
+ pageSize,
431
+ });
374
432
 
375
- // Replace URL
376
- const newUrl = `${window.location.pathname}?${newParams.toString()}`;
377
- window.history.replaceState({}, '', newUrl);
433
+ const newUrl = `${window.location.pathname}?${newParams.toString()}`;
434
+ window.history.replaceState({}, '', newUrl);
435
+ }
378
436
 
379
- // Trigger submit handler
380
- onFinish(values, resetPage);
437
+ onFinish(finalValues, resetPage);
381
438
  };
382
-
383
439
  /**
384
440
  *
385
441
  * @param {*} values
@@ -432,7 +488,7 @@ export default function ReportingDashboard({
432
488
  Object.entries(urlsToUpdate).filter(([_, value]) => value !== undefined && value !== null && value !== '')
433
489
  );
434
490
 
435
- setformContents((prev) => ({ ...prev, ...formContent }));
491
+ setformContents(formContent);
436
492
  Location.search({ ...Location.search(), ...filteredParams });
437
493
  }
438
494
 
@@ -445,7 +501,7 @@ export default function ReportingDashboard({
445
501
 
446
502
  // Call API
447
503
  try {
448
- await fetchReportData(id, values, dbPtr, paginationData, parsedColumns,paramsString);
504
+ await fetchReportData(id, values, dbPtr, paginationData, parsedColumns);
449
505
  } finally {
450
506
  setLoading(false);
451
507
  setCardLoading(false);
@@ -470,23 +526,6 @@ export default function ReportingDashboard({
470
526
  <Card className="reporting-dashboard card card-shadow">
471
527
  {/** If dashBoardIds exist and contain elements, render MenuDashBoard*/}
472
528
 
473
- {/* Page Header */}
474
- {/* <div className="page-header">
475
- <div>
476
- <Title style={{ marginBottom: '0px' }} level={4}>
477
- {config.caption || 'Report'}
478
- </Title>
479
- </div>
480
-
481
- <div className="right">
482
- <div className="date-and-fltr">
483
- <Button onClick={refresh} type="secondary" size={'small'}>
484
- <ReloadOutlined />
485
- </Button>
486
- </div>
487
- </div>
488
- </div> */}
489
- {/* Page Header Ends */}
490
529
  {dashBoardIds?.length > 0 ? (
491
530
  <MenuDashBoardComponent
492
531
  dashBoardIds={dashBoardIds} //Pass the available dashboard IDs to the componen
@@ -556,6 +595,51 @@ export default function ReportingDashboard({
556
595
  />
557
596
  ) : null}
558
597
  {/* </Card> */}
598
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
599
+ {searchParameters.length > 0
600
+ ? searchParameters.map((param) => (
601
+ <AdvancedSearchSelect
602
+ key={param.field}
603
+ parameter={param}
604
+ field={param}
605
+ reportId={reportId}
606
+ value={searchValues[param.field] || []}
607
+ onChange={(value) => {
608
+ setSearchValues((prev) => ({
609
+ ...prev,
610
+ [param.field]: value,
611
+ }));
612
+ }}
613
+ onReset={() => {
614
+ setSearchValues((prev) => {
615
+ const updated = {
616
+ ...prev,
617
+ [param.field]: [],
618
+ };
619
+
620
+ // const finalValues = {
621
+ // ...formContents,
622
+ // search_values: updated,
623
+ // };
624
+ // console.log(finalValues);
625
+ // runSubmit(finalValues); // 🔥 refresh report correctly
626
+ getPatientDetails();
627
+
628
+ return updated;
629
+ });
630
+ }}
631
+ // onReset={() => {
632
+ // setSearchValues((prev) => ({
633
+ // ...prev,
634
+ // [param.field]: [],
635
+ // }));
636
+
637
+ // handleSubmit(formContents); // 🔥 refresh report
638
+ // }}
639
+ />
640
+ ))
641
+ : null}
642
+ </div>
559
643
  </div>
560
644
 
561
645
  {/** GuestList component start*/}
@@ -608,7 +692,6 @@ function GuestList({
608
692
  attributes,
609
693
  fetchReportData,
610
694
  }) {
611
-
612
695
  /**
613
696
  * @param {*} propValues
614
697
  */
@@ -639,269 +722,17 @@ function GuestList({
639
722
 
640
723
  const { isMobile, dispatch } = useContext(GlobalContext);
641
724
  const [single, setSingle] = useState({});
642
-
643
- const getRedirectLink = (entry, record) => {
644
- let redirectLink = entry.redirect_link;
645
- if (entry.replace_variables) {
646
- entry.replace_variables.forEach((replacement) => {
647
- const value = record[replacement.field] || '';
648
- redirectLink = redirectLink.replace(new RegExp(`@${replacement.field};`, 'g'), value);
649
- });
650
- }
651
- return redirectLink;
652
- };
725
+ const otherDetails = config.other_details1 ? JSON.parse(config.other_details1) : {};
653
726
 
654
727
  // const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
655
- const cols = [
656
- ...[
657
- {
658
- title: '#',
659
- dataIndex: 'index',
660
- key: 'ColumnIndex',
661
- width: 60,
662
- render: (value, item, index) => index + 1,
663
- key: 'ColumnIndex',
664
- fixed: isFixedIndex ? 'left' : null,
665
- },
666
- ],
667
- ...columns.map((entry) => {
668
- // if (entry.sort) {
669
- // return {
670
- // render: (record) => {
671
-
672
- // if (entry.render) {
673
-
674
- // return entry.render(record);
675
- // } else {
676
- // return record[entry.dataIndex]
677
- // }
678
- // },
679
- // title: entry.title,
680
- // key: entry.field,
681
- // sorter: (a, b) => entry.sort(a, b),
682
- // sortDirections: ['ascend', 'descend', 'ascend'],
683
- // };
684
- // } else {
685
- return {
686
- render: (record) => {
687
- let textColor = 'inherit';
688
-
689
- if (entry.color) {
690
- textColor = entry.color;
691
- }
692
- /** We can have x types of components that is to be rendered here */
693
-
694
- /**1. Column Data */
695
-
696
- /**2. Action */
697
-
698
- /**3. Custom Component - In future . */
699
-
700
- if (entry.render) {
701
- return entry.render(record);
702
-
703
- // The type of component can be differentiated by type/ we can also reuse
704
- // any field present in the columns to avoid any additional field
705
- } else if (entry.type === 'link') {
706
- //Cheacking type of action to be done ie,If it contains a type then it will match with the record
707
- // for example initally we are implementing it for a type link
708
- // ie, we will return a link in query with the field as link and type link in display_columns
709
- //So here we match the field returned by query with the type in display_columns
710
-
711
- if (record[entry.field]) {
712
- return (
713
- <a href={record[entry.field]} target="_blank" rel="noopener noreferrer">
714
- View
715
- </a>
716
- );
717
- }
718
- } else if (entry.field === 'action') {
719
- let redirectLink = entry.redirect_link;
720
-
721
- // The variables to be replaced can be maintained
722
- // as a configuration in the entry or the column configuration
723
-
724
- // We iterate through all the variables that are present in the configuration
725
- // and replace the link to generate the final link
726
-
727
- entry.replace_variables.forEach((replacement) => {
728
- redirectLink = redirectLink.replace(new RegExp('@' + replacement.field + ';', 'g'), record[replacement.field]);
729
- });
730
-
731
- return <Link to={`${redirectLink}`}>{entry.display_name_link ? entry.display_name_link : 'View'}</Link>;
732
- } else if (entry.field === 'custom') {
733
- // Make all the components in modules available for use in custom column of core script
734
- // var genericComponents = require('./../../../../../../../nura-api-new/nura-desk/src/modules');
735
- var genericComponents = CustomComponents;
736
-
737
- // Arrive the component name
738
- let componentName = entry.component;
739
-
740
- let LoadedComponent = null;
741
- // If there is custom components mensioned in the display_columns,
742
- // then matching the custom components with the generic components
743
- if (componentName) {
744
- if (componentName && genericComponents[componentName]) {
745
- LoadedComponent = genericComponents[componentName];
746
- }
747
- }
748
-
749
- let propValue = {};
750
- // If there is props value
751
- if (entry.props) {
752
- // Looping the props from the props mensioned in display_columns,
753
- // Matching the field of record props field with the and return the
754
- entry.props.forEach((values) => {
755
- // Matching the fields of record with the props field
756
- let valueCreation = record[values.field];
757
- // Returning the values of field matched by fields
758
- propValue[values.value] = valueCreation;
759
- });
760
- }
761
-
762
- return (
763
- <LoadedComponent
764
- // Configuration will define
765
- {...entry.config}
766
- callback={() => {
767
- refresh();
768
- }}
769
- // record={record}
770
-
771
- {...record}
772
- // assigning the props
773
- {...propValue}
774
- />
775
- );
776
- } else {
777
- // Check if both `color_code` exists in the record and `enableColor` is true
778
- if (record.color_code && entry.enableColor) {
779
- // If the column type is 'tag', render the field inside an Ant Design <Tag> with color
780
- if (entry.columnType === 'tag') {
781
- return <Tag color={record.color_code}>{record[entry.field]}</Tag>;
782
-
783
- // If the column type is 'span', render the field inside a <span> with inline color style
784
- } else if (entry.columnType === 'span') {
785
- return <span style={{ color: record.color_code, overflowWrap: 'break-word', WebkitLineClamp: 3 }}>{record[entry.field]}</span>;
786
- }
787
- } else {
788
- /**
789
- * This code dynamically displays icons based on data and a configuration
790
- * When a column's configuration includes a displayIcons JSON, the code will check the corresponding data field.
791
- * If the field's value (e.g., "Pending") matches a key in that JSON,
792
- * the system will display the specified icon with its defined color and size.
793
- */
794
- if (entry.displayIcons) {
795
- // Get the field value
796
- const fieldValue = record[entry.field]?.toString();
797
-
798
- // Get the configuration for the icon from the JSON.
799
- const displayConfig = entry.displayIcons[fieldValue];
800
- if (displayConfig) {
801
- // Look up the actual component from our iconMap using the name from the JSON.
802
- const DynamicIcon = Icons[displayConfig.icon];
803
- // If a component is found, render it with the specified color and size.
804
- if (DynamicIcon) {
805
- return (
806
- <DynamicIcon
807
- style={{
808
- color: displayConfig.color,
809
- fontSize: displayConfig.size,
810
- }}
811
- />
812
- );
813
- }
814
- }
815
- } else {
816
- //If the value is neither 'Y' nor 'N', return the actual field value
817
- return <span style={{ color: textColor, whiteSpace: 'pre-wrap', overflowWrap: 'break-word' }}>{record[entry.field]}</span>;
818
- }
819
- }
820
- }
821
- },
822
- field: entry.field,
823
- // title: entry.title,
824
- // title: (
825
- // <Tooltip title={entry.title}>
826
- // {entry.title}
827
- // </Tooltip>
828
- // ),
829
- title: (
830
- <Tooltip title={entry.tooltip || entry.title}>
831
- <span>{entry.title}</span>
832
- </Tooltip>
833
- ),
834
- key: entry.field,
835
- width: entry.width ? parseInt(entry.width) : 160,
836
- fixed: entry.isFixedColumn ? entry.isFixedColumn : null, // Conditionally setting the 'fixed' key to 'left' if 'isColumnStatic' is true; otherwise, setting it to null.
837
- // Check if filtering is enabled and patients is an array
838
- filters:
839
- entry.isFilterEnabled && Array.isArray(patients)
840
- ? [...new Set(patients.map((item) => item[entry.field]).filter(Boolean))].map((value) => ({ text: value, value }))
841
- : null,
842
- // Apply the filter only if it's enabled for the column
843
- onFilter: entry.isFilterEnabled ? (value, record) => record[entry.field] === value : null,
844
- //If sorting is enabled for this entry, provide a sorter function
845
- sorter: entry.isSortingEnabled ? (a, b) => String(a[entry.field]).localeCompare(String(b[entry.field])) : null,
846
-
847
- // Apply the filter search
848
- filterSearch: entry.isFilterEnabled ? entry.isFilterEnabled : false,
849
- exportDefinition: (record) => {
850
- //Custom components should not be downloaded
851
- // return entry.field === 'custom' ? null : record[entry.field];
852
- if (entry.field === 'custom') {
853
- // Find the field key in props that corresponds to 'description'
854
- const description = entry.props?.find((p) => p.value === 'description')?.field;
855
-
856
- // Return the value from record.props if it exists
857
- return description && record[description] ? record[description] : null;
858
- }
859
- return record[entry.field];
860
- },
861
- // Add align property based on column type
862
- align: entry.type === 'number' ? 'right' : 'left',
863
- };
864
- // }
865
- }),
866
- // ...[
867
- // {
868
- // title: '',
869
- // key: 'action',
870
- // render: (text, record) => {
871
- // let detail = model.slice(0, model.length - 1);
872
-
873
- // return (
874
- // <Space size="middle">
875
- // {!schema.hideView && !actions.length ? <Link to={`/${city}/${model}/${text.id}`}>View</Link> : null}
876
-
877
- // {actions.map((action) => (
878
- // <Link to={action.url(record)}>{action.caption}</Link>
879
- // ))}
880
- // </Space>
881
- // );
882
- // },
883
- // },
884
- // ],
885
-
886
- // ...[
887
- // {
888
- // title: '',
889
- // key: 'action',
890
- // render: (text, record) => {
891
- // let detail = model.slice(0, model.length - 1);
892
-
893
- // return (
894
- // <Link to={`${menu.path.replace(':id',visitid)}?op_no=${OpNo}`}>
895
- // <Button size={'small'}>
896
- // <PlayCircleOutlined />
897
- // Go to Menu
898
- // </Button>
899
- // </Link>
900
- // );
901
- // },
902
- // },
903
- // ],
904
- ];
728
+ const cols = buildDisplayColumns({
729
+ columns,
730
+ patients,
731
+ isFixedIndex,
732
+ CustomComponents,
733
+ refresh,
734
+ otherDetails,
735
+ });
905
736
 
906
737
  /**
907
738
  *
@@ -928,10 +759,6 @@ function GuestList({
928
759
  useEffect(() => {
929
760
  //Cheaking if there is patient data exists
930
761
  if (patients) {
931
- // Commented due to a production issue:
932
- // When displayColumns is not defined, rowIndex and dispatch fields were getting displayed in the table.
933
- // Also, the `data` variable is not used in the code below, and the setData state is already commented out.
934
-
935
762
  // let data = patients?.map((entry) => {
936
763
  // entry.rowIndex = entry.opb_id;
937
764
 
@@ -1003,7 +830,7 @@ function GuestList({
1003
830
  setRedirectPatient(matched);
1004
831
  message.success(`Match found for ${code}, redirecting...`);
1005
832
 
1006
- const actionColumn = columns.find((col) => col.field === 'action');
833
+ const actionColumn = columns.find((col) => col.field === 'action') || columns.find((col) => col.type === 'action');
1007
834
  if (actionColumn) {
1008
835
  const redirectLink = getRedirectLink(actionColumn, patient);
1009
836
  // history.push(redirectLink);
@@ -1036,6 +863,40 @@ function GuestList({
1036
863
  const handleCloseEdit = () => {
1037
864
  setShowEdit(false);
1038
865
  };
866
+ /**
867
+ *
868
+ */
869
+ function calculateSummaryValues(summaryCols, pageData) {
870
+ const summaryValues = {};
871
+
872
+ summaryCols.forEach((col) => {
873
+ const field = col.field;
874
+
875
+ if (col.function === 'sum') {
876
+ summaryValues[field] = pageData.reduce((total, row) => total + Number(row[field] || 0), 0);
877
+ }
878
+
879
+ if (col.function === 'count') {
880
+ summaryValues[field] = pageData.length;
881
+ }
882
+
883
+ if (col.function === 'avg') {
884
+ const total = pageData.reduce((sum, row) => sum + Number(row[field] || 0), 0);
885
+ summaryValues[field] = pageData.length ? total / pageData.length : 0;
886
+ }
887
+ if (col.function === 'min') {
888
+ const values = pageData.map((row) => Number(row[field] || 0));
889
+ summaryValues[field] = values.length ? Math.min(...values) : 0;
890
+ }
891
+
892
+ if (col.function === 'max') {
893
+ const values = pageData.map((row) => Number(row[field] || 0));
894
+ summaryValues[field] = values.length ? Math.max(...values) : 0;
895
+ }
896
+ });
897
+
898
+ return summaryValues;
899
+ }
1039
900
  return (
1040
901
  <>
1041
902
  <div className="table-header">
@@ -1095,12 +956,6 @@ function GuestList({
1095
956
  </div>
1096
957
 
1097
958
  <div>
1098
- {/* {view ? (
1099
- <>
1100
- <CardList dataSource={data} columns={columns} />
1101
- </>
1102
- ) : (
1103
- <> */}
1104
959
  <Card>
1105
960
  {loading ? (
1106
961
  <>
@@ -1109,54 +964,139 @@ function GuestList({
1109
964
  ) : (
1110
965
  <TableComponent
1111
966
  size="small"
967
+ // scroll={{ x: 'max-content' }}
1112
968
  scroll={{ x: 'max-content', y: '60vh' }}
1113
- tableLayout="fixed"
1114
- sticky
1115
969
  rowKey={(record) => record.OpNo}
1116
970
  dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
1117
971
  columns={cols}
972
+ sticky
1118
973
  pagination={false}
1119
974
  // title={config.caption}
975
+ // summary={(pageData) => {
976
+ // // Variable to save the summary data
977
+ // let summary = {};
978
+
979
+ // let summaryColumns = [
980
+ // { field: 'opb_amt', title: 'Amount' },
981
+ // { field: 'opb_netamt', title: 'Net Amount' },
982
+ // ];
983
+
984
+ // let tableColumns = cols;
985
+
986
+ // // Creating a copy of columns to append the summary configuration that is needed to set
987
+ // tableColumns.forEach((record, index) => {
988
+ // summaryColumns.forEach((inner) => {
989
+ // if (record.field === inner.field) {
990
+ // tableColumns[index].summary = inner;
991
+ // }
992
+ // });
993
+ // });
994
+
995
+ // // Initialize
996
+ // summaryColumns.map((item) => {
997
+ // return (summary[item.field] = 0);
998
+ // });
999
+
1000
+ // // Find the total
1001
+ // summaryColumns.map((item) => {
1002
+ // pageData.forEach((entry) => {
1003
+ // return (summary[item.field] = summary[item.field] + entry[item.field]);
1004
+ // });
1005
+ // });
1006
+
1007
+ // return (
1008
+ // <>
1009
+ // <Table.Summary.Row>
1010
+ // {tableColumns.map((column, key) => {
1011
+ // return <Table.Summary.Cell key={key}>{column.summary ? <>{summary[column.summary.field]}</> : null}</Table.Summary.Cell>;
1012
+ // })}
1013
+ // </Table.Summary.Row>
1014
+ // </>
1015
+ // );
1016
+ // }}
1017
+ // summary={(pageData) => {
1018
+ // const summaryCols = columns.filter((col) => col.enable_summary);
1019
+ // if (!summaryCols.length) return null;
1020
+
1021
+ // const summaryValues = {};
1022
+
1023
+ // summaryCols.forEach((col) => {
1024
+ // const field = col.field;
1025
+
1026
+ // if (col.function === 'sum') {
1027
+ // summaryValues[field] = pageData.reduce((total, row) => total + Number(row[field] || 0), 0);
1028
+ // }
1029
+
1030
+ // if (col.function === 'count') {
1031
+ // summaryValues[field] = pageData.length;
1032
+ // }
1033
+
1034
+ // if (col.function === 'avg') {
1035
+ // const total = pageData.reduce((sum, row) => sum + Number(row[field] || 0), 0);
1036
+ // summaryValues[field] = pageData.length ? total / pageData.length : 0;
1037
+ // }
1038
+ // });
1039
+
1040
+ // return (
1041
+ // <Table.Summary.Row className="report-summary-row">
1042
+ // {cols.map((col, index) => {
1043
+ // // show summary value
1044
+ // if (summaryValues[col.field] !== undefined) {
1045
+ // return (
1046
+ // <Table.Summary.Cell key={index}>
1047
+ // <strong>{summaryValues[col.field]}</strong>
1048
+ // </Table.Summary.Cell>
1049
+ // );
1050
+ // }
1051
+
1052
+ // // caption before summary column
1053
+ // const nextCol = cols[index + 1];
1054
+ // if (nextCol && summaryValues[nextCol.field] !== undefined) {
1055
+ // const configCol = columns.find((c) => c.field === nextCol.field);
1056
+ // return (
1057
+ // <Table.Summary.Cell key={index}>
1058
+ // <strong>{configCol?.summary_caption || 'Total'}</strong>
1059
+ // </Table.Summary.Cell>
1060
+ // );
1061
+ // }
1062
+
1063
+ // return <Table.Summary.Cell key={index} />;
1064
+ // })}
1065
+ // </Table.Summary.Row>
1066
+ // );
1067
+ // }}
1068
+
1120
1069
  summary={(pageData) => {
1121
- // Variable to save the summary data
1122
- let summary = {};
1123
-
1124
- let summaryColumns = [
1125
- { field: 'opb_amt', title: 'Amount' },
1126
- { field: 'opb_netamt', title: 'Net Amount' },
1127
- ];
1128
-
1129
- let tableColumns = cols;
1130
-
1131
- // Creating a copy of columns to append the summary configuration that is needed to set
1132
- tableColumns.forEach((record, index) => {
1133
- summaryColumns.forEach((inner) => {
1134
- if (record.field === inner.field) {
1135
- tableColumns[index].summary = inner;
1136
- }
1137
- });
1138
- });
1139
-
1140
- // Initialize
1141
- summaryColumns.map((item) => {
1142
- return (summary[item.field] = 0);
1143
- });
1144
-
1145
- // Find the total
1146
- summaryColumns.map((item) => {
1147
- pageData.forEach((entry) => {
1148
- return (summary[item.field] = summary[item.field] + entry[item.field]);
1149
- });
1150
- });
1070
+ const summaryCols = columns.filter((col) => col.enable_summary);
1071
+ if (!summaryCols.length) return null;
1072
+
1073
+ const summaryValues = calculateSummaryValues(summaryCols, pageData);
1151
1074
 
1152
1075
  return (
1153
- <>
1154
- <Table.Summary.Row>
1155
- {tableColumns.map((column, key) => {
1156
- return <Table.Summary.Cell key={key}>{column.summary ? <>{summary[column.summary.field]}</> : null}</Table.Summary.Cell>;
1157
- })}
1158
- </Table.Summary.Row>
1159
- </>
1076
+ <Table.Summary.Row className="report-summary-row">
1077
+ {cols.map((col, index) => {
1078
+ if (summaryValues[col.field] !== undefined) {
1079
+ return (
1080
+ <Table.Summary.Cell key={index}>
1081
+ <strong>{summaryValues[col.field]}</strong>
1082
+ </Table.Summary.Cell>
1083
+ );
1084
+ }
1085
+
1086
+ const nextCol = cols[index + 1];
1087
+ if (nextCol && summaryValues[nextCol.field] !== undefined) {
1088
+ const configCol = columns.find((c) => c.field === nextCol.field);
1089
+
1090
+ return (
1091
+ <Table.Summary.Cell key={index}>
1092
+ <strong>{configCol?.summary_caption || 'Total'}</strong>
1093
+ </Table.Summary.Cell>
1094
+ );
1095
+ }
1096
+
1097
+ return <Table.Summary.Cell key={index} />;
1098
+ })}
1099
+ </Table.Summary.Row>
1160
1100
  );
1161
1101
  }}
1162
1102
  />
@@ -1183,53 +1123,3 @@ function GuestList({
1183
1123
  </>
1184
1124
  );
1185
1125
  }
1186
-
1187
- // //Mobile view card Section
1188
- // function CardList({ dataSource, url }) {
1189
- // const { user = {}, dispatch } = useContext(GlobalContext);
1190
-
1191
- // function onClick(item) {
1192
- // Location.navigate({
1193
- // url: `/lab-detail/${item.BillID}`,
1194
- // });
1195
-
1196
- // dispatch({ type: 'index', payload: item.rowIndex });
1197
- // }
1198
-
1199
- // return dataSource.map((item, index) => {
1200
- // // to={`/lab-detail/${item.BillID}`}
1201
- // return (
1202
- // <div
1203
- // key={index}
1204
- // className="report-item"
1205
- // onClick={() => {
1206
- // onClick(item);
1207
- // }}
1208
- // >
1209
- // <GuestCard record={item} />
1210
- // </div>
1211
- // );
1212
- // });
1213
- // }
1214
-
1215
- // function GuestCard({ record }) {
1216
- // return (
1217
- // <Card className="card vehicle-card">
1218
- // <div className="card">
1219
- // <h2 className="title">{record.PName}</h2>
1220
-
1221
- // <h4 className="values">{record.OpNo}</h4>
1222
-
1223
- // <h3 className="values">{record.Test}</h3>
1224
-
1225
- // <h3 className="values">Primary Result : {record.PrimaryResult || 'Pending'}</h3>
1226
-
1227
- // <Text type="secondary">{record.Mobile}</Text>
1228
-
1229
- // <h4 className="values">{record.Date}</h4>
1230
- // </div>
1231
- // </Card>
1232
- // );
1233
- // }
1234
- // );
1235
- // }