ui-soxo-bootstrap-core 2.6.1-dev.26 → 2.6.1-dev.28

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.
@@ -1,6 +1,6 @@
1
1
  import React, { useState, useEffect, useContext, useRef } from 'react';
2
2
 
3
- import { Table, Skeleton, Input, Modal, message, Pagination } from 'antd';
3
+ import { Table, Skeleton, Input, Modal, message, Pagination, Tag } from 'antd';
4
4
 
5
5
  import { QrcodeOutlined } from '@ant-design/icons';
6
6
 
@@ -56,7 +56,7 @@ export default function ReportingDashboard({
56
56
  const [config, setConfig] = useState({});
57
57
 
58
58
  // State to manage the layout of the form
59
- const [formLayout, setFormLayout] = useState('inline');
59
+ const [formLayout, setFormLayout] = useState('vertical');
60
60
 
61
61
  const [loading, setLoading] = useState(true);
62
62
 
@@ -69,6 +69,7 @@ export default function ReportingDashboard({
69
69
  const [dashboardVisible, setDashBoardVisible] = useState(false);
70
70
 
71
71
  const [formContents, setformContents] = useState({});
72
+ const [liveFormContents, setLiveFormContents] = useState({});
72
73
 
73
74
  const scriptId = useRef(null);
74
75
 
@@ -116,7 +117,7 @@ export default function ReportingDashboard({
116
117
  }
117
118
  setColumns(parsedColumns);
118
119
 
119
- await prepareInputParameters(result, parsedColumns);
120
+ await prepareInputParameters(result, parsedColumns, fetchId);
120
121
 
121
122
  if (result.summary_columns) {
122
123
  setSummaryColumns(JSON.parse(result.summary_columns));
@@ -161,7 +162,7 @@ export default function ReportingDashboard({
161
162
  */
162
163
 
163
164
  //Prepare input parameters by mapping default values and binding models if needed
164
- async function prepareInputParameters(record, parsedColumns) {
165
+ async function prepareInputParameters(record, parsedColumns, fetchId) {
165
166
  setLoading(true);
166
167
  let urlParams = Location.search();
167
168
 
@@ -170,10 +171,10 @@ export default function ReportingDashboard({
170
171
 
171
172
  let otherDetails = record.other_details1 ? JSON.parse(record.other_details1) : null;
172
173
 
173
- parameters = record.input_parameters ? JSON.parse(record.input_parameters) : null;
174
+ parameters = record.input_parameters ? JSON.parse(record.input_parameters) : [];
174
175
 
175
176
  let formContent = {};
176
- const searchFields = parameters.filter((p) => p.type === 'search' && p.search_enabled === 'yes');
177
+ const searchFields = (parameters || []).filter((p) => p.type === 'search' && p.search_enabled === 'yes');
177
178
  setSearchParameters([...searchFields]);
178
179
 
179
180
  parameters = await parameters?.map((record) => {
@@ -218,13 +219,15 @@ export default function ReportingDashboard({
218
219
  if (record.type === 'date' && !formContent[record.field]) {
219
220
  formContent[record.field] = moment().tz(process.env.REACT_APP_TIMEZONE);
220
221
  }
221
- // if (record.type === 'search') {
222
- // return {
223
- // ...record,
224
- // component: AdvancedSearchSelect,
225
- // required: record.required,
226
- // };
227
- // }
222
+ if (record.type === 'search') {
223
+ if (!formContent[record.field]) formContent[record.field] = [];
224
+ return {
225
+ ...record,
226
+ reportId: fetchId,
227
+ onReset: () => getPatientDetails(fetchId),
228
+ required: record.required,
229
+ };
230
+ }
228
231
  if (['reference-select', 'reference-search', 'select'].indexOf(record.type) !== -1) {
229
232
  // let model = "";
230
233
  let model = CustomModels[record.modelName];
@@ -243,6 +246,7 @@ export default function ReportingDashboard({
243
246
  });
244
247
  // Update form content state
245
248
  setformContents(formContent);
249
+ setLiveFormContents(formContent);
246
250
 
247
251
  // Trigger form submission
248
252
  onFinish(formContent, null, record.input_parameters, parsedColumns);
@@ -254,12 +258,8 @@ export default function ReportingDashboard({
254
258
  // If enabled, clear the details array
255
259
  setDetails([]);
256
260
  } else {
257
- //// Filter the array "parameters" and keep only elements where "type" is present (truthy)
258
- //Keep only parameters that have a "type" → used for UI display
259
- let filter = parameters.filter((ele) => ele.type && ele.type !== 'search');
260
- // Update the "details" state with the filtered results
261
-
262
- setDetails([...filter]);
261
+ // Keep all parameters with a type (including search) to render in FormCreator
262
+ setDetails([...parameters.filter((ele) => ele.type)]);
263
263
  }
264
264
  }
265
265
 
@@ -345,16 +345,29 @@ export default function ReportingDashboard({
345
345
  };
346
346
 
347
347
  const handleSubmit = (values) => {
348
- const hasSearchValues = Object.values(searchValues).some((v) => Array.isArray(v) && v.length > 0);
348
+ // Extract search fields from the form values
349
+ const searchKeys = searchParameters.map((p) => p.field);
350
+ const currentSearchValues = {};
351
+ const formValues = {};
352
+
353
+ Object.keys(values).forEach((key) => {
354
+ if (searchKeys.includes(key)) {
355
+ currentSearchValues[key] = values[key];
356
+ } else {
357
+ formValues[key] = values[key];
358
+ }
359
+ });
360
+
361
+ const hasSearchValues = Object.values(currentSearchValues).some((v) => Array.isArray(v) && v.length > 0);
349
362
 
350
363
  if (!hasSearchValues) {
351
- runSubmit(values);
364
+ runSubmit(formValues);
352
365
  return;
353
366
  }
354
367
 
355
- // Check if form values changed (date or others)
356
- const formChanged = Object.keys(values).some((key) => {
357
- const newVal = values[key];
368
+ // Check if main form values (non-search) changed
369
+ const formChanged = Object.keys(formValues).some((key) => {
370
+ const newVal = formValues[key];
358
371
  const oldVal = formContents[key];
359
372
 
360
373
  if (moment.isMoment(newVal) && moment.isMoment(oldVal)) {
@@ -374,8 +387,8 @@ export default function ReportingDashboard({
374
387
  onOk() {
375
388
  // YES → send form values + search condition
376
389
  const finalValues = {
377
- ...values,
378
- search_values: searchValues,
390
+ ...formValues,
391
+ search_values: currentSearchValues,
379
392
  };
380
393
 
381
394
  runSubmit(finalValues);
@@ -384,13 +397,13 @@ export default function ReportingDashboard({
384
397
  onCancel() {
385
398
  // NO → reset form values
386
399
  const resetValues = {};
387
- Object.keys(values).forEach((key) => {
400
+ Object.keys(formValues).forEach((key) => {
388
401
  resetValues[key] = null;
389
402
  });
390
403
 
391
404
  const finalValues = {
392
405
  ...resetValues,
393
- search_values: searchValues,
406
+ search_values: currentSearchValues,
394
407
  };
395
408
 
396
409
  runSubmit(finalValues);
@@ -399,13 +412,13 @@ export default function ReportingDashboard({
399
412
  } else {
400
413
  // no form change
401
414
  const resetValues = {};
402
- Object.keys(values).forEach((key) => {
415
+ Object.keys(formValues).forEach((key) => {
403
416
  resetValues[key] = null;
404
417
  });
405
418
 
406
419
  const finalValues = {
407
420
  ...resetValues,
408
- search_values: searchValues,
421
+ search_values: currentSearchValues,
409
422
  };
410
423
 
411
424
  runSubmit(finalValues);
@@ -436,6 +449,24 @@ export default function ReportingDashboard({
436
449
 
437
450
  onFinish(finalValues, resetPage);
438
451
  };
452
+
453
+ const selectedSearchFields = details.filter((field) => {
454
+ if (field.type !== 'search') return false;
455
+
456
+ const value = liveFormContents[field.field];
457
+ return Array.isArray(value) ? value.length > 0 : !!value;
458
+ });
459
+
460
+ const handleRemoveSearchField = (fieldName) => {
461
+ const updatedValues = {
462
+ ...liveFormContents,
463
+ [fieldName]: [],
464
+ };
465
+
466
+ setLiveFormContents(updatedValues);
467
+ handleSubmit(updatedValues);
468
+ };
469
+
439
470
  /**
440
471
  *
441
472
  * @param {*} values
@@ -468,6 +499,12 @@ export default function ReportingDashboard({
468
499
  //Getting url friendly value by matching the url values and input_parameter values
469
500
  let value = values[parameter.field];
470
501
 
502
+ // If the value is missing at the root level (which happens when search fields are moved
503
+ // into search_values during submission), try to retrieve it from the nested object.
504
+ if (value === undefined && values.search_values && values.search_values[parameter.field] !== undefined) {
505
+ value = values.search_values[parameter.field];
506
+ }
507
+
471
508
  // Keep Moment object in state for picker
472
509
  if (parameter.type === 'date' && value) {
473
510
  formContent[parameter.field] = value.isValid ? value : moment(value); // ensure Moment
@@ -489,6 +526,7 @@ export default function ReportingDashboard({
489
526
  );
490
527
 
491
528
  setformContents(formContent);
529
+ setLiveFormContents(formContent);
492
530
  Location.search({ ...Location.search(), ...filteredParams });
493
531
  }
494
532
 
@@ -584,62 +622,18 @@ export default function ReportingDashboard({
584
622
  }}
585
623
  styles={{ paddingRight: '15px', alignItems: 'center' }}
586
624
  fields={details}
625
+ reportId={id}
587
626
  formContent={formContents}
588
627
  // formContent={{ [model]: {} }}
589
628
  modelIndex="requestId"
590
629
  model={model}
591
630
  onSubmit={handleSubmit}
631
+ onFormValuesChange={setLiveFormContents}
592
632
  callback={() => {
593
633
  // history.goBack();
594
634
  }}
595
635
  />
596
636
  ) : null}
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>
643
637
  </div>
644
638
 
645
639
  {/** GuestList component start*/}
@@ -657,6 +651,8 @@ export default function ReportingDashboard({
657
651
  pagination={pagination}
658
652
  handlePagination={handlePagination}
659
653
  attributes={attributes}
654
+ selectedSearchFields={selectedSearchFields}
655
+ handleRemoveSearchField={handleRemoveSearchField}
660
656
  fetchReportData={(paginationUpdate) => fetchReportData(id, formContents, dbPtr, paginationUpdate || pagination)}
661
657
  />
662
658
  {/** GuestList component end*/}
@@ -690,6 +686,8 @@ function GuestList({
690
686
  pagination,
691
687
  handlePagination,
692
688
  attributes,
689
+ selectedSearchFields,
690
+ handleRemoveSearchField,
693
691
  fetchReportData,
694
692
  }) {
695
693
  /**
@@ -722,7 +720,6 @@ function GuestList({
722
720
 
723
721
  const { isMobile, dispatch } = useContext(GlobalContext);
724
722
  const [single, setSingle] = useState({});
725
- const otherDetails = config.other_details1 ? JSON.parse(config.other_details1) : {};
726
723
 
727
724
  // const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
728
725
  const cols = buildDisplayColumns({
@@ -731,7 +728,6 @@ function GuestList({
731
728
  isFixedIndex,
732
729
  CustomComponents,
733
730
  refresh,
734
- otherDetails,
735
731
  });
736
732
 
737
733
  /**
@@ -863,51 +859,35 @@ function GuestList({
863
859
  const handleCloseEdit = () => {
864
860
  setShowEdit(false);
865
861
  };
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
- }
900
862
  return (
901
863
  <>
902
864
  <div className="table-header">
903
865
  <div className="table-left">
904
- {/* shwoing caption is not correct so this commented */}
905
- {/* <span className="menu-caption">{config.caption}</span> */}
906
- <Search placeholder="Enter Search Value" allowClear onChange={onSearch} />
907
- {/* <p className="size-hint">{patients.length} records.</p> */}
866
+ {/* {selectedSearchFields?.length > 0 ? (
867
+ <div className="search-tags-container">
868
+ {selectedSearchFields.map((field) => (
869
+ <Tag key={field.field} closable color="blue" onClose={() => handleRemoveSearchField(field.field)}>
870
+ {field.caption}
871
+ </Tag>
872
+ ))}
873
+ </div>
874
+ ) : null} */}
908
875
  </div>
909
876
 
910
877
  <div className="table-right">
878
+ {/* shwoing caption is not correct so this commented */}
879
+ {/* <span className="menu-caption">{config.caption}</span> */}
880
+ <Search className="table-search-input" placeholder="Enter Search Value" allowClear onChange={onSearch} />
881
+ <div className="table-export-button">
882
+ {exportData.exportDatas && (
883
+ <ExportReactCSV
884
+ title={config.caption}
885
+ headers={exportData.exportDatas.exportDataHeaders}
886
+ csvData={exportData.exportDatas.exportDataColumns}
887
+ />
888
+ )}
889
+ </div>
890
+
911
891
  {/* QR Scan start */}
912
892
  {showScanner ? (
913
893
  <Button size="small" type="primary" icon={<QrcodeOutlined />} onClick={() => setScannerVisible(true)}>
@@ -940,18 +920,7 @@ function GuestList({
940
920
  <Modal open={isScannerVisible} title="Scan QR Code" footer={null} onCancel={() => setScannerVisible(false)} destroyOnClose>
941
921
  <QrScanner onScanSuccess={handleScanSuccess} onClose={() => setScannerVisible(false)} />
942
922
  </Modal>
943
-
944
- <div>
945
- {/* QR Scan End */}
946
- {/*table data export to csc component*/}
947
- {exportData.exportDatas && (
948
- <ExportReactCSV
949
- title={config.caption}
950
- headers={exportData.exportDatas.exportDataHeaders}
951
- csvData={exportData.exportDatas.exportDataColumns}
952
- />
953
- )}
954
- </div>
923
+ {/* QR Scan End */}
955
924
  </div>
956
925
  </div>
957
926
 
@@ -972,131 +941,46 @@ function GuestList({
972
941
  sticky
973
942
  pagination={false}
974
943
  // 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
-
1069
944
  summary={(pageData) => {
1070
- const summaryCols = columns.filter((col) => col.enable_summary);
1071
- if (!summaryCols.length) return null;
1072
-
1073
- const summaryValues = calculateSummaryValues(summaryCols, pageData);
945
+ // Variable to save the summary data
946
+ let summary = {};
947
+
948
+ let summaryColumns = [
949
+ { field: 'opb_amt', title: 'Amount' },
950
+ { field: 'opb_netamt', title: 'Net Amount' },
951
+ ];
952
+
953
+ let tableColumns = cols;
954
+
955
+ // Creating a copy of columns to append the summary configuration that is needed to set
956
+ tableColumns.forEach((record, index) => {
957
+ summaryColumns.forEach((inner) => {
958
+ if (record.field === inner.field) {
959
+ tableColumns[index].summary = inner;
960
+ }
961
+ });
962
+ });
963
+
964
+ // Initialize
965
+ summaryColumns.map((item) => {
966
+ return (summary[item.field] = 0);
967
+ });
968
+
969
+ // Find the total
970
+ summaryColumns.map((item) => {
971
+ pageData.forEach((entry) => {
972
+ return (summary[item.field] = summary[item.field] + entry[item.field]);
973
+ });
974
+ });
1074
975
 
1075
976
  return (
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>
977
+ <>
978
+ <Table.Summary.Row>
979
+ {tableColumns.map((column, key) => {
980
+ return <Table.Summary.Cell key={key}>{column.summary ? <>{summary[column.summary.field]}</> : null}</Table.Summary.Cell>;
981
+ })}
982
+ </Table.Summary.Row>
983
+ </>
1100
984
  );
1101
985
  }}
1102
986
  />
@@ -16,12 +16,17 @@
16
16
  display: flex;
17
17
  justify-content: space-between;
18
18
  align-items: center;
19
+ gap: 12px;
19
20
  padding-bottom: 10px;
20
21
  padding-top: 10px;
21
22
 
22
23
  .table-left {
23
24
  display: flex;
24
25
  align-items: center;
26
+ gap: 8px;
27
+ flex: 1;
28
+ min-width: 0;
29
+ margin-left: 6px;
25
30
 
26
31
  .menu-caption {
27
32
  min-width: 100px;
@@ -31,9 +36,23 @@
31
36
 
32
37
  .table-right{
33
38
  display: flex;
39
+ align-items: center;
40
+ justify-content: flex-end;
41
+ flex-wrap: nowrap;
34
42
  gap: 4px;
35
43
  }
36
44
  }
45
+
46
+ .table-search-input {
47
+ width: 220px;
48
+ min-width: 220px;
49
+ }
50
+
51
+ .table-export-button {
52
+ display: flex;
53
+ align-items: center;
54
+ flex-shrink: 0;
55
+ }
37
56
 
38
57
 
39
58
  .form-card {
@@ -48,6 +67,23 @@
48
67
  padding: 0px;
49
68
  }
50
69
 
70
+ .search-tags-container {
71
+ display: flex;
72
+ align-items: center;
73
+ flex-wrap: nowrap;
74
+ gap: 8px;
75
+ min-width: 0;
76
+ max-width: 100%;
77
+ overflow-x: auto;
78
+ overflow-y: hidden;
79
+ white-space: nowrap;
80
+ }
81
+
82
+ .search-tags-container .ant-tag {
83
+ flex-shrink: 0;
84
+ margin-inline-end: 0;
85
+ }
86
+
51
87
  // margin: 0px 10px;
52
88
 
53
89
  .ant-skeleton {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.6.1-dev.26",
3
+ "version": "2.6.1-dev.28",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"