@visns-studio/visns-components 4.8.3 → 4.8.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/package.json +1 -1
- package/src/components/crm/Field.jsx +110 -14
- package/src/components/crm/QuickAction.jsx +475 -0
- package/src/components/crm/generic/GenericDetail.jsx +5 -0
- package/src/components/crm/generic/GenericDynamic.jsx +26 -9
- package/src/components/crm/generic/GenericFormBuilder.jsx +84 -56
- package/src/components/crm/generic/GenericIndex.jsx +13 -1
package/package.json
CHANGED
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
76
76
|
},
|
|
77
77
|
"name": "@visns-studio/visns-components",
|
|
78
|
-
"version": "4.8.
|
|
78
|
+
"version": "4.8.4",
|
|
79
79
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
80
80
|
"main": "src/index.js",
|
|
81
81
|
"files": [
|
|
@@ -1237,6 +1237,7 @@ function Field({
|
|
|
1237
1237
|
for (const key in formData) {
|
|
1238
1238
|
if (formData[key].id === settings.id) {
|
|
1239
1239
|
if (formData[key].hasOwnProperty('options')) {
|
|
1240
|
+
console.info(formData[key].rows, 'test form');
|
|
1240
1241
|
tableRadioColumns = formData[key].options;
|
|
1241
1242
|
tableRadioRows = formData[key].rows;
|
|
1242
1243
|
break; // Assuming there's only one match needed, break out of the loop
|
|
@@ -1244,6 +1245,107 @@ function Field({
|
|
|
1244
1245
|
}
|
|
1245
1246
|
}
|
|
1246
1247
|
|
|
1248
|
+
const renderTableRadioField = (s, cell, rowKey, cellKey) => {
|
|
1249
|
+
switch (s.type) {
|
|
1250
|
+
case 'checkbox':
|
|
1251
|
+
return (
|
|
1252
|
+
<input
|
|
1253
|
+
type={'checkbox'}
|
|
1254
|
+
checked={cell[cellKey] || false}
|
|
1255
|
+
data-id={settings.id}
|
|
1256
|
+
onChange={(e) => {
|
|
1257
|
+
onChangeTableRadio(e, rowKey, cellKey);
|
|
1258
|
+
}}
|
|
1259
|
+
/>
|
|
1260
|
+
);
|
|
1261
|
+
case 'html5_date':
|
|
1262
|
+
return (
|
|
1263
|
+
<input
|
|
1264
|
+
type="date"
|
|
1265
|
+
value={cell[cellKey] || ''}
|
|
1266
|
+
data-id={settings.id}
|
|
1267
|
+
onChange={(e) =>
|
|
1268
|
+
onChangeTableRadio(e, rowKey, cellKey)
|
|
1269
|
+
}
|
|
1270
|
+
/>
|
|
1271
|
+
);
|
|
1272
|
+
case 'html5_datetime':
|
|
1273
|
+
return (
|
|
1274
|
+
<input
|
|
1275
|
+
type="datetime"
|
|
1276
|
+
value={cell[cellKey] || ''}
|
|
1277
|
+
data-id={settings.id}
|
|
1278
|
+
onChange={(e) =>
|
|
1279
|
+
onChangeTableRadio(e, rowKey, cellKey)
|
|
1280
|
+
}
|
|
1281
|
+
/>
|
|
1282
|
+
);
|
|
1283
|
+
case 'html5_time':
|
|
1284
|
+
return (
|
|
1285
|
+
<input
|
|
1286
|
+
type="time"
|
|
1287
|
+
value={cell[cellKey] || ''}
|
|
1288
|
+
data-id={settings.id}
|
|
1289
|
+
onChange={(e) =>
|
|
1290
|
+
onChangeTableRadio(e, rowKey, cellKey)
|
|
1291
|
+
}
|
|
1292
|
+
/>
|
|
1293
|
+
);
|
|
1294
|
+
case 'number':
|
|
1295
|
+
return (
|
|
1296
|
+
<input
|
|
1297
|
+
type="number"
|
|
1298
|
+
value={cell[cellKey] || ''}
|
|
1299
|
+
data-id={settings.id}
|
|
1300
|
+
onChange={(e) =>
|
|
1301
|
+
onChangeTableRadio(e, rowKey, cellKey)
|
|
1302
|
+
}
|
|
1303
|
+
/>
|
|
1304
|
+
);
|
|
1305
|
+
case 'textarea':
|
|
1306
|
+
return (
|
|
1307
|
+
<textarea
|
|
1308
|
+
value={cell[cellKey] || ''}
|
|
1309
|
+
data-id={settings.id}
|
|
1310
|
+
onChange={(e) =>
|
|
1311
|
+
onChangeTableRadio(e, rowKey, cellKey)
|
|
1312
|
+
}
|
|
1313
|
+
></textarea>
|
|
1314
|
+
);
|
|
1315
|
+
case 'toggle':
|
|
1316
|
+
return (
|
|
1317
|
+
<Toggle
|
|
1318
|
+
name={settings.id}
|
|
1319
|
+
data-name={settings.id}
|
|
1320
|
+
data-id={settings.id}
|
|
1321
|
+
checked={
|
|
1322
|
+
cell[cellKey] !== undefined
|
|
1323
|
+
? cell[cellKey] === ''
|
|
1324
|
+
? false
|
|
1325
|
+
: cell[cellKey] === 'on'
|
|
1326
|
+
? false
|
|
1327
|
+
: cell[cellKey]
|
|
1328
|
+
: false
|
|
1329
|
+
}
|
|
1330
|
+
onChange={(e) => {
|
|
1331
|
+
onChangeTableRadio(e, rowKey, cellKey);
|
|
1332
|
+
}}
|
|
1333
|
+
/>
|
|
1334
|
+
);
|
|
1335
|
+
default:
|
|
1336
|
+
return (
|
|
1337
|
+
<input
|
|
1338
|
+
type={'text'}
|
|
1339
|
+
value={cell[cellKey] || ''}
|
|
1340
|
+
data-id={settings.id}
|
|
1341
|
+
onChange={(e) =>
|
|
1342
|
+
onChangeTableRadio(e, rowKey, cellKey)
|
|
1343
|
+
}
|
|
1344
|
+
/>
|
|
1345
|
+
);
|
|
1346
|
+
}
|
|
1347
|
+
};
|
|
1348
|
+
|
|
1247
1349
|
return (
|
|
1248
1350
|
<table className={styles.contentTable}>
|
|
1249
1351
|
<thead>
|
|
@@ -1274,22 +1376,16 @@ function Field({
|
|
|
1274
1376
|
>
|
|
1275
1377
|
{row.label}
|
|
1276
1378
|
</td>
|
|
1277
|
-
{tableRadioColumns.map((
|
|
1379
|
+
{tableRadioColumns.map((cell, cellKey) => (
|
|
1278
1380
|
<td
|
|
1279
|
-
key={`table-radio-cell-${settings.id}-${row.id}-${
|
|
1381
|
+
key={`table-radio-cell-${settings.id}-${row.id}-${cell.id}`}
|
|
1280
1382
|
>
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
onChange={onChangeTableRadio}
|
|
1288
|
-
checked={
|
|
1289
|
-
row.value?.[column.id] ===
|
|
1290
|
-
true
|
|
1291
|
-
}
|
|
1292
|
-
/>
|
|
1383
|
+
{renderTableRadioField(
|
|
1384
|
+
tableRadioColumns[cellKey],
|
|
1385
|
+
row.value || '',
|
|
1386
|
+
row.id,
|
|
1387
|
+
cellKey
|
|
1388
|
+
)}
|
|
1293
1389
|
</td>
|
|
1294
1390
|
))}
|
|
1295
1391
|
</tr>
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState } from 'react';
|
|
2
|
+
import { useNavigate, useLocation } from 'react-router-dom';
|
|
3
|
+
import Popup from 'reactjs-popup';
|
|
4
|
+
import { Search } from 'akar-icons';
|
|
5
|
+
|
|
6
|
+
import CustomFetch from './Fetch';
|
|
7
|
+
import Form from './Form';
|
|
8
|
+
|
|
9
|
+
const QuickAction = ({ childRef, navConfig }) => {
|
|
10
|
+
const navigate = useNavigate();
|
|
11
|
+
const location = useLocation();
|
|
12
|
+
const searchBarRef = useRef(null);
|
|
13
|
+
|
|
14
|
+
/** Modal Contact */
|
|
15
|
+
const [contactModalShow, setContactModalShow] = useState(false);
|
|
16
|
+
const [contactFormType, setContactFormType] = useState('create');
|
|
17
|
+
const [contactFormId, setContactFormId] = useState(0);
|
|
18
|
+
const [contactForm, setContactForm] = useState({
|
|
19
|
+
primaryKey: 'id',
|
|
20
|
+
url: '/ajax/contacts',
|
|
21
|
+
create: {
|
|
22
|
+
title: 'Create a new Contact entry',
|
|
23
|
+
},
|
|
24
|
+
update: {
|
|
25
|
+
title: 'Update an Existing Contact entry',
|
|
26
|
+
},
|
|
27
|
+
fields: [
|
|
28
|
+
{
|
|
29
|
+
id: 'id',
|
|
30
|
+
label: 'ID',
|
|
31
|
+
type: 'hidden',
|
|
32
|
+
required: false,
|
|
33
|
+
size: 'half',
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
id: 'title',
|
|
37
|
+
label: 'Title',
|
|
38
|
+
type: 'text',
|
|
39
|
+
required: false,
|
|
40
|
+
size: 'full',
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: 'firstname',
|
|
44
|
+
label: 'Firstname',
|
|
45
|
+
type: 'text',
|
|
46
|
+
required: true,
|
|
47
|
+
size: 'half',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'surname',
|
|
51
|
+
label: 'Surname',
|
|
52
|
+
type: 'text',
|
|
53
|
+
required: true,
|
|
54
|
+
size: 'half',
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: 'address1',
|
|
58
|
+
label: 'Address',
|
|
59
|
+
type: 'json',
|
|
60
|
+
parent: 'address',
|
|
61
|
+
required: false,
|
|
62
|
+
size: 'half',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
id: 'suburb',
|
|
66
|
+
label: 'Suburb',
|
|
67
|
+
type: 'json',
|
|
68
|
+
parent: 'address',
|
|
69
|
+
required: false,
|
|
70
|
+
size: 'half',
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
id: 'postcode',
|
|
74
|
+
label: 'Postcode',
|
|
75
|
+
type: 'json',
|
|
76
|
+
parent: 'address',
|
|
77
|
+
required: false,
|
|
78
|
+
size: 'half',
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
id: 'state',
|
|
82
|
+
label: 'State',
|
|
83
|
+
type: 'json',
|
|
84
|
+
parent: 'address',
|
|
85
|
+
required: false,
|
|
86
|
+
size: 'half',
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
id: 'phone',
|
|
90
|
+
label: 'Phone',
|
|
91
|
+
type: 'phone',
|
|
92
|
+
required: false,
|
|
93
|
+
size: 'half',
|
|
94
|
+
},
|
|
95
|
+
{
|
|
96
|
+
id: 'mobile',
|
|
97
|
+
label: 'mobile',
|
|
98
|
+
type: 'mobile',
|
|
99
|
+
required: false,
|
|
100
|
+
size: 'half',
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
id: 'fax',
|
|
104
|
+
label: 'Fax',
|
|
105
|
+
type: 'phone',
|
|
106
|
+
required: false,
|
|
107
|
+
size: 'half',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
id: 'email',
|
|
111
|
+
label: 'Email',
|
|
112
|
+
type: 'text',
|
|
113
|
+
required: false,
|
|
114
|
+
size: 'half',
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: 'client_id',
|
|
118
|
+
label: 'User',
|
|
119
|
+
type: 'multi-dropdown-ajax',
|
|
120
|
+
url: '/ajax/clients/dropdown',
|
|
121
|
+
relation: ['client'],
|
|
122
|
+
relationName: 'name',
|
|
123
|
+
required: false,
|
|
124
|
+
size: 'half',
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: 'position',
|
|
128
|
+
label: 'Position',
|
|
129
|
+
type: 'text',
|
|
130
|
+
required: false,
|
|
131
|
+
size: 'half',
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
id: 'contact_types',
|
|
135
|
+
label: 'Role Type (Select Applicable)',
|
|
136
|
+
type: 'multi-dropdown-ajax',
|
|
137
|
+
url: '/ajax/contactTypes/dropdown',
|
|
138
|
+
required: false,
|
|
139
|
+
multi: true,
|
|
140
|
+
size: 'half',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
id: 'contacts_industries',
|
|
144
|
+
label: 'Sector (Select Applicable)',
|
|
145
|
+
type: 'multi-dropdown-ajax',
|
|
146
|
+
url: '/ajax/industries/dropdown',
|
|
147
|
+
required: true,
|
|
148
|
+
multi: true,
|
|
149
|
+
size: 'half',
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
id: 'stakeholder_type_id',
|
|
153
|
+
label: 'Stakeholder Type',
|
|
154
|
+
type: 'dropdown-ajax',
|
|
155
|
+
url: '/ajax/stakeholderTypes/dropdown',
|
|
156
|
+
where: [],
|
|
157
|
+
child: {
|
|
158
|
+
id: 'stakeholder_category_id',
|
|
159
|
+
url: '/ajax/stakeholderCategories/dropdown',
|
|
160
|
+
},
|
|
161
|
+
relation: ['stakeholderTypes'],
|
|
162
|
+
relation_from: 'id',
|
|
163
|
+
required: false,
|
|
164
|
+
size: 'half',
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
id: 'stakeholder_category_id',
|
|
168
|
+
label: 'Stakeholder Category',
|
|
169
|
+
type: 'dropdown',
|
|
170
|
+
options: [],
|
|
171
|
+
required: false,
|
|
172
|
+
size: 'half',
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
id: 'bio',
|
|
176
|
+
label: 'Bio',
|
|
177
|
+
type: 'richeditor',
|
|
178
|
+
required: false,
|
|
179
|
+
size: 'full',
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
const contactModalClose = (e) => {
|
|
185
|
+
setContactModalShow(false);
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const contactModalOpen = (ft, fi) => {
|
|
189
|
+
setContactModalShow(true);
|
|
190
|
+
setContactFormType(ft);
|
|
191
|
+
setContactFormId(fi);
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
const contactChildDropdownCallback = (data) => {
|
|
195
|
+
let _form = contactForm;
|
|
196
|
+
|
|
197
|
+
contactForm.fields.map((a, b) => {
|
|
198
|
+
if (a.id === data.id) {
|
|
199
|
+
_form.fields[b].options = data.data;
|
|
200
|
+
}
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
setContactForm(() => ({
|
|
204
|
+
..._form,
|
|
205
|
+
}));
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
/** Modal Client */
|
|
209
|
+
const [clientModalShow, setClientModalShow] = useState(false);
|
|
210
|
+
const [clientFormType, setClientFormType] = useState('create');
|
|
211
|
+
const [clientFormId, setClientFormId] = useState(0);
|
|
212
|
+
const [clientForm, setClientForm] = useState({
|
|
213
|
+
primaryKey: 'id',
|
|
214
|
+
url: '/ajax/clients',
|
|
215
|
+
create: {
|
|
216
|
+
title: 'Create a new User entry',
|
|
217
|
+
},
|
|
218
|
+
update: {
|
|
219
|
+
title: 'Update an Existing User entry',
|
|
220
|
+
},
|
|
221
|
+
fields: [
|
|
222
|
+
{
|
|
223
|
+
id: 'id',
|
|
224
|
+
label: 'ID',
|
|
225
|
+
type: 'hidden',
|
|
226
|
+
required: false,
|
|
227
|
+
size: 'half',
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
id: 'name',
|
|
231
|
+
label: 'Name',
|
|
232
|
+
type: 'text',
|
|
233
|
+
required: true,
|
|
234
|
+
size: 'half',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: 'abn',
|
|
238
|
+
label: 'ABN',
|
|
239
|
+
type: 'text',
|
|
240
|
+
required: false,
|
|
241
|
+
size: 'half',
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
id: 'notes',
|
|
245
|
+
label: 'Notes',
|
|
246
|
+
type: 'textarea',
|
|
247
|
+
required: false,
|
|
248
|
+
size: 'full',
|
|
249
|
+
},
|
|
250
|
+
],
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
const clientModalClose = (e) => {
|
|
254
|
+
setClientModalShow(false);
|
|
255
|
+
};
|
|
256
|
+
|
|
257
|
+
const clientModalOpen = (ft, fi) => {
|
|
258
|
+
setClientModalShow(true);
|
|
259
|
+
setClientFormType(ft);
|
|
260
|
+
setClientFormId(fi);
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
const clientChildDropdownCallback = (data) => {
|
|
264
|
+
let _form = clientForm;
|
|
265
|
+
|
|
266
|
+
clientForm.fields.map((a, b) => {
|
|
267
|
+
if (a.id === data.id) {
|
|
268
|
+
_form.fields[b].options = data.data;
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
setClientForm(() => ({
|
|
273
|
+
..._form,
|
|
274
|
+
}));
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/** Universal Search */
|
|
278
|
+
const [search, setSearch] = useState('');
|
|
279
|
+
const [searchResults, setSearchResults] = useState([]);
|
|
280
|
+
const [showSearchResults, setShowSearchResults] = useState(false);
|
|
281
|
+
|
|
282
|
+
const ResultSection = ({ title, data, handleSelectResult }) => {
|
|
283
|
+
if (data === null || data === undefined || data?.length === 0)
|
|
284
|
+
return null;
|
|
285
|
+
|
|
286
|
+
return (
|
|
287
|
+
<>
|
|
288
|
+
<div className="resultItem">
|
|
289
|
+
<h3>{title}</h3>
|
|
290
|
+
</div>
|
|
291
|
+
{data.map((result) => (
|
|
292
|
+
<div
|
|
293
|
+
key={result.id}
|
|
294
|
+
className="resultItem"
|
|
295
|
+
onClick={() => handleSelectResult(title, result)}
|
|
296
|
+
>
|
|
297
|
+
<p>
|
|
298
|
+
{title === 'Contacts'
|
|
299
|
+
? `${result.firstname} ${result.surname}`
|
|
300
|
+
: result.name || result.project_name}
|
|
301
|
+
</p>
|
|
302
|
+
</div>
|
|
303
|
+
))}
|
|
304
|
+
</>
|
|
305
|
+
);
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
const handleSearch = (e) => {
|
|
309
|
+
setSearchResults([]);
|
|
310
|
+
setSearch(e.target.value);
|
|
311
|
+
|
|
312
|
+
if (e.key === 'Enter') {
|
|
313
|
+
// Check if the Enter key was pressed
|
|
314
|
+
fetchSearchResults();
|
|
315
|
+
}
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
const handleSelectResult = (type, d) => {
|
|
319
|
+
switch (type) {
|
|
320
|
+
case 'Contacts':
|
|
321
|
+
navigate(`/contacts`);
|
|
322
|
+
break;
|
|
323
|
+
case 'Enquiries':
|
|
324
|
+
navigate(`/leads/${d.id}`);
|
|
325
|
+
break;
|
|
326
|
+
case 'Users':
|
|
327
|
+
navigate(`/clients/${d.id}`);
|
|
328
|
+
break;
|
|
329
|
+
}
|
|
330
|
+
setSearchResults([]);
|
|
331
|
+
};
|
|
332
|
+
|
|
333
|
+
const fetchSearchResults = async () => {
|
|
334
|
+
try {
|
|
335
|
+
const res = await CustomFetch('/ajax/dashboard/search', 'POST', {
|
|
336
|
+
search,
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
setSearchResults(res.data);
|
|
340
|
+
setSearch('');
|
|
341
|
+
} catch (e) {
|
|
342
|
+
console.log(e);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
|
|
346
|
+
useEffect(() => {
|
|
347
|
+
let ssr = false;
|
|
348
|
+
|
|
349
|
+
if (searchResults?.contacts?.length > 0) {
|
|
350
|
+
ssr = true;
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (searchResults?.enquiries?.length > 0) {
|
|
354
|
+
ssr = true;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
if (searchResults?.users?.length > 0) {
|
|
358
|
+
ssr = true;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
setShowSearchResults(ssr);
|
|
362
|
+
}, [searchResults]);
|
|
363
|
+
|
|
364
|
+
useEffect(() => {
|
|
365
|
+
const handleClickOutside = (event) => {
|
|
366
|
+
if (
|
|
367
|
+
searchBarRef.current &&
|
|
368
|
+
!searchBarRef.current.contains(event.target)
|
|
369
|
+
) {
|
|
370
|
+
setShowSearchResults(false);
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
|
|
374
|
+
document.addEventListener('mousedown', handleClickOutside);
|
|
375
|
+
return () => {
|
|
376
|
+
document.removeEventListener('mousedown', handleClickOutside);
|
|
377
|
+
};
|
|
378
|
+
}, []);
|
|
379
|
+
|
|
380
|
+
return (
|
|
381
|
+
<>
|
|
382
|
+
<div className="quickActions">
|
|
383
|
+
<span>Quick Actions</span>
|
|
384
|
+
<button
|
|
385
|
+
onClick={() => {
|
|
386
|
+
navigate('/leads/create');
|
|
387
|
+
}}
|
|
388
|
+
>
|
|
389
|
+
New Enquiry
|
|
390
|
+
</button>
|
|
391
|
+
<button
|
|
392
|
+
onClick={() => {
|
|
393
|
+
clientModalOpen('create', 0);
|
|
394
|
+
}}
|
|
395
|
+
>
|
|
396
|
+
New User
|
|
397
|
+
</button>
|
|
398
|
+
<button
|
|
399
|
+
onClick={() => {
|
|
400
|
+
if (location.pathname === '/leads/create') {
|
|
401
|
+
console.info('aa');
|
|
402
|
+
childRef.current.contactOpen();
|
|
403
|
+
} else {
|
|
404
|
+
contactModalOpen('create', 0);
|
|
405
|
+
}
|
|
406
|
+
}}
|
|
407
|
+
>
|
|
408
|
+
New Contact
|
|
409
|
+
</button>
|
|
410
|
+
|
|
411
|
+
<div className="searchBar" ref={searchBarRef}>
|
|
412
|
+
<input
|
|
413
|
+
type="text"
|
|
414
|
+
placeholder="Search..."
|
|
415
|
+
value={search}
|
|
416
|
+
onChange={handleSearch}
|
|
417
|
+
onKeyDown={handleSearch}
|
|
418
|
+
/>
|
|
419
|
+
|
|
420
|
+
<div className="iconContainer">
|
|
421
|
+
<Search
|
|
422
|
+
strokeWidth={2}
|
|
423
|
+
size={18}
|
|
424
|
+
className="searchIcon"
|
|
425
|
+
/>
|
|
426
|
+
</div>
|
|
427
|
+
|
|
428
|
+
{showSearchResults && (
|
|
429
|
+
<div className="searchResults">
|
|
430
|
+
<ResultSection
|
|
431
|
+
title="Contacts"
|
|
432
|
+
data={searchResults.contacts}
|
|
433
|
+
handleSelectResult={handleSelectResult}
|
|
434
|
+
/>
|
|
435
|
+
<ResultSection
|
|
436
|
+
title="Enquiries"
|
|
437
|
+
data={searchResults.enquiries}
|
|
438
|
+
handleSelectResult={handleSelectResult}
|
|
439
|
+
/>
|
|
440
|
+
<ResultSection
|
|
441
|
+
title="Users"
|
|
442
|
+
data={searchResults.users}
|
|
443
|
+
handleSelectResult={handleSelectResult}
|
|
444
|
+
/>
|
|
445
|
+
</div>
|
|
446
|
+
)}
|
|
447
|
+
</div>
|
|
448
|
+
</div>
|
|
449
|
+
|
|
450
|
+
<Popup open={contactModalShow} onClose={contactModalClose}>
|
|
451
|
+
<Form
|
|
452
|
+
closeModal={contactModalClose}
|
|
453
|
+
fetchTable={() => {}}
|
|
454
|
+
formSettings={contactForm}
|
|
455
|
+
formType={contactFormType}
|
|
456
|
+
columnId={contactFormId}
|
|
457
|
+
childDropdownCallback={contactChildDropdownCallback}
|
|
458
|
+
/>
|
|
459
|
+
</Popup>
|
|
460
|
+
|
|
461
|
+
<Popup open={clientModalShow} onClose={clientModalClose}>
|
|
462
|
+
<Form
|
|
463
|
+
closeModal={clientModalClose}
|
|
464
|
+
fetchTable={() => {}}
|
|
465
|
+
formSettings={clientForm}
|
|
466
|
+
formType={clientFormType}
|
|
467
|
+
columnId={clientFormId}
|
|
468
|
+
childDropdownCallback={clientChildDropdownCallback}
|
|
469
|
+
/>
|
|
470
|
+
</Popup>
|
|
471
|
+
</>
|
|
472
|
+
);
|
|
473
|
+
};
|
|
474
|
+
|
|
475
|
+
export default QuickAction;
|
|
@@ -36,6 +36,7 @@ function GenericDetail({
|
|
|
36
36
|
extraUrlParam,
|
|
37
37
|
layout = 'full',
|
|
38
38
|
setting,
|
|
39
|
+
setActiveNav = null,
|
|
39
40
|
urlParam,
|
|
40
41
|
userProfile,
|
|
41
42
|
}) {
|
|
@@ -1203,6 +1204,10 @@ function GenericDetail({
|
|
|
1203
1204
|
}, []);
|
|
1204
1205
|
|
|
1205
1206
|
useEffect(() => {
|
|
1207
|
+
// if (setActiveNav) {
|
|
1208
|
+
// setActiveNav();
|
|
1209
|
+
// }
|
|
1210
|
+
|
|
1206
1211
|
if (tabs && tabs.length > 0) {
|
|
1207
1212
|
const activeTab = subnav.find((s) => s.show === true);
|
|
1208
1213
|
|
|
@@ -211,9 +211,12 @@ const GenericDynamic = ({
|
|
|
211
211
|
}
|
|
212
212
|
};
|
|
213
213
|
|
|
214
|
-
const handleChangeTableRadio = (e) => {
|
|
214
|
+
const handleChangeTableRadio = (e, rowId, columnId) => {
|
|
215
215
|
const {
|
|
216
|
-
dataset: { id
|
|
216
|
+
dataset: { id },
|
|
217
|
+
type,
|
|
218
|
+
checked,
|
|
219
|
+
value,
|
|
217
220
|
} = e.target;
|
|
218
221
|
|
|
219
222
|
setActiveForm((prevActiveForm) => {
|
|
@@ -229,17 +232,27 @@ const GenericDynamic = ({
|
|
|
229
232
|
return prevActiveForm;
|
|
230
233
|
}
|
|
231
234
|
|
|
232
|
-
// Create a new rows array with
|
|
235
|
+
// Create a new rows array with updated value properties based on input type
|
|
233
236
|
const updatedRows = prevActiveForm[formKey].rows.map((row) => {
|
|
234
237
|
if (row.id === rowId) {
|
|
235
|
-
//
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
}
|
|
238
|
+
// Determine new value based on input type
|
|
239
|
+
let newValue;
|
|
240
|
+
if (type === 'checkbox') {
|
|
241
|
+
newValue = { ...row.value, [columnId]: checked };
|
|
242
|
+
} else if (
|
|
243
|
+
type === 'date' ||
|
|
244
|
+
type === 'time' ||
|
|
245
|
+
type === 'text'
|
|
246
|
+
) {
|
|
247
|
+
newValue = { ...row.value, [columnId]: value };
|
|
248
|
+
} else {
|
|
249
|
+
// For other input types, use the value directly
|
|
250
|
+
newValue = { ...row.value, [columnId]: value };
|
|
251
|
+
}
|
|
252
|
+
|
|
240
253
|
return {
|
|
241
254
|
...row,
|
|
242
|
-
value:
|
|
255
|
+
value: newValue,
|
|
243
256
|
};
|
|
244
257
|
}
|
|
245
258
|
return row;
|
|
@@ -256,6 +269,10 @@ const GenericDynamic = ({
|
|
|
256
269
|
});
|
|
257
270
|
};
|
|
258
271
|
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
console.info(activeForm);
|
|
274
|
+
}, [activeForm]);
|
|
275
|
+
|
|
259
276
|
const handleChangeAddTableTextRow = (id) => {
|
|
260
277
|
setActiveForm((prevActiveForm) => {
|
|
261
278
|
// Find the key of the form that matches the id
|
|
@@ -1176,11 +1176,9 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1176
1176
|
</div>
|
|
1177
1177
|
)}
|
|
1178
1178
|
|
|
1179
|
-
{[
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
'table_radio',
|
|
1183
|
-
].includes(dataField.type) ? (
|
|
1179
|
+
{['checkbox', 'dropdown'].includes(
|
|
1180
|
+
dataField.type
|
|
1181
|
+
) ? (
|
|
1184
1182
|
<div
|
|
1185
1183
|
className={`${styles.formItem} ${styles.fwItem}`}
|
|
1186
1184
|
>
|
|
@@ -1195,7 +1193,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1195
1193
|
<tr>
|
|
1196
1194
|
<th
|
|
1197
1195
|
style={{
|
|
1198
|
-
width: '
|
|
1196
|
+
width: '790px',
|
|
1199
1197
|
}}
|
|
1200
1198
|
>
|
|
1201
1199
|
{dataField.type ===
|
|
@@ -1251,7 +1249,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1251
1249
|
<td
|
|
1252
1250
|
style={{
|
|
1253
1251
|
textAlign:
|
|
1254
|
-
'
|
|
1252
|
+
'right',
|
|
1255
1253
|
}}
|
|
1256
1254
|
>
|
|
1257
1255
|
<button
|
|
@@ -1260,7 +1258,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1260
1258
|
}
|
|
1261
1259
|
style={{
|
|
1262
1260
|
padding:
|
|
1263
|
-
'
|
|
1261
|
+
'15px 20px',
|
|
1264
1262
|
}}
|
|
1265
1263
|
onClick={
|
|
1266
1264
|
handleDeleteOption
|
|
@@ -1278,7 +1276,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1278
1276
|
2
|
|
1279
1277
|
}
|
|
1280
1278
|
size={
|
|
1281
|
-
|
|
1279
|
+
18
|
|
1282
1280
|
}
|
|
1283
1281
|
/>
|
|
1284
1282
|
</button>
|
|
@@ -1320,7 +1318,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1320
1318
|
<td
|
|
1321
1319
|
style={{
|
|
1322
1320
|
textAlign:
|
|
1323
|
-
'
|
|
1321
|
+
'right',
|
|
1324
1322
|
}}
|
|
1325
1323
|
>
|
|
1326
1324
|
<button
|
|
@@ -1329,7 +1327,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1329
1327
|
}
|
|
1330
1328
|
style={{
|
|
1331
1329
|
padding:
|
|
1332
|
-
'
|
|
1330
|
+
'15px 20px',
|
|
1333
1331
|
}}
|
|
1334
1332
|
onClick={
|
|
1335
1333
|
handleAddOption
|
|
@@ -1343,7 +1341,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1343
1341
|
strokeWidth={
|
|
1344
1342
|
2
|
|
1345
1343
|
}
|
|
1346
|
-
size={
|
|
1344
|
+
size={18}
|
|
1347
1345
|
/>
|
|
1348
1346
|
</button>
|
|
1349
1347
|
</td>
|
|
@@ -1353,7 +1351,9 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1353
1351
|
</div>
|
|
1354
1352
|
) : null}
|
|
1355
1353
|
|
|
1356
|
-
{['table_text'].includes(
|
|
1354
|
+
{['table_radio', 'table_text'].includes(
|
|
1355
|
+
dataField.type
|
|
1356
|
+
) ? (
|
|
1357
1357
|
<div
|
|
1358
1358
|
className={`${styles.formItem} ${styles.fwItem}`}
|
|
1359
1359
|
>
|
|
@@ -1444,30 +1444,46 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1444
1444
|
);
|
|
1445
1445
|
}}
|
|
1446
1446
|
>
|
|
1447
|
+
<option value="">
|
|
1448
|
+
No
|
|
1449
|
+
Type
|
|
1450
|
+
Selected
|
|
1451
|
+
</option>
|
|
1447
1452
|
{fieldTypes
|
|
1448
1453
|
.filter(
|
|
1449
1454
|
(
|
|
1450
1455
|
item
|
|
1451
|
-
) =>
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1456
|
+
) => {
|
|
1457
|
+
const excludedKeywords =
|
|
1458
|
+
[
|
|
1459
|
+
'dropdown',
|
|
1460
|
+
'dynamicdata',
|
|
1461
|
+
'file',
|
|
1462
|
+
'image',
|
|
1463
|
+
'plaintext',
|
|
1464
|
+
'plaintextheading',
|
|
1465
|
+
'section',
|
|
1466
|
+
'signature',
|
|
1467
|
+
'table',
|
|
1468
|
+
];
|
|
1469
|
+
// Exclude 'checkbox' only if dataField.type is 'table_text'
|
|
1470
|
+
if (
|
|
1471
|
+
dataField.type ===
|
|
1472
|
+
'table_text'
|
|
1473
|
+
) {
|
|
1474
|
+
excludedKeywords.push(
|
|
1475
|
+
'checkbox'
|
|
1476
|
+
);
|
|
1477
|
+
}
|
|
1478
|
+
return !excludedKeywords.some(
|
|
1464
1479
|
(
|
|
1465
1480
|
keyword
|
|
1466
1481
|
) =>
|
|
1467
1482
|
item.value.includes(
|
|
1468
1483
|
keyword
|
|
1469
1484
|
)
|
|
1470
|
-
)
|
|
1485
|
+
);
|
|
1486
|
+
}
|
|
1471
1487
|
)
|
|
1472
1488
|
.map(
|
|
1473
1489
|
(
|
|
@@ -1491,7 +1507,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1491
1507
|
<td
|
|
1492
1508
|
style={{
|
|
1493
1509
|
textAlign:
|
|
1494
|
-
'
|
|
1510
|
+
'right',
|
|
1495
1511
|
}}
|
|
1496
1512
|
>
|
|
1497
1513
|
<button
|
|
@@ -1500,7 +1516,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1500
1516
|
}
|
|
1501
1517
|
style={{
|
|
1502
1518
|
padding:
|
|
1503
|
-
'
|
|
1519
|
+
'15px 20px',
|
|
1504
1520
|
}}
|
|
1505
1521
|
onClick={
|
|
1506
1522
|
handleDeleteOption
|
|
@@ -1518,7 +1534,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1518
1534
|
2
|
|
1519
1535
|
}
|
|
1520
1536
|
size={
|
|
1521
|
-
|
|
1537
|
+
18
|
|
1522
1538
|
}
|
|
1523
1539
|
/>
|
|
1524
1540
|
</button>
|
|
@@ -1565,7 +1581,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1565
1581
|
handleDataOption
|
|
1566
1582
|
}
|
|
1567
1583
|
>
|
|
1568
|
-
<option>
|
|
1584
|
+
<option value="">
|
|
1569
1585
|
Please
|
|
1570
1586
|
Select an
|
|
1571
1587
|
Option
|
|
@@ -1574,26 +1590,37 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1574
1590
|
.filter(
|
|
1575
1591
|
(
|
|
1576
1592
|
item
|
|
1577
|
-
) =>
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1593
|
+
) => {
|
|
1594
|
+
const excludedKeywords =
|
|
1595
|
+
[
|
|
1596
|
+
'dropdown',
|
|
1597
|
+
'dynamicdata',
|
|
1598
|
+
'file',
|
|
1599
|
+
'image',
|
|
1600
|
+
'plaintext',
|
|
1601
|
+
'plaintextheading',
|
|
1602
|
+
'section',
|
|
1603
|
+
'signature',
|
|
1604
|
+
'table',
|
|
1605
|
+
];
|
|
1606
|
+
// Exclude 'checkbox' only if dataField.type is 'table_text'
|
|
1607
|
+
if (
|
|
1608
|
+
dataField.type ===
|
|
1609
|
+
'table_text'
|
|
1610
|
+
) {
|
|
1611
|
+
excludedKeywords.push(
|
|
1612
|
+
'checkbox'
|
|
1613
|
+
);
|
|
1614
|
+
}
|
|
1615
|
+
return !excludedKeywords.some(
|
|
1590
1616
|
(
|
|
1591
1617
|
keyword
|
|
1592
1618
|
) =>
|
|
1593
1619
|
item.value.includes(
|
|
1594
1620
|
keyword
|
|
1595
1621
|
)
|
|
1596
|
-
)
|
|
1622
|
+
);
|
|
1623
|
+
}
|
|
1597
1624
|
)
|
|
1598
1625
|
.map(
|
|
1599
1626
|
(
|
|
@@ -1617,7 +1644,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1617
1644
|
<td
|
|
1618
1645
|
style={{
|
|
1619
1646
|
textAlign:
|
|
1620
|
-
'
|
|
1647
|
+
'right',
|
|
1621
1648
|
}}
|
|
1622
1649
|
>
|
|
1623
1650
|
<button
|
|
@@ -1626,7 +1653,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1626
1653
|
}
|
|
1627
1654
|
style={{
|
|
1628
1655
|
padding:
|
|
1629
|
-
'
|
|
1656
|
+
'15px 20px',
|
|
1630
1657
|
}}
|
|
1631
1658
|
onClick={
|
|
1632
1659
|
handleAddOption
|
|
@@ -1640,7 +1667,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1640
1667
|
strokeWidth={
|
|
1641
1668
|
2
|
|
1642
1669
|
}
|
|
1643
|
-
size={
|
|
1670
|
+
size={18}
|
|
1644
1671
|
/>
|
|
1645
1672
|
</button>
|
|
1646
1673
|
</td>
|
|
@@ -1698,7 +1725,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1698
1725
|
<tr>
|
|
1699
1726
|
<th
|
|
1700
1727
|
style={{
|
|
1701
|
-
width: '
|
|
1728
|
+
width: '790px',
|
|
1702
1729
|
}}
|
|
1703
1730
|
>
|
|
1704
1731
|
Row Label
|
|
@@ -1745,7 +1772,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1745
1772
|
<td
|
|
1746
1773
|
style={{
|
|
1747
1774
|
textAlign:
|
|
1748
|
-
'
|
|
1775
|
+
'right',
|
|
1749
1776
|
}}
|
|
1750
1777
|
>
|
|
1751
1778
|
<button
|
|
@@ -1754,7 +1781,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1754
1781
|
}
|
|
1755
1782
|
style={{
|
|
1756
1783
|
padding:
|
|
1757
|
-
'
|
|
1784
|
+
'15px 20px',
|
|
1758
1785
|
}}
|
|
1759
1786
|
onClick={
|
|
1760
1787
|
handleDeleteRowOption
|
|
@@ -1772,7 +1799,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1772
1799
|
2
|
|
1773
1800
|
}
|
|
1774
1801
|
size={
|
|
1775
|
-
|
|
1802
|
+
18
|
|
1776
1803
|
}
|
|
1777
1804
|
/>
|
|
1778
1805
|
</button>
|
|
@@ -1795,8 +1822,9 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1795
1822
|
<input
|
|
1796
1823
|
type="text"
|
|
1797
1824
|
style={{
|
|
1825
|
+
height: '52px',
|
|
1798
1826
|
padding:
|
|
1799
|
-
'
|
|
1827
|
+
'1rem',
|
|
1800
1828
|
}}
|
|
1801
1829
|
name="label"
|
|
1802
1830
|
onChange={
|
|
@@ -1810,7 +1838,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1810
1838
|
<td
|
|
1811
1839
|
style={{
|
|
1812
1840
|
textAlign:
|
|
1813
|
-
'
|
|
1841
|
+
'right',
|
|
1814
1842
|
}}
|
|
1815
1843
|
>
|
|
1816
1844
|
<button
|
|
@@ -1819,7 +1847,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1819
1847
|
}
|
|
1820
1848
|
style={{
|
|
1821
1849
|
padding:
|
|
1822
|
-
'
|
|
1850
|
+
'15px 20px',
|
|
1823
1851
|
}}
|
|
1824
1852
|
onClick={
|
|
1825
1853
|
handleAddRowOption
|
|
@@ -1833,7 +1861,7 @@ function GenericFormBuilder({ setting, urlParam, userProfile }) {
|
|
|
1833
1861
|
strokeWidth={
|
|
1834
1862
|
2
|
|
1835
1863
|
}
|
|
1836
|
-
size={
|
|
1864
|
+
size={18}
|
|
1837
1865
|
/>
|
|
1838
1866
|
</button>
|
|
1839
1867
|
</td>
|
|
@@ -172,7 +172,13 @@ const ReportForm = ({ config, userProfile }) => {
|
|
|
172
172
|
);
|
|
173
173
|
};
|
|
174
174
|
|
|
175
|
-
function GenericIndex({
|
|
175
|
+
function GenericIndex({
|
|
176
|
+
layout = 'full',
|
|
177
|
+
setting,
|
|
178
|
+
setActiveNav = null,
|
|
179
|
+
themeType,
|
|
180
|
+
userProfile,
|
|
181
|
+
}) {
|
|
176
182
|
const gridRef = useRef(null);
|
|
177
183
|
const [rowsSelected, setRowsSelected] = useState({});
|
|
178
184
|
const [total, setTotal] = useState(0);
|
|
@@ -331,6 +337,12 @@ function GenericIndex({ layout = 'full', setting, themeType, userProfile }) {
|
|
|
331
337
|
}
|
|
332
338
|
}, [layout, subnav, setSubnav, setConfig, renderTable]);
|
|
333
339
|
|
|
340
|
+
useEffect(() => {
|
|
341
|
+
if (setActiveNav) {
|
|
342
|
+
setActiveNav();
|
|
343
|
+
}
|
|
344
|
+
}, []);
|
|
345
|
+
|
|
334
346
|
return (
|
|
335
347
|
<>
|
|
336
348
|
<div className={styles.grid}>
|