ui-soxo-bootstrap-core 2.6.19 → 2.6.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.
@@ -10,6 +10,8 @@
10
10
  import React from 'react';
11
11
  // Import XLSX library to handle Excel file creation
12
12
  import * as XLSX from 'xlsx';
13
+ // import * as XLSX from 'xlsx-js-style';
14
+ // import * as XLSX from 'xlsx-js-style';
13
15
  // Import saveAs from file-saver to trigger file download in browser
14
16
  import { saveAs } from 'file-saver';
15
17
  import { Button } from 'antd';
@@ -30,7 +32,27 @@ export const ExportReactCSV = ({ csvData, headers, fileName,title }) => {
30
32
  // Extract label names for the header row
31
33
  const headerLabels = headers.map((h) => h.label);
32
34
  // Map data rows according to the headers' key order
33
- const rows = csvData.map((row) => headers.map((h) => row[h.key]));
35
+ const rows = csvData.map((row) =>
36
+ headers.map((h, hIndex) => {
37
+ let value = row[h.key];
38
+ // If it's a summary row, return a cell object with bold styling
39
+ if (row.isSummaryRow) {
40
+ // Remove the index value (typically the first column) for the summary row
41
+ if (hIndex === 0) value = "";
42
+
43
+ return {
44
+ v: value,
45
+ s: {
46
+ font: { bold: true },
47
+ // "Thick" visual indicator via border
48
+ // Visual indicator via border
49
+ border: { bottom: { style: "thick", color: { rgb: "000000" } } }
50
+ }
51
+ };
52
+ }
53
+ return value;
54
+ })
55
+ );
34
56
  // Create a worksheet from an array of arrays (header + rows)
35
57
  const data = [[title || "Report"], [], headerLabels, ...rows];
36
58
  // worksheet = XLSX.utils.aoa_to_sheet([headerLabels, ...rows]);
@@ -44,7 +66,11 @@ export const ExportReactCSV = ({ csvData, headers, fileName,title }) => {
44
66
  // Append the worksheet to the workbook with the name 'Sheet1'
45
67
  XLSX.utils.book_append_sheet(workbook, worksheet, 'Sheet1');
46
68
  // Write workbook to a buffer as an array
47
- const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
69
+ const excelBuffer = XLSX.write(workbook, {
70
+ bookType: 'xlsx',
71
+ type: 'array',
72
+ cellStyles: true, // Required to export styles
73
+ });
48
74
  // Create a Blob object from the buffer with correct MIME type
49
75
  const blob = new Blob([excelBuffer], {
50
76
  type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
@@ -306,6 +306,8 @@ function LoginPhone({ history, appSettings }) {
306
306
  *
307
307
  */
308
308
  const sendAuthenticationOtp = () => {
309
+ if (loading) return;
310
+
309
311
  // Reset previous error
310
312
  setModeError(false);
311
313
 
@@ -445,6 +447,8 @@ function LoginPhone({ history, appSettings }) {
445
447
  * Otp resend Logic
446
448
  */
447
449
  const handleResendOTP = () => {
450
+ if (loading) return;
451
+
448
452
  setOtpValue('');
449
453
  setModeError(false);
450
454
  setOtpError(false);
@@ -714,7 +718,7 @@ function LoginPhone({ history, appSettings }) {
714
718
 
715
719
  {!otpVerification ? (
716
720
  <div className="otp-container">
717
- <Button type="primary" onClick={sendAuthenticationOtp} style={{ marginTop: '6px' }}>
721
+ <Button loading={loading} type="primary" onClick={sendAuthenticationOtp} style={{ marginTop: '6px' }}>
718
722
  Send OTP
719
723
  </Button>
720
724
  </div>
@@ -760,7 +764,7 @@ function LoginPhone({ history, appSettings }) {
760
764
  </div>
761
765
  </div>
762
766
  {!otpSuccess && (
763
- <Button type="primary" disabled={otpExpired} onClick={verifyOtp} style={{ marginTop: 16 }}>
767
+ <Button loading={loading} type="primary" disabled={otpExpired} onClick={verifyOtp} style={{ marginTop: 16 }}>
764
768
  Verify OTP
765
769
  </Button>
766
770
  )}
@@ -1,4 +1,4 @@
1
- import { PostData, GetData, PutData, PatchData, DeleteData } from './../http/http.utils';
1
+ import { PostData, GetData, PutData, PatchData, DeleteData,UploadData } from './../http/http.utils';
2
2
 
3
3
  let headers = {};
4
4
 
@@ -124,33 +124,29 @@ export default class ApiUtils {
124
124
  });
125
125
  };
126
126
 
127
- static upload = ({ url, data }) => {
128
- return fetch(process.env.REACT_APP_endpoint + url, {
129
- // Your POST endpoint
130
- method: 'POST',
131
- headers: {
132
- // 'App-Type': 313,
133
- // 'App-Version': '1.0.1',
134
- Authorization: 'Bearer ' + localStorage.access_token,
135
- // type:'multipart/formData'
136
- },
137
- // credentials: 'include',
138
- body: data,
139
- }).then(
140
- (result) => {
141
- return result.json();
142
- },
143
- (error) => {
144
- console.log(error);
145
- return error;
146
- },
147
- (progress) => {
148
- console.log(progress);
149
- return progress;
150
- }
151
- );
127
+ // upload document
128
+ static upload = async ({ url, data, headers: customHeaders = {}, ...props }) => {
129
+ const dbPtr = localStorage.getItem('db_ptr');
130
+
131
+ // Ensure settings object exists to avoid undefined errors in ApiCall
132
+ const settings = {
133
+ headers: {},
134
+ getToken: () => localStorage.getItem('access_token')
135
+ };
136
+
137
+ return UploadData({
138
+ url,
139
+ formBody: data,
140
+ // Your FormData goes here
141
+ isUpload: true, // Indicate that this is an upload request
142
+ settings,
143
+ headers: {
144
+ ...(dbPtr ? { db_ptr: dbPtr } : {}),
145
+ ...customHeaders,
146
+ },
147
+ ...props,
148
+ });
152
149
  };
153
-
154
150
  /**
155
151
  * Get Auth Status
156
152
  *
@@ -31,7 +31,8 @@ export function getExportData(records, exportDataColumns) {
31
31
  const response = records.map((row, columnIndex) => {
32
32
 
33
33
  let entry = {
34
- 'Sl No': columnIndex + 1
34
+ 'Sl No': row.isSummaryRow ? '' : columnIndex + 1,
35
+ isSummaryRow: row.isSummaryRow
35
36
  };
36
37
 
37
38
  filteredColumns.forEach((column, indexValue) => {
@@ -70,24 +70,37 @@ export async function DeleteData({ url, ...props }) {
70
70
  *
71
71
  * @param {*} param
72
72
  */
73
-
74
73
  export async function ApiCall({ url, formBody, method, settings, ...props }) {
75
74
  const token = props.token || (await settings.getToken());
76
75
  const path = window.location.pathname;
77
76
  const baseUrl = props.baseUrl || process.env.REACT_APP_endpoint;
78
77
 
78
+ const isFormData = formBody instanceof FormData;
79
+
79
80
  const payload = {
80
81
  method,
81
82
  headers: {
82
83
  ...settings.headers,
83
- ...headers,
84
84
  ...(props.headers || {}),
85
85
  Authorization: `Bearer ${token}`,
86
- 'Content-Type': 'application/json',
87
86
  },
88
- body: formBody ? JSON.stringify(formBody) : null,
89
87
  };
90
88
 
89
+ // ✅ Upload + normal handling
90
+ if (method !== 'GET' && formBody) {
91
+ payload.body = formBody;
92
+
93
+ if (!isFormData && !(props.isUpload)) {
94
+ payload.body = JSON.stringify(formBody);
95
+ payload.headers['Content-Type'] = 'application/json';
96
+ }
97
+
98
+ // ❗ VERY IMPORTANT
99
+ if (isFormData || (props.isUpload)) {
100
+ delete payload.headers['Content-Type'];
101
+ }
102
+ }
103
+
91
104
  // 🟡 Handles both normal and retried responses
92
105
  const handleResponse = async (res) => {
93
106
  if (props.responseType === 'blob') {
@@ -156,3 +169,18 @@ export async function ApiCall({ url, formBody, method, settings, ...props }) {
156
169
  throw err;
157
170
  }
158
171
  }
172
+
173
+ /**
174
+ * Method for uploading documents
175
+ * @public
176
+ */
177
+ export async function UploadData({ url, formBody, ...props }) {
178
+ return ApiCall({
179
+ url,
180
+ formBody,
181
+ isUpload: true,
182
+ method: 'POST',
183
+ returnResponse: true,
184
+ ...props,
185
+ });
186
+ }
@@ -25,7 +25,7 @@ function getExportDefinition(entry, record) {
25
25
  * @param {Array} root0.columns
26
26
  * @param {Array} root0.patients
27
27
  * @param {boolean} root0.isFixedIndex
28
- * @param {Object} root0.CustomComponents
28
+ * @param {Object.<string, React.ComponentType<any>>} root0.CustomComponents
29
29
  * @param {Function} root0.refresh
30
30
  * @param {Object} [root0.otherDetails={}] - Optional details from the report configuration.
31
31
  * @param {boolean} [root0.otherDetails.isFilterEnabled] - Fallback to enable filtering on all columns.
@@ -298,7 +298,6 @@ export default function ReportingDashboard({
298
298
  }
299
299
  // Update patients
300
300
  setPatients(resultDetails || []);
301
- console.log(parsedColumns);
302
301
 
303
302
  // Check if columns are not yet defined
304
303
  if (parsedColumns.length === 0 && resultDetails.length > 0) {
@@ -601,6 +600,7 @@ function GuestList({
601
600
  const [single, setSingle] = useState({});
602
601
  const otherDetails = config.other_details1 ? JSON.parse(config.other_details1) : {};
603
602
 
603
+ // const otherDetails = config.other_details1 ? JSON.parse(config.other_details1) : {};
604
604
  // const [view, setView] = useState(isMobile ? true : false); //Need to check this condition
605
605
  const cols = buildDisplayColumns({
606
606
  columns,
@@ -608,7 +608,7 @@ function GuestList({
608
608
  isFixedIndex,
609
609
  CustomComponents,
610
610
  refresh,
611
- otherDetails
611
+ otherDetails,
612
612
  });
613
613
 
614
614
  /**
@@ -654,8 +654,39 @@ function GuestList({
654
654
  }
655
655
  return col;
656
656
  });
657
+ const summaryCols = columns.filter((col) => col.enable_summary);
658
+ let dataToExport = [...patients];
659
+
660
+ if (summaryCols.length > 0) {
661
+ // Build one synthetic row for CSV export that mirrors the table layout:
662
+ // numeric summary cells are populated from `calculateSummaryValues`, while
663
+ // non-summary columns stay blank unless a configured caption should be shown.
664
+ const summaryValues = calculateSummaryValues(summaryCols, patients);
665
+ const summaryRow = { isSummaryRow: true };
666
+
667
+ cols.forEach((col, index) => {
668
+ // Start each export column empty so the appended row keeps the same shape
669
+ // as the data rows and does not leak index/helper values into the export.
670
+ const colKey = col.field || col.key || col.dataIndex;
671
+ if (colKey && !summaryRow[colKey]) {
672
+ summaryRow[colKey] = '';
673
+ }
657
674
 
658
- let exportDatas = getExportData(patients, exportCols);
675
+ if (summaryValues[col.field] !== undefined) {
676
+ // Fill columns that have an aggregate configured (sum, count, avg, etc.).
677
+ summaryRow[col.field] = summaryValues[col.field];
678
+ } else {
679
+ // If this column is marked as the caption target for a summary column,
680
+ // place the configured label (for example "Total") into that cell.
681
+ const captionConfig = columns.find((c) => col.field && c.caption_field === col.field);
682
+ if (captionConfig) {
683
+ summaryRow[col.field] = captionConfig.summary_caption || '';
684
+ }
685
+ }
686
+ });
687
+ dataToExport.push(summaryRow);
688
+ }
689
+ let exportDatas = getExportData(dataToExport, exportCols);
659
690
 
660
691
  if (exportDatas.exportDataColumns.length && exportDatas.exportDataHeaders.length) {
661
692
  setExportData({ exportDatas });
@@ -740,6 +771,55 @@ function GuestList({
740
771
  const handleCloseEdit = () => {
741
772
  setShowEdit(false);
742
773
  };
774
+ /**
775
+ * Calculates aggregate values for the configured summary columns.
776
+ *
777
+ * Each summary definition contributes one value keyed by its `field`. Missing
778
+ * row values are treated as `0` for numeric operations so the table summary and
779
+ * export summary row can be built from the same result object.
780
+ *
781
+ * Supported functions:
782
+ * `sum` - totals all numeric values in the field.
783
+ * `count` - returns the number of rows in the current dataset.
784
+ * `avg` - returns the arithmetic mean of the field values.
785
+ * `min` - returns the smallest numeric value in the field.
786
+ * `max` - returns the largest numeric value in the field.
787
+ *
788
+ * @param {Array<Object>} summaryCols - Column configs with `field` and `function`.
789
+ * @param {Array<Object>} pageData - Rows currently being summarized.
790
+ * @returns {Object} Aggregate values keyed by field name.
791
+ */
792
+ function calculateSummaryValues(summaryCols, pageData) {
793
+ const summaryValues = {};
794
+
795
+ summaryCols.forEach((col) => {
796
+ const field = col.field;
797
+
798
+ if (col.function === 'sum') {
799
+ summaryValues[field] = pageData.reduce((total, row) => total + Number(row[field] || 0), 0);
800
+ }
801
+
802
+ if (col.function === 'count') {
803
+ summaryValues[field] = pageData.length;
804
+ }
805
+
806
+ if (col.function === 'avg') {
807
+ const total = pageData.reduce((sum, row) => sum + Number(row[field] || 0), 0);
808
+ summaryValues[field] = pageData.length ? total / pageData.length : 0;
809
+ }
810
+ if (col.function === 'min') {
811
+ const values = pageData.map((row) => Number(row[field] || 0));
812
+ summaryValues[field] = values.length ? Math.min(...values) : 0;
813
+ }
814
+
815
+ if (col.function === 'max') {
816
+ const values = pageData.map((row) => Number(row[field] || 0));
817
+ summaryValues[field] = values.length ? Math.max(...values) : 0;
818
+ }
819
+ });
820
+
821
+ return summaryValues;
822
+ }
743
823
  return (
744
824
  <>
745
825
  <div className="table-header">
@@ -790,6 +870,7 @@ function GuestList({
790
870
  {exportData.exportDatas && (
791
871
  <ExportReactCSV
792
872
  title={config.caption}
873
+ fileName={`${(config.caption || 'Report').trim().replace(/\s+/g, '_')}_${moment().format('YYYY-MM-DD-HH-mm-ss-SSS')}.xlsx`}
793
874
  headers={exportData.exportDatas.exportDataHeaders}
794
875
  csvData={exportData.exportDatas.exportDataColumns}
795
876
  />
@@ -814,47 +895,37 @@ function GuestList({
814
895
  columns={cols}
815
896
  sticky
816
897
  pagination={false}
817
- // title={config.caption}
818
898
  summary={(pageData) => {
819
- // Variable to save the summary data
820
- let summary = {};
821
-
822
- let summaryColumns = [
823
- { field: 'opb_amt', title: 'Amount' },
824
- { field: 'opb_netamt', title: 'Net Amount' },
825
- ];
826
-
827
- let tableColumns = cols;
828
-
829
- // Creating a copy of columns to append the summary configuration that is needed to set
830
- tableColumns.forEach((record, index) => {
831
- summaryColumns.forEach((inner) => {
832
- if (record.field === inner.field) {
833
- tableColumns[index].summary = inner;
834
- }
835
- });
836
- });
837
-
838
- // Initialize
839
- summaryColumns.map((item) => {
840
- return (summary[item.field] = 0);
841
- });
842
-
843
- // Find the total
844
- summaryColumns.map((item) => {
845
- pageData.forEach((entry) => {
846
- return (summary[item.field] = summary[item.field] + entry[item.field]);
847
- });
848
- });
899
+ const summaryCols = columns.filter((col) => col.enable_summary);
900
+ if (!summaryCols.length) return null;
901
+ /** calculate summary*/
902
+
903
+ const summaryValues = calculateSummaryValues(summaryCols, pageData);
904
+
849
905
 
850
906
  return (
851
- <>
852
- <Table.Summary.Row>
853
- {tableColumns.map((column, key) => {
854
- return <Table.Summary.Cell key={key}>{column.summary ? <>{summary[column.summary.field]}</> : null}</Table.Summary.Cell>;
855
- })}
856
- </Table.Summary.Row>
857
- </>
907
+ <Table.Summary.Row className="report-summary-row">
908
+ {cols.map((col, index) => {
909
+ if (summaryValues[col.field] !== undefined) {
910
+ return (
911
+ <Table.Summary.Cell key={index}>
912
+ <strong style={{ fontWeight: 900 }}>{summaryValues[col.field]}</strong>
913
+ </Table.Summary.Cell>
914
+ );
915
+ }
916
+
917
+ const captionConfig = columns.find((c) => col.field && c.caption_field === col.field);
918
+ if (captionConfig) {
919
+ return (
920
+ <Table.Summary.Cell key={index}>
921
+ <strong style={{ fontWeight: 900 }}>{captionConfig.summary_caption || ''}</strong>
922
+ </Table.Summary.Cell>
923
+ );
924
+ }
925
+
926
+ return <Table.Summary.Cell key={index} />;
927
+ })}
928
+ </Table.Summary.Row>
858
929
  );
859
930
  }}
860
931
  />
@@ -5,6 +5,13 @@
5
5
  justify-content: space-between;
6
6
  }
7
7
 
8
+ .report-summary-row {
9
+ position: sticky;
10
+ bottom: 0;
11
+ background: #fff;
12
+ z-index: 10;
13
+ }
14
+
8
15
  .table-header {
9
16
  display: flex;
10
17
  justify-content: space-between;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ui-soxo-bootstrap-core",
3
- "version": "2.6.19",
3
+ "version": "2.6.20",
4
4
  "description": "All the Core Components for you to start",
5
5
  "keywords": [
6
6
  "all in one"