ui-soxo-bootstrap-core 2.6.20 → 2.6.22
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.
- package/core/components/landing-api/landing-api.js +12 -21
- package/core/lib/Store.js +5 -1
- package/core/lib/components/global-header/global-header.js +67 -37
- package/core/lib/models/forms/components/form-creator/form-creator.js +31 -8
- package/core/lib/models/forms/components/form-creator/form-creator.scss +29 -26
- package/core/models/core-scripts/core-scripts.js +134 -126
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js +174 -0
- package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss +76 -0
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.js +177 -49
- package/core/modules/reporting/components/reporting-dashboard/reporting-dashboard.scss +36 -0
- package/package.json +1 -1
|
@@ -9,142 +9,150 @@ import React from 'react';
|
|
|
9
9
|
|
|
10
10
|
import Base from '../base/base';
|
|
11
11
|
|
|
12
|
-
import { ApiUtils } from './../../lib/'
|
|
13
|
-
|
|
12
|
+
import { ApiUtils } from './../../lib/';
|
|
14
13
|
|
|
15
14
|
class CoreScript extends Base {
|
|
15
|
+
constructor() {
|
|
16
|
+
super();
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
{
|
|
22
|
-
field: 'name',
|
|
23
|
-
caption: 'Name'
|
|
24
|
-
},
|
|
25
|
-
{
|
|
26
|
-
field: 'amount',
|
|
27
|
-
caption: 'Amount'
|
|
28
|
-
}
|
|
29
|
-
];
|
|
30
|
-
|
|
31
|
-
// this.columns = ;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
get id() {
|
|
35
|
-
return 'id';
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
get getEndpoint() {
|
|
39
|
-
return 'core-scripts';
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
get modelName() {
|
|
44
|
-
return `core-scripts`;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
get columns() {
|
|
48
|
-
return [
|
|
49
|
-
{
|
|
50
|
-
caption: 'Staff',
|
|
51
|
-
field: 'staff.name',
|
|
52
|
-
key: 'staff'
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
caption: 'Weight',
|
|
56
|
-
field: 'weight',
|
|
57
|
-
key: 'weight'
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
caption: 'Date',
|
|
61
|
-
field: 'created_at',
|
|
62
|
-
key: 'created_at'
|
|
63
|
-
}
|
|
64
|
-
];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
/**
|
|
69
|
-
*Getting core_script data
|
|
70
|
-
* @returns
|
|
71
|
-
*/
|
|
72
|
-
getReportingLisitng = (id, formBody, dbPtr = null) => {
|
|
73
|
-
|
|
74
|
-
// Settings db pointer
|
|
75
|
-
if (!dbPtr) dbPtr = localStorage.db_ptr;
|
|
76
|
-
return ApiUtils.post({
|
|
77
|
-
url: `core-scripts/dashboardquery/${id}`,
|
|
78
|
-
formBody,
|
|
79
|
-
headers: {
|
|
80
|
-
'Content-Type': 'application/json',
|
|
81
|
-
Authorization: 'Bearer ' + localStorage.access_token,
|
|
82
|
-
db_ptr: dbPtr
|
|
18
|
+
this.fields = [
|
|
19
|
+
{
|
|
20
|
+
field: 'name',
|
|
21
|
+
caption: 'Name',
|
|
83
22
|
},
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
23
|
+
{
|
|
24
|
+
field: 'amount',
|
|
25
|
+
caption: 'Amount',
|
|
26
|
+
},
|
|
27
|
+
];
|
|
87
28
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
* @returns
|
|
91
|
-
*/
|
|
92
|
-
getUserDetailsLisitng = (id, formBody) => {
|
|
29
|
+
// this.columns = ;
|
|
30
|
+
}
|
|
93
31
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
});
|
|
98
|
-
};
|
|
32
|
+
get id() {
|
|
33
|
+
return 'id';
|
|
34
|
+
}
|
|
99
35
|
|
|
36
|
+
get getEndpoint() {
|
|
37
|
+
return 'core-scripts';
|
|
38
|
+
}
|
|
100
39
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
getReportMenu = async ({ id }) => {
|
|
105
|
-
|
|
106
|
-
const result = await this.getRecord({ id });
|
|
107
|
-
|
|
108
|
-
const report = result.result
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
...report,
|
|
112
|
-
caption: report.caption,
|
|
113
|
-
path: '/reports/' + report.id,
|
|
114
|
-
is_visible: true
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* To get extra script data corresponding to mode
|
|
120
|
-
*
|
|
121
|
-
* @param {*} mode
|
|
122
|
-
* @returns
|
|
123
|
-
*/
|
|
124
|
-
getExtraInfo = (mode) => {
|
|
125
|
-
return ApiUtils.get({
|
|
126
|
-
url: `core-scripts/get-script-data?mode=${mode}`,
|
|
127
|
-
});
|
|
128
|
-
};
|
|
40
|
+
get modelName() {
|
|
41
|
+
return `core-scripts`;
|
|
42
|
+
}
|
|
129
43
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
44
|
+
get columns() {
|
|
45
|
+
return [
|
|
46
|
+
{
|
|
47
|
+
caption: 'Staff',
|
|
48
|
+
field: 'staff.name',
|
|
49
|
+
key: 'staff',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
caption: 'Weight',
|
|
53
|
+
field: 'weight',
|
|
54
|
+
key: 'weight',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
caption: 'Date',
|
|
58
|
+
field: 'created_at',
|
|
59
|
+
key: 'created_at',
|
|
60
|
+
},
|
|
61
|
+
];
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
*Getting core_script data
|
|
66
|
+
* @returns
|
|
67
|
+
*/
|
|
68
|
+
getReportingLisitng = (id, formBody, dbPtr = null) => {
|
|
69
|
+
// Settings db pointer
|
|
70
|
+
if (!dbPtr) dbPtr = localStorage.db_ptr;
|
|
71
|
+
return ApiUtils.post({
|
|
72
|
+
// baseUrl: 'http://localhost:8002/dev/',
|
|
73
|
+
url: `core-scripts/dashboardquery/${id}`,
|
|
74
|
+
formBody,
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
Authorization: 'Bearer ' + localStorage.access_token,
|
|
78
|
+
db_ptr: dbPtr,
|
|
79
|
+
},
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
*Updating user details
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
87
|
+
getUserDetailsLisitng = (id, formBody) => {
|
|
88
|
+
return ApiUtils.post({
|
|
89
|
+
url: `core-scripts/update-user-deatils/${id}`,
|
|
90
|
+
formBody,
|
|
91
|
+
});
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
getQuery = (formBody) => {
|
|
95
|
+
return ApiUtils.post({
|
|
96
|
+
url: `core-scripts/execute-script-api`,
|
|
97
|
+
formBody,
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
*/
|
|
103
|
+
getQuerySeacch = (formBody) => {
|
|
104
|
+
return ApiUtils.post({
|
|
105
|
+
// baseUrl: 'http://localhost:8002/dev/',
|
|
106
|
+
url: `core-scripts/execute-script-by-search`,
|
|
107
|
+
formBody,
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
*
|
|
113
|
+
*/
|
|
114
|
+
getReportMenu = async ({ id }) => {
|
|
115
|
+
const result = await this.getRecord({ id });
|
|
116
|
+
|
|
117
|
+
const report = result.result;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
...report,
|
|
121
|
+
caption: report.caption,
|
|
122
|
+
path: '/reports/' + report.id,
|
|
123
|
+
is_visible: true,
|
|
147
124
|
};
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* To get extra script data corresponding to mode
|
|
129
|
+
*
|
|
130
|
+
* @param {*} mode
|
|
131
|
+
* @returns
|
|
132
|
+
*/
|
|
133
|
+
getExtraInfo = (mode) => {
|
|
134
|
+
return ApiUtils.get({
|
|
135
|
+
url: `core-scripts/get-script-data?mode=${mode}`,
|
|
136
|
+
});
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* To get executed data
|
|
141
|
+
*
|
|
142
|
+
* @param {*} mode
|
|
143
|
+
* @returns
|
|
144
|
+
*/
|
|
145
|
+
|
|
146
|
+
getSelectedExtraInfo = (formBody) => {
|
|
147
|
+
return ApiUtils.post({
|
|
148
|
+
url: `core-scripts/get-selected-script-data`,
|
|
149
|
+
formBody,
|
|
150
|
+
headers: {
|
|
151
|
+
'Content-Type': 'application/json',
|
|
152
|
+
Authorization: 'Bearer ' + localStorage.access_token,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
};
|
|
148
156
|
}
|
|
149
157
|
|
|
150
158
|
export default CoreScript;
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
|
|
2
|
+
import React, { useState, useEffect, useRef } from "react";
|
|
3
|
+
import { Select, Checkbox, Input, Spin } from "antd";
|
|
4
|
+
import { CoreScripts } from "../../../../../models";
|
|
5
|
+
import "./advance-search.scss";
|
|
6
|
+
|
|
7
|
+
const { Search } = Input;
|
|
8
|
+
|
|
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 : [];
|
|
22
|
+
|
|
23
|
+
const [searchResults, setSearchResults] = useState([]);
|
|
24
|
+
const [displayOptions, setDisplayOptions] = useState([]);
|
|
25
|
+
const [loading, setLoading] = useState(false);
|
|
26
|
+
const debounceRef = useRef(null);
|
|
27
|
+
|
|
28
|
+
const loadOptions = async (searchText = "") => {
|
|
29
|
+
try {
|
|
30
|
+
setLoading(true);
|
|
31
|
+
|
|
32
|
+
const formBody = {
|
|
33
|
+
script_id: finalReportId,
|
|
34
|
+
search_field: fieldName,
|
|
35
|
+
search_condition: searchText,
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const res = await CoreScripts.getQuerySeacch(formBody);
|
|
39
|
+
const data = Array.isArray(res.data) ? res.data : [];
|
|
40
|
+
const apiValues = data?.map((r) => r[fieldName]) || [];
|
|
41
|
+
|
|
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
|
+
]);
|
|
48
|
+
} catch (error) {
|
|
49
|
+
console.error("Search API error", error);
|
|
50
|
+
} finally {
|
|
51
|
+
setLoading(false);
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
// Clear search results when the field or report context changes,
|
|
57
|
+
// but do not load automatically on mount.
|
|
58
|
+
setSearchResults([]);
|
|
59
|
+
}, [fieldName, finalReportId]);
|
|
60
|
+
|
|
61
|
+
const handleSearch = (text) => {
|
|
62
|
+
if (debounceRef.current) clearTimeout(debounceRef.current);
|
|
63
|
+
debounceRef.current = setTimeout(() => loadOptions(text), 400);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
const toggleValue = (checked, item) => {
|
|
67
|
+
let newValues;
|
|
68
|
+
|
|
69
|
+
if (checked) newValues = [...safeValue, item];
|
|
70
|
+
else newValues = safeValue.filter((v) => v !== item);
|
|
71
|
+
|
|
72
|
+
onChange(newValues);
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
const handleReset = () => {
|
|
76
|
+
onChange([]);
|
|
77
|
+
if (finalOnReset) {
|
|
78
|
+
finalOnReset(); // 🔥 trigger dashboard refresh
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
const isActive = safeValue.length > 0;
|
|
83
|
+
|
|
84
|
+
// -------- INPUT MODE --------
|
|
85
|
+
if (!isSearchQuery) {
|
|
86
|
+
return (
|
|
87
|
+
<Input
|
|
88
|
+
allowClear
|
|
89
|
+
className={`advanced-search-input ${isActive ? "advanced-search-active" : ""}`}
|
|
90
|
+
placeholder={`Search ${caption}`}
|
|
91
|
+
style={style}
|
|
92
|
+
// The parent provides an array for `value`.
|
|
93
|
+
// We take the first element for the input's display value.
|
|
94
|
+
value={safeValue[0] || ""}
|
|
95
|
+
onChange={(e) => {
|
|
96
|
+
const text = e?.target?.value;
|
|
97
|
+
// Always pass an array back to the parent to be consistent with the Select mode.
|
|
98
|
+
onChange(text ? [text] : []);
|
|
99
|
+
if (!text && finalOnReset) {
|
|
100
|
+
finalOnReset();
|
|
101
|
+
}
|
|
102
|
+
}}
|
|
103
|
+
/>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// -------- SELECT MODE --------
|
|
108
|
+
return (
|
|
109
|
+
<Select
|
|
110
|
+
className={`advanced-search-select ${isActive ? "advanced-search-active" : ""}`}
|
|
111
|
+
style={style}
|
|
112
|
+
mode="multiple"
|
|
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
|
+
}}
|
|
127
|
+
allowClear
|
|
128
|
+
maxTagCount={1}
|
|
129
|
+
onChange={onChange}
|
|
130
|
+
maxTagPlaceholder={(omittedValues) => (
|
|
131
|
+
<span className="tag-placeholder-count">
|
|
132
|
+
+{omittedValues.length}
|
|
133
|
+
</span>
|
|
134
|
+
)}
|
|
135
|
+
dropdownRender={() => (
|
|
136
|
+
<div className="dropdown-content">
|
|
137
|
+
<Search
|
|
138
|
+
placeholder={`Search ${caption}`}
|
|
139
|
+
onChange={(e) => handleSearch(e.target.value)}
|
|
140
|
+
/>
|
|
141
|
+
|
|
142
|
+
<div className="dropdown-options-list">
|
|
143
|
+
{loading ? (
|
|
144
|
+
<Spin />
|
|
145
|
+
) : (
|
|
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
|
+
>
|
|
153
|
+
<Checkbox
|
|
154
|
+
checked={safeValue.includes(item)}
|
|
155
|
+
onClick={(e) => e.stopPropagation()} // Prevent double trigger when clicking the checkbox box specifically
|
|
156
|
+
onChange={(e) => toggleValue(e.target.checked, item)}
|
|
157
|
+
>
|
|
158
|
+
{item}
|
|
159
|
+
</Checkbox>
|
|
160
|
+
</div>
|
|
161
|
+
))
|
|
162
|
+
)}
|
|
163
|
+
</div>
|
|
164
|
+
|
|
165
|
+
<div className="dropdown-footer">
|
|
166
|
+
<span className="dropdown-reset-button" onClick={handleReset}>
|
|
167
|
+
Reset
|
|
168
|
+
</span>
|
|
169
|
+
</div>
|
|
170
|
+
</div>
|
|
171
|
+
)}
|
|
172
|
+
/>
|
|
173
|
+
);
|
|
174
|
+
}
|
package/core/modules/reporting/components/reporting-dashboard/adavance-search/advance-search.scss
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
// .advanced-search-select,
|
|
2
|
+
/* Base select width */
|
|
3
|
+
.advanced-search-select {
|
|
4
|
+
width: 160px;
|
|
5
|
+
// width: 100%;
|
|
6
|
+
}
|
|
7
|
+
.advanced-search-input {
|
|
8
|
+
width: 160px;
|
|
9
|
+
}
|
|
10
|
+
/* Active state (when value selected) */
|
|
11
|
+
.advanced-search-select.advanced-search-active .ant-select-selector {
|
|
12
|
+
border-color: #1677ff !important;
|
|
13
|
+
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.2) !important;
|
|
14
|
+
border-radius: 6px !important;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Input active state */
|
|
18
|
+
.advanced-search-input.advanced-search-active {
|
|
19
|
+
border-color: #1677ff !important;
|
|
20
|
+
box-shadow: 0 0 0 2px rgba(22, 119, 255, 0.2);
|
|
21
|
+
border-radius: 6px;
|
|
22
|
+
}
|
|
23
|
+
// .advanced-search-input {
|
|
24
|
+
// width: 250px;
|
|
25
|
+
|
|
26
|
+
// &.advanced-search-active {
|
|
27
|
+
// // Antd Select and Input don't share the same border-radius property,
|
|
28
|
+
// // so we apply a wrapper style that works for both.
|
|
29
|
+
// // For more specific control, you might need to target internal antd classes.
|
|
30
|
+
// border: 1px solid #91caff !important;
|
|
31
|
+
// box-shadow: 0 0 0 1px #91caff;
|
|
32
|
+
// border-radius: 6px;
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
.tag-placeholder {
|
|
36
|
+
display: flex;
|
|
37
|
+
align-items: center;
|
|
38
|
+
gap: 8px;
|
|
39
|
+
|
|
40
|
+
.tag-placeholder-count {
|
|
41
|
+
background: #e6f0ff;
|
|
42
|
+
color: #1677ff;
|
|
43
|
+
border-radius: 10px;
|
|
44
|
+
padding: 0 8px;
|
|
45
|
+
font-weight: 600;
|
|
46
|
+
font-size: 12px;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.dropdown-content {
|
|
51
|
+
padding: 10px;
|
|
52
|
+
|
|
53
|
+
.dropdown-options-list {
|
|
54
|
+
max-height: 200px;
|
|
55
|
+
overflow-y: auto;
|
|
56
|
+
margin-top: 10px;
|
|
57
|
+
|
|
58
|
+
.dropdown-option-item {
|
|
59
|
+
margin-bottom: 6px;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
.dropdown-footer {
|
|
64
|
+
display: flex;
|
|
65
|
+
justify-content: flex-end;
|
|
66
|
+
margin-top: 10px;
|
|
67
|
+
border-top: 1px solid #f0f0f0;
|
|
68
|
+
padding-top: 8px;
|
|
69
|
+
|
|
70
|
+
.dropdown-reset-button {
|
|
71
|
+
color: #1677ff;
|
|
72
|
+
cursor: pointer;
|
|
73
|
+
font-weight: 500;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|