ui-soxo-bootstrap-core 2.6.0 → 2.6.1-dev.10

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 (57) hide show
  1. package/.github/workflows/npm-publish.yml +49 -19
  2. package/core/components/extra-info/extra-info-details.js +2 -2
  3. package/core/components/menu-template-api/menu-template-api.js +2 -2
  4. package/core/lib/Store.js +3 -3
  5. package/core/lib/components/global-header/global-header.js +2 -2
  6. package/core/lib/components/sidemenu/sidemenu.js +19 -13
  7. package/core/lib/elements/basic/country-phone-input/country-phone-input.js +35 -60
  8. package/core/lib/elements/basic/country-phone-input/phone-input.scss +14 -0
  9. package/core/lib/elements/basic/dragabble-wrapper/draggable-wrapper.js +1 -1
  10. package/core/lib/elements/basic/menu-tree/menu-tree.js +26 -13
  11. package/core/lib/models/forms/components/form-creator/form-creator.js +468 -502
  12. package/core/lib/models/forms/components/form-creator/form-creator.scss +5 -4
  13. package/core/lib/models/menus/components/menu-list/menu-list.js +424 -467
  14. package/core/lib/pages/change-password/change-password.js +17 -24
  15. package/core/lib/pages/change-password/change-password.scss +45 -48
  16. package/core/lib/pages/login/commnication-mode-selection.js +46 -0
  17. package/core/lib/pages/login/communication-mode-selection.scss +60 -0
  18. package/core/lib/pages/login/login.js +153 -24
  19. package/core/lib/pages/login/login.scss +229 -334
  20. package/core/lib/pages/login/reset-password.js +124 -0
  21. package/core/lib/pages/login/reset-password.scss +31 -0
  22. package/core/lib/pages/profile/themes.json +4 -4
  23. package/core/lib/utils/api/api.utils.js +71 -48
  24. package/core/lib/utils/common/common.utils.js +109 -0
  25. package/core/lib/utils/http/http.utils.js +1 -0
  26. package/core/lib/utils/index.js +25 -28
  27. package/core/models/base/base.js +7 -3
  28. package/core/models/core-scripts/core-scripts.js +9 -0
  29. package/core/models/doctor/components/doctor-add/doctor-add.js +9 -4
  30. package/core/models/menus/components/menu-add/menu-add.js +1 -1
  31. package/core/models/menus/components/menu-lists/menu-lists.js +5 -9
  32. package/core/models/menus/menus.js +21 -2
  33. package/core/models/roles/components/role-add/role-add.js +92 -59
  34. package/core/models/roles/components/role-list/role-list.js +1 -1
  35. package/core/models/roles/roles.js +9 -0
  36. package/core/models/staff/components/staff-add/staff-add.js +20 -32
  37. package/core/models/users/components/assign-role/assign-role.js +145 -50
  38. package/core/models/users/components/assign-role/assign-role.scss +209 -45
  39. package/core/models/users/components/assign-role/avatar-props.js +45 -0
  40. package/core/models/users/components/user-add/user-add.js +47 -56
  41. package/core/models/users/components/user-add/user-edit.js +25 -4
  42. package/core/models/users/users.js +34 -8
  43. package/core/modules/dashboard/components/dashboard-card/menu-dashboard-card.js +1 -1
  44. package/core/modules/reporting/components/reporting-dashboard/README.md +316 -0
  45. package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +120 -0
  46. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.js +75 -0
  47. package/core/modules/reporting/components/reporting-dashboard/display-columns/build-display-columns.test.js +74 -0
  48. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.js +252 -0
  49. package/core/modules/reporting/components/reporting-dashboard/display-columns/display-cell-renderer.test.js +126 -0
  50. package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +222 -376
  51. package/core/modules/steps/action-buttons.js +47 -45
  52. package/core/modules/steps/action-buttons.scss +35 -6
  53. package/core/modules/steps/steps.js +12 -10
  54. package/core/modules/steps/steps.scss +229 -31
  55. package/core/modules/steps/timeline.js +21 -19
  56. package/package.json +3 -2
  57. package/core/components/external-window/DEVELOPER_GUIDE.md +0 -705
@@ -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,14 @@ 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
+ let parsedColumns = [];
112
113
 
113
- setColumns(JSON.parse(result.display_columns));
114
- } else {
115
- // Reset columns if no display columns exist
116
-
117
- setColumns([]);
114
+ if (result.display_columns) {
115
+ parsedColumns = JSON.parse(result.display_columns);
118
116
  }
119
- await prepareInputParameters(result);
117
+ setColumns(parsedColumns);
118
+
119
+ await prepareInputParameters(result, parsedColumns);
120
120
 
121
121
  if (result.summary_columns) {
122
122
  setSummaryColumns(JSON.parse(result.summary_columns));
@@ -161,7 +161,7 @@ export default function ReportingDashboard({
161
161
  */
162
162
 
163
163
  //Prepare input parameters by mapping default values and binding models if needed
164
- async function prepareInputParameters(record) {
164
+ async function prepareInputParameters(record, parsedColumns) {
165
165
  setLoading(true);
166
166
  let urlParams = Location.search();
167
167
 
@@ -173,6 +173,8 @@ export default function ReportingDashboard({
173
173
  parameters = record.input_parameters ? JSON.parse(record.input_parameters) : null;
174
174
 
175
175
  let formContent = {};
176
+ const searchFields = parameters.filter((p) => p.type === 'search');
177
+ setSearchParameters([...searchFields]);
176
178
 
177
179
  parameters = await parameters?.map((record) => {
178
180
  // Only if the url params does have a matching value ,
@@ -216,7 +218,13 @@ export default function ReportingDashboard({
216
218
  if (record.type === 'date' && !formContent[record.field]) {
217
219
  formContent[record.field] = moment().tz(process.env.REACT_APP_TIMEZONE);
218
220
  }
219
-
221
+ // if (record.type === 'search') {
222
+ // return {
223
+ // ...record,
224
+ // component: AdvancedSearchSelect,
225
+ // required: record.required,
226
+ // };
227
+ // }
220
228
  if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
221
229
  // let model = "";
222
230
  let model = CustomModels[record.modelName];
@@ -237,7 +245,7 @@ export default function ReportingDashboard({
237
245
  setformContents(formContent);
238
246
 
239
247
  // Trigger form submission
240
- onFinish(formContent, null, record.input_parameters);
248
+ onFinish(formContent, null, record.input_parameters, parsedColumns);
241
249
 
242
250
  setLoading(false);
243
251
 
@@ -248,7 +256,7 @@ export default function ReportingDashboard({
248
256
  } else {
249
257
  //// Filter the array "parameters" and keep only elements where "type" is present (truthy)
250
258
  //Keep only parameters that have a "type" → used for UI display
251
- let filter = parameters.filter((ele) => ele.type);
259
+ let filter = parameters.filter((ele) => ele.type && ele.type !== 'search');
252
260
  // Update the "details" state with the filtered results
253
261
 
254
262
  setDetails([...filter]);
@@ -261,7 +269,7 @@ export default function ReportingDashboard({
261
269
  getPatientDetails();
262
270
  }
263
271
 
264
- const fetchReportData = async (id, values, dbPtr, pagination) => {
272
+ const fetchReportData = async (id, values, dbPtr, pagination, parsedColumns) => {
265
273
  const { current, pageSize } = pagination || {};
266
274
  // If card script id is exist load that id otherwise load id
267
275
  const coreScriptId = scriptId.current ? scriptId.current : id;
@@ -290,17 +298,23 @@ export default function ReportingDashboard({
290
298
  if (scope) {
291
299
  formBody = { body: { ...scope, ...paginationData } };
292
300
  }
301
+
293
302
  // Fetch result
294
303
  const result = await CoreScripts.getReportingLisitng(coreScriptId, formBody, dbPtr);
304
+
305
+ const apiData = Array.isArray(result) ? result : Array.isArray(result?.result) ? result.result : [];
306
+
295
307
  // Handle both result formats
296
- let resultDetails = result[0];
308
+ let resultDetails = apiData[0] || [];
297
309
  if (result?.result && result?.result[0]) {
298
310
  resultDetails = result.result[0];
299
311
  }
300
312
  // Update patients
301
313
  setPatients(resultDetails || []);
314
+ console.log(parsedColumns);
315
+
302
316
  // Check if columns are not yet defined
303
- if (columns.length === 0 && resultDetails.length > 0) {
317
+ if (parsedColumns.length === 0 && resultDetails.length > 0) {
304
318
  // Create columns dynamically from resultDetails keys
305
319
  setColumns((prev) => {
306
320
  if (prev.length > 0) return prev;
@@ -324,46 +338,183 @@ export default function ReportingDashboard({
324
338
  }
325
339
  } catch (error) {
326
340
  console.error('Error fetching report data:', error);
327
- message.warn('Please try again');
328
341
  } finally {
329
342
  // Always runs, success or error
330
343
  setLoading(false);
331
344
  }
332
345
  };
333
346
 
334
- // Handle Submit
335
- const handleSubmit = (values) => {
336
- const { pageSize } = pagination;
337
- const resetPage = 1;
347
+ /**
348
+ *
349
+ * @param {*} searchValues
350
+ * @param {*} searchParameters
351
+ * @returns
352
+ */
353
+ const buildSearchCondition = (searchValues, searchParameters) => {
354
+ let searchCondition = "";
355
+
356
+ Object.keys(searchValues).forEach((key) => {
357
+ const arr = searchValues[key];
358
+ if (!Array.isArray(arr) || arr.length === 0) return;
359
+
360
+ const param = searchParameters.find((p) => p.field === key);
361
+ if (!param) return;
362
+
363
+ const operator = (param.search_operator || "").trim().toUpperCase();
364
+ const column = param.db_column || key;
365
+
366
+ if (operator === "LIKE") {
367
+ const likeConditions = arr
368
+ .map((v) => `${column} LIKE '${v}'`)
369
+ .join(" OR ");
370
+ searchCondition += ` AND (${likeConditions})`;
371
+ }
372
+
373
+ else if (operator === "IN") {
374
+ const valueList = arr.map((v) => `'${v}'`).join(",");
375
+ searchCondition += ` AND ${column} IN (${valueList})`;
376
+ }
377
+
378
+ else if (operator === "=") {
379
+ const eqConditions = arr
380
+ .map((v) => `${column} = '${v}'`)
381
+ .join(" OR ");
382
+ searchCondition += ` AND (${eqConditions})`;
383
+ }
384
+
385
+ else if (operator === "!=") {
386
+ const neqConditions = arr
387
+ .map((v) => `${column} != '${v}'`)
388
+ .join(" AND ");
389
+ searchCondition += ` AND (${neqConditions})`;
390
+ }
391
+
392
+ else {
393
+ // fallback for other operators like > < >= <=
394
+ const conditions = arr
395
+ .map((v) => `${column} ${operator} '${v}'`)
396
+ .join(" OR ");
397
+ searchCondition += ` AND (${conditions})`;
398
+ }
399
+ });
400
+
401
+ return searchCondition;
402
+ };
403
+ const handleSubmit = (values) => {
404
+
405
+ const hasSearchValues = Object.values(searchValues).some(
406
+ (v) => Array.isArray(v) && v.length > 0
407
+ );
408
+
409
+ let finalValues = values;
410
+
411
+ if (hasSearchValues) {
412
+
413
+ const searchCondition = buildSearchCondition(
414
+ searchValues,
415
+ searchParameters
416
+ );
417
+
418
+ const resetValues = {};
419
+ Object.keys(values).forEach((key) => {
420
+ resetValues[key] = null;
421
+ });
422
+
423
+ finalValues = {
424
+ ...resetValues,
425
+ search_condition: searchCondition,
426
+ };
427
+ }
338
428
 
339
- // Reset script id on Submit
340
- scriptId.current = null;
429
+ const { pageSize } = pagination;
430
+ const resetPage = 1;
341
431
 
342
- // Get current query params
432
+ scriptId.current = null;
433
+
434
+ if (!hasSearchValues) {
343
435
  const currentUrlParams = Location.search();
344
436
  const { script_id, selected_card, ...cleanParams } = currentUrlParams;
345
437
 
346
- // Construct new query string
347
438
  const newParams = new URLSearchParams({
348
439
  ...cleanParams,
349
440
  current: resetPage,
350
441
  pageSize,
351
442
  });
352
443
 
353
- // Replace URL
354
444
  const newUrl = `${window.location.pathname}?${newParams.toString()}`;
355
- window.history.replaceState({}, '', newUrl);
445
+ window.history.replaceState({}, "", newUrl);
446
+ }
356
447
 
357
- // Trigger submit handler
358
- onFinish(values, resetPage);
359
- };
448
+ onFinish(finalValues, resetPage);
449
+ };
450
+ // const handleSubmit = (values) => {
451
+
452
+ // // Check if any advanced search value exists
453
+ // const hasSearchValues = Object.values(searchValues).some((v) => Array.isArray(v) && v.length > 0);
454
+
455
+ // let finalValues = values;
456
+ // let searchCondition = '';
457
+ // if (hasSearchValues) {
458
+ // Object.keys(searchValues).forEach((key) => {
459
+ // const arr = searchValues[key];
460
+ // if (!Array.isArray(arr) || arr.length === 0) return;
461
+
462
+ // // Find input parameter configuration
463
+ // const param = searchParameters.find((p) => p.field === key);
464
+ // if (!param) return;
465
+
466
+ // const operator = (param.search_operator || '').trim(); // LIKE / IN / =
467
+ // const column = param.db_column || key; // DB column mapping if available
468
+
469
+ // // Build value list
470
+ // const valueList = arr.map((v) => `'${v}'`).join(',');
471
+
472
+ // // Build SQL condition dynamically
473
+ // searchCondition += ` AND ${column} ${operator} (${valueList})`;
474
+ // });
475
+
476
+ // // Reset normal form parameters
477
+ // const resetValues = {};
478
+ // Object.keys(values).forEach((key) => {
479
+ // resetValues[key] = null;
480
+ // });
481
+
482
+ // // Final values sent to API
483
+ // finalValues = {
484
+ // ...resetValues,
485
+ // search_condition: searchCondition,
486
+ // };
487
+ // }
488
+
489
+ // const { pageSize } = pagination;
490
+ // const resetPage = 1;
491
+
492
+ // scriptId.current = null;
360
493
 
494
+ // // Update URL only when NOT using advanced search
495
+ // if (!hasSearchValues) {
496
+ // const currentUrlParams = Location.search();
497
+ // const { script_id, selected_card, ...cleanParams } = currentUrlParams;
498
+
499
+ // const newParams = new URLSearchParams({
500
+ // ...cleanParams,
501
+ // current: resetPage,
502
+ // pageSize,
503
+ // });
504
+
505
+ // const newUrl = `${window.location.pathname}?${newParams.toString()}`;
506
+ // window.history.replaceState({}, '', newUrl);
507
+ // }
508
+
509
+ // // Call report API
510
+ // onFinish(finalValues, resetPage);
511
+ // };
361
512
  /**
362
513
  *
363
514
  * @param {*} values
364
515
  */
365
516
 
366
- const onFinish = async (values, resetPage, inputParamsString) => {
517
+ const onFinish = async (values, resetPage, inputParamsString, parsedColumns) => {
367
518
  setLoading(true);
368
519
  setCardLoading(true);
369
520
  // // Check if the dashboard is visible and both start and end values are provided
@@ -423,7 +574,7 @@ export default function ReportingDashboard({
423
574
 
424
575
  // Call API
425
576
  try {
426
- await fetchReportData(id, values, dbPtr, paginationData);
577
+ await fetchReportData(id, values, dbPtr, paginationData, parsedColumns);
427
578
  } finally {
428
579
  setLoading(false);
429
580
  setCardLoading(false);
@@ -448,23 +599,6 @@ export default function ReportingDashboard({
448
599
  <Card className="reporting-dashboard card card-shadow">
449
600
  {/** If dashBoardIds exist and contain elements, render MenuDashBoard*/}
450
601
 
451
- {/* Page Header */}
452
- {/* <div className="page-header">
453
- <div>
454
- <Title style={{ marginBottom: '0px' }} level={4}>
455
- {config.caption || 'Report'}
456
- </Title>
457
- </div>
458
-
459
- <div className="right">
460
- <div className="date-and-fltr">
461
- <Button onClick={refresh} type="secondary" size={'small'}>
462
- <ReloadOutlined />
463
- </Button>
464
- </div>
465
- </div>
466
- </div> */}
467
- {/* Page Header Ends */}
468
602
  {dashBoardIds?.length > 0 ? (
469
603
  <MenuDashBoardComponent
470
604
  dashBoardIds={dashBoardIds} //Pass the available dashboard IDs to the componen
@@ -534,6 +668,24 @@ export default function ReportingDashboard({
534
668
  />
535
669
  ) : null}
536
670
  {/* </Card> */}
671
+ <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap', marginTop: 10 }}>
672
+ {searchParameters.length > 0
673
+ ? searchParameters.map((param) => (
674
+ <AdvancedSearchSelect
675
+ key={param.field}
676
+ parameter={param}
677
+ field={param}
678
+ value={searchValues[param.field] || []}
679
+ onChange={(value) => {
680
+ setSearchValues((prev) => ({
681
+ ...prev,
682
+ [param.field]: value,
683
+ }));
684
+ }}
685
+ />
686
+ ))
687
+ : null}
688
+ </div>
537
689
  </div>
538
690
 
539
691
  {/** GuestList component start*/}
@@ -617,266 +769,14 @@ function GuestList({
617
769
  const { isMobile, dispatch } = useContext(GlobalContext);
618
770
  const [single, setSingle] = useState({});
619
771
 
620
- const getRedirectLink = (entry, record) => {
621
- let redirectLink = entry.redirect_link;
622
- if (entry.replace_variables) {
623
- entry.replace_variables.forEach((replacement) => {
624
- const value = record[replacement.field] || '';
625
- redirectLink = redirectLink.replace(new RegExp(`@${replacement.field};`, 'g'), value);
626
- });
627
- }
628
- return redirectLink;
629
- };
630
-
631
772
  // const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
632
- const cols = [
633
- ...[
634
- {
635
- title: '#',
636
- dataIndex: 'index',
637
- render: (value, item, index) => index + 1,
638
- key: 'ColumnIndex',
639
- fixed: isFixedIndex ? 'left' : null,
640
- },
641
- ],
642
- ...columns.map((entry) => {
643
- // if (entry.sort) {
644
- // return {
645
- // render: (record) => {
646
-
647
- // if (entry.render) {
648
-
649
- // return entry.render(record);
650
- // } else {
651
- // return record[entry.dataIndex]
652
- // }
653
- // },
654
- // title: entry.title,
655
- // key: entry.field,
656
- // sorter: (a, b) => entry.sort(a, b),
657
- // sortDirections: ['ascend', 'descend', 'ascend'],
658
- // };
659
- // } else {
660
- return {
661
- render: (record) => {
662
- let textColor = 'inherit';
663
-
664
- if (entry.color) {
665
- textColor = entry.color;
666
- }
667
- /** We can have x types of components that is to be rendered here */
668
-
669
- /**1. Column Data */
670
-
671
- /**2. Action */
672
-
673
- /**3. Custom Component - In future . */
674
-
675
- if (entry.render) {
676
- return entry.render(record);
677
-
678
- // The type of component can be differentiated by type/ we can also reuse
679
- // any field present in the columns to avoid any additional field
680
- } else if (entry.type === 'link') {
681
- //Cheacking type of action to be done ie,If it contains a type then it will match with the record
682
- // for example initally we are implementing it for a type link
683
- // ie, we will return a link in query with the field as link and type link in display_columns
684
- //So here we match the field returned by query with the type in display_columns
685
-
686
- if (record[entry.field]) {
687
- return (
688
- <a href={record[entry.field]} target="_blank" rel="noopener noreferrer">
689
- View
690
- </a>
691
- );
692
- }
693
- } else if (entry.field === 'action') {
694
- let redirectLink = entry.redirect_link;
695
-
696
- // The variables to be replaced can be maintained
697
- // as a configuration in the entry or the column configuration
698
-
699
- // We iterate through all the variables that are present in the configuration
700
- // and replace the link to generate the final link
701
-
702
- entry.replace_variables.forEach((replacement) => {
703
- redirectLink = redirectLink.replace(new RegExp('@' + replacement.field + ';', 'g'), record[replacement.field]);
704
- });
705
-
706
- return <Link to={`${redirectLink}`}>{entry.display_name_link ? entry.display_name_link : 'View'}</Link>;
707
- } else if (entry.field === 'custom') {
708
- // Make all the components in modules available for use in custom column of core script
709
- // var genericComponents = require('./../../../../../../../nura-api-new/nura-desk/src/modules');
710
- var genericComponents = CustomComponents;
711
-
712
- // Arrive the component name
713
- let componentName = entry.component;
714
-
715
- let LoadedComponent = null;
716
- // If there is custom components mensioned in the display_columns,
717
- // then matching the custom components with the generic components
718
- if (componentName) {
719
- if (componentName && genericComponents[componentName]) {
720
- LoadedComponent = genericComponents[componentName];
721
- }
722
- }
723
-
724
- let propValue = {};
725
- // If there is props value
726
- if (entry.props) {
727
- // Looping the props from the props mensioned in display_columns,
728
- // Matching the field of record props field with the and return the
729
- entry.props.forEach((values) => {
730
- // Matching the fields of record with the props field
731
- let valueCreation = record[values.field];
732
- // Returning the values of field matched by fields
733
- propValue[values.value] = valueCreation;
734
- });
735
- }
736
-
737
- return (
738
- <LoadedComponent
739
- // Configuration will define
740
- {...entry.config}
741
- callback={() => {
742
- refresh();
743
- }}
744
- // record={record}
745
-
746
- {...record}
747
- // assigning the props
748
- {...propValue}
749
- />
750
- );
751
- } else {
752
- // Check if both `color_code` exists in the record and `enableColor` is true
753
- if (record.color_code && entry.enableColor) {
754
- // If the column type is 'tag', render the field inside an Ant Design <Tag> with color
755
- if (entry.columnType === 'tag') {
756
- return <Tag color={record.color_code}>{record[entry.field]}</Tag>;
757
-
758
- // If the column type is 'span', render the field inside a <span> with inline color style
759
- } else if (entry.columnType === 'span') {
760
- return <span style={{ color: record.color_code, overflowWrap: 'break-word', WebkitLineClamp: 3 }}>{record[entry.field]}</span>;
761
- }
762
- } else {
763
- /**
764
- * This code dynamically displays icons based on data and a configuration
765
- * When a column's configuration includes a displayIcons JSON, the code will check the corresponding data field.
766
- * If the field's value (e.g., "Pending") matches a key in that JSON,
767
- * the system will display the specified icon with its defined color and size.
768
- */
769
- if (entry.displayIcons) {
770
- // Get the field value
771
- const fieldValue = record[entry.field]?.toString();
772
-
773
- // Get the configuration for the icon from the JSON.
774
- const displayConfig = entry.displayIcons[fieldValue];
775
- if (displayConfig) {
776
- // Look up the actual component from our iconMap using the name from the JSON.
777
- const DynamicIcon = Icons[displayConfig.icon];
778
- // If a component is found, render it with the specified color and size.
779
- if (DynamicIcon) {
780
- return (
781
- <DynamicIcon
782
- style={{
783
- color: displayConfig.color,
784
- fontSize: displayConfig.size,
785
- }}
786
- />
787
- );
788
- }
789
- }
790
- } else {
791
- //If the value is neither 'Y' nor 'N', return the actual field value
792
- return <span style={{ color: textColor, whiteSpace: 'pre-wrap', overflowWrap: 'break-word' }}>{record[entry.field]}</span>;
793
- }
794
- }
795
- }
796
- },
797
- field: entry.field,
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
- ),
809
- key: entry.field,
810
- width: entry.width ? parseInt(entry.width) : 160,
811
- fixed: entry.isFixedColumn ? entry.isFixedColumn : null, // Conditionally setting the 'fixed' key to 'left' if 'isColumnStatic' is true; otherwise, setting it to null.
812
- // Check if filtering is enabled and patients is an array
813
- filters:
814
- entry.isFilterEnabled && Array.isArray(patients)
815
- ? [...new Set(patients.map((item) => item[entry.field]).filter(Boolean))].map((value) => ({ text: value, value }))
816
- : null,
817
- // Apply the filter only if it's enabled for the column
818
- onFilter: entry.isFilterEnabled ? (value, record) => record[entry.field] === value : null,
819
- //If sorting is enabled for this entry, provide a sorter function
820
- sorter: entry.isSortingEnabled ? (a, b) => String(a[entry.field]).localeCompare(String(b[entry.field])) : null,
821
-
822
- // Apply the filter search
823
- filterSearch: entry.isFilterEnabled ? entry.isFilterEnabled : false,
824
- exportDefinition: (record) => {
825
- //Custom components should not be downloaded
826
- // return entry.field === 'custom' ? null : record[entry.field];
827
- if (entry.field === 'custom') {
828
- // Find the field key in props that corresponds to 'description'
829
- const description = entry.props?.find((p) => p.value === 'description')?.field;
830
-
831
- // Return the value from record.props if it exists
832
- return description && record[description] ? record[description] : null;
833
- }
834
- return record[entry.field];
835
- },
836
- // Add align property based on column type
837
- align: entry.type === 'number' ? 'right' : 'left',
838
- };
839
- // }
840
- }),
841
- // ...[
842
- // {
843
- // title: '',
844
- // key: 'action',
845
- // render: (text, record) => {
846
- // let detail = model.slice(0, model.length - 1);
847
-
848
- // return (
849
- // <Space size="middle">
850
- // {!schema.hideView && !actions.length ? <Link to={`/${city}/${model}/${text.id}`}>View</Link> : null}
851
-
852
- // {actions.map((action) => (
853
- // <Link to={action.url(record)}>{action.caption}</Link>
854
- // ))}
855
- // </Space>
856
- // );
857
- // },
858
- // },
859
- // ],
860
-
861
- // ...[
862
- // {
863
- // title: '',
864
- // key: 'action',
865
- // render: (text, record) => {
866
- // let detail = model.slice(0, model.length - 1);
867
-
868
- // return (
869
- // <Link to={`${menu.path.replace(':id',visitid)}?op_no=${OpNo}`}>
870
- // <Button size={'small'}>
871
- // <PlayCircleOutlined />
872
- // Go to Menu
873
- // </Button>
874
- // </Link>
875
- // );
876
- // },
877
- // },
878
- // ],
879
- ];
773
+ const cols = buildDisplayColumns({
774
+ columns,
775
+ patients,
776
+ isFixedIndex,
777
+ CustomComponents,
778
+ refresh,
779
+ });
880
780
 
881
781
  /**
882
782
  *
@@ -903,13 +803,13 @@ function GuestList({
903
803
  useEffect(() => {
904
804
  //Cheaking if there is patient data exists
905
805
  if (patients) {
906
- let data = patients?.map((entry) => {
907
- entry.rowIndex = entry.opb_id;
806
+ // let data = patients?.map((entry) => {
807
+ // entry.rowIndex = entry.opb_id;
908
808
 
909
- entry.dispatch = dispatch;
809
+ // entry.dispatch = dispatch;
910
810
 
911
- return entry;
912
- });
811
+ // return entry;
812
+ // });
913
813
 
914
814
  // setData(data);
915
815
 
@@ -974,7 +874,7 @@ function GuestList({
974
874
  setRedirectPatient(matched);
975
875
  message.success(`Match found for ${code}, redirecting...`);
976
876
 
977
- const actionColumn = columns.find((col) => col.field === 'action');
877
+ const actionColumn = columns.find((col) => col.field === 'action') || columns.find((col) => col.type === 'action');
978
878
  if (actionColumn) {
979
879
  const redirectLink = getRedirectLink(actionColumn, patient);
980
880
  // history.push(redirectLink);
@@ -1066,12 +966,6 @@ function GuestList({
1066
966
  </div>
1067
967
 
1068
968
  <div>
1069
- {/* {view ? (
1070
- <>
1071
- <CardList dataSource={data} columns={columns} />
1072
- </>
1073
- ) : (
1074
- <> */}
1075
969
  <Card>
1076
970
  {loading ? (
1077
971
  <>
@@ -1080,10 +974,12 @@ function GuestList({
1080
974
  ) : (
1081
975
  <TableComponent
1082
976
  size="small"
1083
- scroll={{ x: 'max-content' }}
977
+ // scroll={{ x: 'max-content' }}
978
+ scroll={{ x: 'max-content', y: '60vh' }}
1084
979
  rowKey={(record) => record.OpNo}
1085
980
  dataSource={filtered ? filtered : patients} // In case if there is no filtered values we can use patient data
1086
981
  columns={cols}
982
+ sticky
1087
983
  pagination={false}
1088
984
  // title={config.caption}
1089
985
  summary={(pageData) => {
@@ -1152,53 +1048,3 @@ function GuestList({
1152
1048
  </>
1153
1049
  );
1154
1050
  }
1155
-
1156
- // //Mobile view card Section
1157
- // function CardList({ dataSource, url }) {
1158
- // const { user = {}, dispatch } = useContext(GlobalContext);
1159
-
1160
- // function onClick(item) {
1161
- // Location.navigate({
1162
- // url: `/lab-detail/${item.BillID}`,
1163
- // });
1164
-
1165
- // dispatch({ type: 'index', payload: item.rowIndex });
1166
- // }
1167
-
1168
- // return dataSource.map((item, index) => {
1169
- // // to={`/lab-detail/${item.BillID}`}
1170
- // return (
1171
- // <div
1172
- // key={index}
1173
- // className="report-item"
1174
- // onClick={() => {
1175
- // onClick(item);
1176
- // }}
1177
- // >
1178
- // <GuestCard record={item} />
1179
- // </div>
1180
- // );
1181
- // });
1182
- // }
1183
-
1184
- // function GuestCard({ record }) {
1185
- // return (
1186
- // <Card className="card vehicle-card">
1187
- // <div className="card">
1188
- // <h2 className="title">{record.PName}</h2>
1189
-
1190
- // <h4 className="values">{record.OpNo}</h4>
1191
-
1192
- // <h3 className="values">{record.Test}</h3>
1193
-
1194
- // <h3 className="values">Primary Result : {record.PrimaryResult || 'Pending'}</h3>
1195
-
1196
- // <Text type="secondary">{record.Mobile}</Text>
1197
-
1198
- // <h4 className="values">{record.Date}</h4>
1199
- // </div>
1200
- // </Card>
1201
- // );
1202
- // }
1203
- // );
1204
- // }