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

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,31 +1,34 @@
1
1
  .form-creator {
2
- .ant-input-number {
3
- width: 100%;
4
- }
5
-
6
- .new-record {
7
- gap: 10px !important;
8
- }
2
+ .ant-input-number {
3
+ width: 100%;
4
+ }
9
5
 
10
- .form-item-element {
11
- position: relative;
12
- padding: 8px;
13
- border-radius: 4px;
14
- border: 1px solid #e3e3e3;
15
- margin-bottom: 3px;
16
-
17
- .field-customizer {
18
- .actions {
19
- // display: none;
20
- float: right;
21
- // position: absolute;
22
- // right: 0px;
23
- }
24
- }
6
+ .new-record {
7
+ display: flex;
8
+ flex-wrap: wrap;
9
+ align-items: flex-start;
10
+ gap: 10px !important;
11
+ }
25
12
 
13
+ .form-item-element {
14
+ position: relative;
15
+ padding: 4px;
16
+ // border-radius: 4px;
17
+ // border: 1px solid #e3e3e3;
18
+ // margin-bottom: 3px;
19
+ min-width: 140px;
20
+ // flex: 0 1 auto;
21
+ .field-customizer {
22
+ .actions {
23
+ // display: none;
24
+ float: right;
25
+ // position: absolute;
26
+ // right: 0px;
27
+ }
26
28
  }
29
+ }
27
30
 
28
- .submit-button {
29
- margin-top: 10px;
30
- }
31
- }
31
+ .submit-button {
32
+ margin-top: 34px;
33
+ }
34
+ }
@@ -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',
@@ -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) => {
@@ -6,34 +6,45 @@ import "./advance-search.scss";
6
6
 
7
7
  const { Search } = Input;
8
8
 
9
- export default function AdvancedSearchSelect({ reportId, field, value = [], onChange ,onReset}) {
10
-
11
- const isSearchQuery = !!field.search_query;
9
+ export default function AdvancedSearchSelect({ reportId, onReset, field, value, onChange, style, ...rest }) {
10
+ // Normalize the configuration.
11
+ // If 'field' is an object, we are in "legacy" mode. If it's a string, we are using the spread props from rest.
12
+ const isObjectMode = typeof field === "object";
13
+ const config = isObjectMode ? field : { ...rest, field };
14
+
15
+ const finalReportId = reportId || config.reportId;
16
+ const finalOnReset = onReset || config.onReset;
17
+ const isSearchQuery = !!config.search_query;
18
+ const fieldName = isObjectMode ? field.field : field;
19
+ const caption = config.caption;
20
+
21
+ const safeValue = Array.isArray(value) ? value : [];
12
22
 
13
23
  const [searchResults, setSearchResults] = useState([]);
14
- const [selectedItems, setSelectedItems] = useState(value);
24
+ const [displayOptions, setDisplayOptions] = useState([]);
15
25
  const [loading, setLoading] = useState(false);
16
26
  const debounceRef = useRef(null);
17
27
 
18
- useEffect(() => {
19
- setSelectedItems(value);
20
- }, [value]);
21
-
22
28
  const loadOptions = async (searchText = "") => {
23
29
  try {
24
30
  setLoading(true);
25
31
 
26
32
  const formBody = {
27
- script_id: reportId,
28
- search_field: field.field,
33
+ script_id: finalReportId,
34
+ search_field: fieldName,
29
35
  search_condition: searchText,
30
36
  };
31
37
 
32
38
  const res = await CoreScripts.getQuerySeacch(formBody);
33
39
  const data = Array.isArray(res.data) ? res.data : [];
34
- const apiValues = data?.map((r) => r[field.field]) || [];
40
+ const apiValues = data?.map((r) => r[fieldName]) || [];
35
41
 
36
42
  setSearchResults(apiValues);
43
+ // Refresh display options only when new search results arrive to keep the list stable during interaction
44
+ setDisplayOptions([
45
+ ...safeValue,
46
+ ...apiValues.filter((item) => !safeValue.includes(item)),
47
+ ]);
37
48
  } catch (error) {
38
49
  console.error("Search API error", error);
39
50
  } finally {
@@ -42,8 +53,10 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
42
53
  };
43
54
 
44
55
  useEffect(() => {
45
- if (isSearchQuery) loadOptions("");
46
- }, [field]);
56
+ // Clear search results when the field or report context changes,
57
+ // but do not load automatically on mount.
58
+ setSearchResults([]);
59
+ }, [fieldName, finalReportId]);
47
60
 
48
61
  const handleSearch = (text) => {
49
62
  if (debounceRef.current) clearTimeout(debounceRef.current);
@@ -53,41 +66,39 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
53
66
  const toggleValue = (checked, item) => {
54
67
  let newValues;
55
68
 
56
- if (checked) newValues = [...selectedItems, item];
57
- else newValues = selectedItems.filter((v) => v !== item);
69
+ if (checked) newValues = [...safeValue, item];
70
+ else newValues = safeValue.filter((v) => v !== item);
58
71
 
59
- setSelectedItems(newValues);
60
72
  onChange(newValues);
61
73
  };
62
74
 
63
75
  const handleReset = () => {
64
- setSelectedItems([]);
65
76
  onChange([]);
66
- // if (onReset) {
67
- onReset(); // 🔥 trigger dashboard refresh
68
- // }
77
+ if (finalOnReset) {
78
+ finalOnReset(); // 🔥 trigger dashboard refresh
79
+ }
69
80
  };
70
81
 
71
- const displayOptions = [
72
- ...selectedItems,
73
- ...searchResults.filter((item) => !selectedItems.includes(item)),
74
- ];
75
-
76
- const isActive = value && value.length > 0;
82
+ const isActive = safeValue.length > 0;
77
83
 
78
84
  // -------- INPUT MODE --------
79
85
  if (!isSearchQuery) {
80
86
  return (
81
87
  <Input
88
+ allowClear
82
89
  className={`advanced-search-input ${isActive ? "advanced-search-active" : ""}`}
83
- placeholder={`Search ${field.caption}`}
90
+ placeholder={`Search ${caption}`}
91
+ style={style}
84
92
  // The parent provides an array for `value`.
85
93
  // We take the first element for the input's display value.
86
- value={value[0] || ""}
94
+ value={safeValue[0] || ""}
87
95
  onChange={(e) => {
88
- const text = e.target.value;
96
+ const text = e?.target?.value;
89
97
  // Always pass an array back to the parent to be consistent with the Select mode.
90
98
  onChange(text ? [text] : []);
99
+ if (!text && finalOnReset) {
100
+ finalOnReset();
101
+ }
91
102
  }}
92
103
  />
93
104
  );
@@ -97,24 +108,34 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
97
108
  return (
98
109
  <Select
99
110
  className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
111
+ style={style}
100
112
  mode="multiple"
101
- value={value}
102
- placeholder={field.caption}
113
+ value={safeValue}
114
+ placeholder={caption}
115
+ onDropdownVisibleChange={(open) => {
116
+ if (open) {
117
+ loadOptions("");
118
+ } else {
119
+ // When closing, re-sort selected items to the top so they are visible next time it opens
120
+ const currentResults = searchResults.length > 0 ? searchResults : displayOptions;
121
+ setDisplayOptions([
122
+ ...safeValue,
123
+ ...currentResults.filter((item) => !safeValue.includes(item)),
124
+ ]);
125
+ }
126
+ }}
103
127
  allowClear
104
- maxTagCount={0}
128
+ maxTagCount={1}
105
129
  onChange={onChange}
106
- maxTagPlaceholder={() => (
107
- <span className="tag-placeholder">
108
- {field.caption}
109
- <span className="tag-placeholder-count">
110
- {value?.length || 0}
111
- </span>
130
+ maxTagPlaceholder={(omittedValues) => (
131
+ <span className="tag-placeholder-count">
132
+ +{omittedValues.length}
112
133
  </span>
113
134
  )}
114
135
  dropdownRender={() => (
115
136
  <div className="dropdown-content">
116
137
  <Search
117
- placeholder={`Search ${field.caption}`}
138
+ placeholder={`Search ${caption}`}
118
139
  onChange={(e) => handleSearch(e.target.value)}
119
140
  />
120
141
 
@@ -122,10 +143,16 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
122
143
  {loading ? (
123
144
  <Spin />
124
145
  ) : (
125
- displayOptions.map((item) => (
126
- <div key={item} className="dropdown-option-item">
146
+ displayOptions.map((item, idx) => (
147
+ <div
148
+ key={`${item}-${idx}`}
149
+ className="dropdown-option-item"
150
+ onClick={() => toggleValue(!safeValue.includes(item), item)}
151
+ style={{ cursor: 'pointer' }}
152
+ >
127
153
  <Checkbox
128
- checked={selectedItems.includes(item)}
154
+ checked={safeValue.includes(item)}
155
+ onClick={(e) => e.stopPropagation()} // Prevent double trigger when clicking the checkbox box specifically
129
156
  onChange={(e) => toggleValue(e.target.checked, item)}
130
157
  >
131
158
  {item}
@@ -144,4 +171,4 @@ export default function AdvancedSearchSelect({ reportId, field, value = [], onCh
144
171
  )}
145
172
  />
146
173
  );
147
- }
174
+ }
@@ -2,6 +2,7 @@
2
2
  /* Base select width */
3
3
  .advanced-search-select {
4
4
  width: 160px;
5
+ // width: 100%;
5
6
  }
6
7
  .advanced-search-input {
7
8
  width: 160px;
@@ -30,7 +31,6 @@
30
31
  // box-shadow: 0 0 0 1px #91caff;
31
32
  // border-radius: 6px;
32
33
  // }
33
- // }
34
34
 
35
35
  .tag-placeholder {
36
36
  display: flex;
@@ -73,4 +73,4 @@
73
73
  font-weight: 500;
74
74
  }
75
75
  }
76
- }
76
+ }