@visns-studio/visns-components 5.10.2 → 5.10.4
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/README.md +42 -1
- package/package.json +3 -3
- package/src/components/crm/DataGrid.jsx +707 -2686
- package/src/components/crm/Field.jsx +0 -2
- package/src/components/crm/cells/CellWithTooltip.jsx +138 -0
- package/src/components/crm/columns/ColumnRenderers.jsx +1358 -0
- package/src/components/crm/controls/AudioPlayer.jsx +19 -0
- package/src/components/crm/controls/AutoRefreshControls.jsx +37 -0
- package/src/components/crm/controls/DataGridSearch.jsx +238 -0
- package/src/components/crm/modals/GalleryModal.jsx +405 -0
- package/src/components/crm/styles/DataGrid.module.scss +296 -0
- package/src/components/crm/styles/global-datagrid.css +21 -0
- package/src/index.js +11 -0
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { useEffect, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
const AudioPlayer = ({ audioSource }) => {
|
|
4
|
+
const audioRef = useRef(null);
|
|
5
|
+
|
|
6
|
+
useEffect(() => {
|
|
7
|
+
if (audioSource) {
|
|
8
|
+
audioRef.current.play();
|
|
9
|
+
}
|
|
10
|
+
}, [audioSource]);
|
|
11
|
+
|
|
12
|
+
if (!audioSource) {
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return <audio src={audioSource} ref={audioRef} />;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export default AudioPlayer;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import styles from '../styles/DataGrid.module.scss';
|
|
2
|
+
|
|
3
|
+
const AutoRefreshControls = ({
|
|
4
|
+
ajaxSetting,
|
|
5
|
+
autoRefreshEnabled,
|
|
6
|
+
setAutoRefreshEnabled,
|
|
7
|
+
refreshInterval,
|
|
8
|
+
isRelocated = false,
|
|
9
|
+
}) => {
|
|
10
|
+
// Only show auto-refresh if the setting is present in ajaxSetting
|
|
11
|
+
if (!ajaxSetting || ajaxSetting.autoRefresh === undefined) {
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const containerClass = isRelocated
|
|
16
|
+
? styles.autoRefreshContainerRelocated
|
|
17
|
+
: styles.autoRefreshContainer;
|
|
18
|
+
|
|
19
|
+
return (
|
|
20
|
+
<div className={containerClass}>
|
|
21
|
+
<label className={styles.autoRefreshLabel}>
|
|
22
|
+
<input
|
|
23
|
+
type="checkbox"
|
|
24
|
+
checked={autoRefreshEnabled}
|
|
25
|
+
onChange={() =>
|
|
26
|
+
setAutoRefreshEnabled(!autoRefreshEnabled)
|
|
27
|
+
}
|
|
28
|
+
className={styles.autoRefreshCheckbox}
|
|
29
|
+
/>
|
|
30
|
+
Auto Refresh{' '}
|
|
31
|
+
{refreshInterval && `(${refreshInterval}s)`}
|
|
32
|
+
</label>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default AutoRefreshControls;
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { useNavigate } from 'react-router-dom';
|
|
2
|
+
import { toast } from 'react-toastify';
|
|
3
|
+
import { saveAs } from 'file-saver';
|
|
4
|
+
import {
|
|
5
|
+
LinkOut,
|
|
6
|
+
Plus,
|
|
7
|
+
Search,
|
|
8
|
+
Sort,
|
|
9
|
+
} from 'akar-icons';
|
|
10
|
+
import Download from '../Download';
|
|
11
|
+
import styles from '../styles/DataGrid.module.scss';
|
|
12
|
+
|
|
13
|
+
const DataGridSearch = ({
|
|
14
|
+
form,
|
|
15
|
+
search,
|
|
16
|
+
onSearchChange,
|
|
17
|
+
onModalOpen,
|
|
18
|
+
// Export dependencies
|
|
19
|
+
dataRef,
|
|
20
|
+
filterValue,
|
|
21
|
+
paramValue,
|
|
22
|
+
// Sort dependencies
|
|
23
|
+
pageData,
|
|
24
|
+
pageSetting,
|
|
25
|
+
}) => {
|
|
26
|
+
const navigate = useNavigate();
|
|
27
|
+
|
|
28
|
+
const handleChangeSearch = (e) => {
|
|
29
|
+
const { value } = e.target;
|
|
30
|
+
onSearchChange(value);
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const exportTrigger = async () => {
|
|
34
|
+
try {
|
|
35
|
+
toast.success('Starting export please wait...');
|
|
36
|
+
|
|
37
|
+
let payload;
|
|
38
|
+
|
|
39
|
+
if (form.export.useData) {
|
|
40
|
+
payload = { data: dataRef.current };
|
|
41
|
+
} else {
|
|
42
|
+
payload = {
|
|
43
|
+
filter: filterValue,
|
|
44
|
+
paramId: paramValue || '',
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const res = await Download(form.export.url, 'POST', payload);
|
|
49
|
+
|
|
50
|
+
if (res.data.error) {
|
|
51
|
+
toast.error(res.data.error);
|
|
52
|
+
} else {
|
|
53
|
+
// Assuming the response contains a Blob or similar data
|
|
54
|
+
if (res.data && res.data instanceof Blob) {
|
|
55
|
+
const fileName = `${form.export.filename}`;
|
|
56
|
+
saveAs(res.data, fileName);
|
|
57
|
+
} else {
|
|
58
|
+
// Handle the case where the response is not a Blob
|
|
59
|
+
toast.error('Invalid file format received.');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
} catch (error) {
|
|
63
|
+
console.info(error);
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const sortTrigger = () => {
|
|
68
|
+
let baseUrl = form.url.replace('/ajax', '');
|
|
69
|
+
let sortUrl = paramValue
|
|
70
|
+
? `${baseUrl}/${paramValue}/sort`
|
|
71
|
+
: `${baseUrl}/sort`;
|
|
72
|
+
|
|
73
|
+
if (form.sort?.url) {
|
|
74
|
+
sortUrl = form.sort.url;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const newState = {
|
|
78
|
+
form,
|
|
79
|
+
page: {
|
|
80
|
+
data: pageData,
|
|
81
|
+
setting: pageSetting,
|
|
82
|
+
},
|
|
83
|
+
paramValue,
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
navigate(sortUrl, { state: newState });
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
const renderCreateButton = () => {
|
|
90
|
+
if (form.createDisable && form.createDisable === true) {
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const isSearchEnabled = !(form.searchDisable === true);
|
|
95
|
+
const buttonClassName = isSearchEnabled
|
|
96
|
+
? styles.actionButtons
|
|
97
|
+
: styles.actionButtonsStandalone;
|
|
98
|
+
|
|
99
|
+
return (
|
|
100
|
+
<div className={buttonClassName}>
|
|
101
|
+
{form.export && form.export.url && (
|
|
102
|
+
<button className={styles.btn} onClick={exportTrigger}>
|
|
103
|
+
<LinkOut
|
|
104
|
+
strokeWidth={2}
|
|
105
|
+
size={16}
|
|
106
|
+
style={{ marginRight: '10px' }}
|
|
107
|
+
/>{' '}
|
|
108
|
+
Export
|
|
109
|
+
</button>
|
|
110
|
+
)}
|
|
111
|
+
{form.sort && form.sort.title && (
|
|
112
|
+
<button className={styles.btn} onClick={sortTrigger}>
|
|
113
|
+
<Sort
|
|
114
|
+
strokeWidth={2}
|
|
115
|
+
size={16}
|
|
116
|
+
style={{ marginRight: '10px' }}
|
|
117
|
+
/>{' '}
|
|
118
|
+
Sort Order
|
|
119
|
+
</button>
|
|
120
|
+
)}
|
|
121
|
+
|
|
122
|
+
{(form.create &&
|
|
123
|
+
form.create.title &&
|
|
124
|
+
form.create.title !== '') ||
|
|
125
|
+
(form.url && form.create?.title !== '') ? (
|
|
126
|
+
<button
|
|
127
|
+
className={styles.btn}
|
|
128
|
+
onClick={(e) => {
|
|
129
|
+
e.preventDefault();
|
|
130
|
+
|
|
131
|
+
if (form.create) {
|
|
132
|
+
if (form.create.url) {
|
|
133
|
+
// Check if URL is external
|
|
134
|
+
if (
|
|
135
|
+
form.create.url.startsWith(
|
|
136
|
+
'http://'
|
|
137
|
+
) ||
|
|
138
|
+
form.create.url.startsWith(
|
|
139
|
+
'https://'
|
|
140
|
+
)
|
|
141
|
+
) {
|
|
142
|
+
window.open(
|
|
143
|
+
form.create.url,
|
|
144
|
+
'_blank'
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
navigate(form.create.url);
|
|
148
|
+
}
|
|
149
|
+
} else {
|
|
150
|
+
onModalOpen('create', 0);
|
|
151
|
+
}
|
|
152
|
+
} else {
|
|
153
|
+
if (form.url) {
|
|
154
|
+
window.open(
|
|
155
|
+
form.url,
|
|
156
|
+
'',
|
|
157
|
+
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
158
|
+
);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}}
|
|
162
|
+
>
|
|
163
|
+
<Plus
|
|
164
|
+
strokeWidth={2}
|
|
165
|
+
size={16}
|
|
166
|
+
style={{ marginRight: '10px' }}
|
|
167
|
+
/>
|
|
168
|
+
Add New
|
|
169
|
+
</button>
|
|
170
|
+
) : form.url && form.modal === false ? (
|
|
171
|
+
<button
|
|
172
|
+
className={styles.btn}
|
|
173
|
+
onClick={(e) => {
|
|
174
|
+
e.preventDefault();
|
|
175
|
+
|
|
176
|
+
window.open(
|
|
177
|
+
form.url,
|
|
178
|
+
'',
|
|
179
|
+
'width=1440,height=1024,menubar=1,resizable=1,status=1,titlebar=1,toolbar=1'
|
|
180
|
+
);
|
|
181
|
+
}}
|
|
182
|
+
>
|
|
183
|
+
<Plus
|
|
184
|
+
strokeWidth={2}
|
|
185
|
+
size={16}
|
|
186
|
+
style={{ marginRight: '10px' }}
|
|
187
|
+
/>
|
|
188
|
+
Add New
|
|
189
|
+
</button>
|
|
190
|
+
) : null}
|
|
191
|
+
</div>
|
|
192
|
+
);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const isSearchEnabled = !(form.searchDisable === true);
|
|
196
|
+
const isCreateDisabled = form.createDisable === true;
|
|
197
|
+
|
|
198
|
+
// Don't render anything if both search and create are disabled
|
|
199
|
+
if (!isSearchEnabled && isCreateDisabled) {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// If search is disabled but create button is enabled, render only the button with its own container
|
|
204
|
+
if (!isSearchEnabled && !isCreateDisabled) {
|
|
205
|
+
return (
|
|
206
|
+
<div className={styles.actionButtonsContainer}>
|
|
207
|
+
{renderCreateButton()}
|
|
208
|
+
</div>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// If search is enabled, render the full search container with both search and buttons
|
|
213
|
+
return (
|
|
214
|
+
<div className={styles.searchContainer}>
|
|
215
|
+
<div className={styles.searchInputWrapper}>
|
|
216
|
+
<div
|
|
217
|
+
className={styles['icon-search']}
|
|
218
|
+
style={{ left: 25, color: '#000' }}
|
|
219
|
+
>
|
|
220
|
+
<Search strokeWidth={2} size={14} />
|
|
221
|
+
</div>
|
|
222
|
+
<input
|
|
223
|
+
type="text"
|
|
224
|
+
placeholder="Search"
|
|
225
|
+
value={search}
|
|
226
|
+
onChange={handleChangeSearch}
|
|
227
|
+
style={{ borderRadius: 0, paddingLeft: 55 }}
|
|
228
|
+
className={styles.searchInput}
|
|
229
|
+
/>
|
|
230
|
+
</div>
|
|
231
|
+
<div className={styles.actionButtonsWrapper}>
|
|
232
|
+
{!isCreateDisabled && renderCreateButton()}
|
|
233
|
+
</div>
|
|
234
|
+
</div>
|
|
235
|
+
);
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
export default DataGridSearch;
|