@visns-studio/visns-components 2.5.12 → 2.5.14

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.
@@ -42,6 +42,7 @@ import 'react-confirm-alert/src/react-confirm-alert.css';
42
42
 
43
43
  import CustomFetch from './Fetch';
44
44
  import Form from './Form';
45
+ import _ from 'lodash';
45
46
 
46
47
  const loadData = async (
47
48
  { skip, limit, sortInfo, currentData, filterValue, groupBy },
@@ -104,10 +105,12 @@ const DataGrid = forwardRef(
104
105
  form,
105
106
  gridHeight,
106
107
  settings,
107
- setFilterData,
108
108
  setConfig,
109
+ setFilterData,
109
110
  setTotal,
111
+ setRowsSelected,
110
112
  style,
113
+ tableSetting,
111
114
  },
112
115
  ref
113
116
  ) => {
@@ -171,6 +174,7 @@ const DataGrid = forwardRef(
171
174
  const [gridColumns, setGridColumns] = useState([]);
172
175
  const [limit, setLimit] = useState(0);
173
176
  const [search, setSearch] = useState('');
177
+ const [selected, setSelected] = useState({});
174
178
 
175
179
  /** Table Functions */
176
180
  useImperativeHandle(ref, () => ({
@@ -179,6 +183,11 @@ const DataGrid = forwardRef(
179
183
  },
180
184
  }));
181
185
 
186
+ const handleSelectionChnage = useCallback(({ selected }) => {
187
+ console.info(selected, data);
188
+ setSelected(selected);
189
+ }, []);
190
+
182
191
  const handleFilterChange = useCallback((d) => {
183
192
  setFilterValue(d);
184
193
  }, []);
@@ -425,7 +434,9 @@ const DataGrid = forwardRef(
425
434
  <div className="filterAction--alt">
426
435
  <button
427
436
  className="btn"
428
- onClick={() => {
437
+ onClick={(e) => {
438
+ e.preventDefault();
439
+
429
440
  if (form.create) {
430
441
  if (form.create.url) {
431
442
  navigate(form.create.url);
@@ -450,28 +461,33 @@ const DataGrid = forwardRef(
450
461
  };
451
462
 
452
463
  const renderSearch = () => {
453
- const searchEnable = !(
454
- form.searchDisable && form.searchDisable === true
455
- );
464
+ const isSearchEnabled = !(form.searchDisable === true);
465
+ const isCreateDisabled = form.createDisable === true;
456
466
 
457
- return searchEnable ? (
458
- <div className="filterInput--alt">
459
- <div className="icon-search">
460
- <Search strokeWidth={2} size={18} />
467
+ if (isSearchEnabled) {
468
+ return (
469
+ <div className="filterInput--alt">
470
+ <div className="icon-search">
471
+ <Search strokeWidth={2} size={18} />
472
+ </div>
473
+ <input
474
+ type="text"
475
+ placeholder="Search"
476
+ value={search}
477
+ onChange={handleChangeSearch}
478
+ />
461
479
  </div>
462
- <input
463
- type="text"
464
- placeholder="Search"
465
- value={search}
466
- onChange={handleChangeSearch}
467
- />
468
- </div>
469
- ) : (
470
- <div
471
- className="filterInput--alt"
472
- style={{ height: '37px' }}
473
- ></div>
474
- );
480
+ );
481
+ } else if (!isCreateDisabled) {
482
+ return (
483
+ <div
484
+ className="filterInput--alt"
485
+ style={{ height: '37px' }}
486
+ ></div>
487
+ );
488
+ }
489
+
490
+ return null; // Return null if neither search is enabled nor create is disabled
475
491
  };
476
492
 
477
493
  const handleCoding = (c, data) => {
@@ -555,6 +571,10 @@ const DataGrid = forwardRef(
555
571
  };
556
572
 
557
573
  /** Table Hooks */
574
+ useEffect(() => {
575
+ setRowsSelected(selected);
576
+ }, [selected]);
577
+
558
578
  useEffect(() => {
559
579
  const delayedSetDSearch = debounce(() => {
560
580
  setDSearch(search);
@@ -813,6 +833,19 @@ const DataGrid = forwardRef(
813
833
  }
814
834
  },
815
835
  };
836
+ case 'boolean':
837
+ return {
838
+ ...commonProps,
839
+ name: `${column.type}.${column.id}`,
840
+ sortable: false,
841
+ render: ({ data }) => {
842
+ if (data[column.id]) {
843
+ return 'Yes';
844
+ } else {
845
+ return 'No';
846
+ }
847
+ },
848
+ };
816
849
  case 'coding':
817
850
  return {
818
851
  ...commonProps,
@@ -1220,29 +1253,39 @@ const DataGrid = forwardRef(
1220
1253
  null
1221
1254
  );
1222
1255
 
1223
- if (
1224
- column.filter &&
1225
- column.filter.type &&
1226
- column.filter.type === 'select'
1227
- ) {
1228
- filterEditor = SelectFilter;
1229
- filterEditorProps = {
1230
- multiple:
1231
- column.filter.operator === 'inlist'
1232
- ? true
1233
- : false,
1234
- wrapMultiple:
1235
- column.filter.operator === 'inlist'
1236
- ? false
1237
- : true,
1238
- placeholder: column.filter.placeholder
1239
- ? column.filter.placeholder
1240
- : 'All Options',
1241
- dataSource: selectedDataSource,
1242
- };
1243
- } else {
1244
- filterEditor = null;
1245
- filterEditorProps = null;
1256
+ if (column.filter && column.filter.type) {
1257
+ switch (column.filter.type) {
1258
+ case 'select':
1259
+ filterEditor = SelectFilter;
1260
+ filterEditorProps = {
1261
+ multiple:
1262
+ column.filter.operator === 'inlist'
1263
+ ? true
1264
+ : false,
1265
+ wrapMultiple:
1266
+ column.filter.operator === 'inlist'
1267
+ ? false
1268
+ : true,
1269
+ placeholder: column.filter.placeholder
1270
+ ? column.filter.placeholder
1271
+ : 'All Options',
1272
+ dataSource: selectedDataSource,
1273
+ };
1274
+ break;
1275
+ case 'date':
1276
+ filterEditor = DateFilter;
1277
+ filterEditorProps = (props, { index }) => {
1278
+ return {
1279
+ dateFormat: 'DD-MM-YYYY',
1280
+ placeholder: 'All Dates',
1281
+ };
1282
+ };
1283
+ break;
1284
+ default:
1285
+ filterEditor = null;
1286
+ filterEditorProps = null;
1287
+ break;
1288
+ }
1246
1289
  }
1247
1290
 
1248
1291
  return {
@@ -1292,6 +1335,15 @@ const DataGrid = forwardRef(
1292
1335
  <span>{column.placeholder}</span>
1293
1336
  );
1294
1337
  } else {
1338
+ if (column.hasOwnProperty('format')) {
1339
+ if (column.format) {
1340
+ value =
1341
+ moment(value).format(
1342
+ 'DD-MM-YYYY'
1343
+ );
1344
+ }
1345
+ }
1346
+
1295
1347
  return <span>{value}</span>;
1296
1348
  }
1297
1349
  } else {
@@ -1635,7 +1687,10 @@ const DataGrid = forwardRef(
1635
1687
  header: 'Action',
1636
1688
  defaultWidth: 100,
1637
1689
  textAlign: 'center',
1638
- textVerticalAlign: 'top',
1690
+ textVerticalAlign:
1691
+ style && style.text_vertical_align
1692
+ ? style.text_vertical_align
1693
+ : 'center',
1639
1694
  render: ({ data }) => {
1640
1695
  return (
1641
1696
  <div className="tdactions">
@@ -1823,32 +1878,33 @@ const DataGrid = forwardRef(
1823
1878
  <>
1824
1879
  {renderSearch()}
1825
1880
  {renderCreateButton()}
1826
- {limit && limit > 0 && (
1881
+ {limit && limit > 0 ? (
1827
1882
  <>
1828
1883
  <ReactDataGrid
1884
+ {...tableSetting}
1829
1885
  columns={gridColumns}
1830
1886
  dataSource={data}
1831
1887
  filterValue={filterValue}
1832
1888
  limit={limit}
1833
1889
  enableColumnAutosize={false}
1834
1890
  enableColumnFilterContextMenu={false}
1835
- enableSelection={false}
1891
+ enableSelection={true}
1836
1892
  headerProps={headerProps}
1837
- idProperty={`visns-datagrid-${ajaxSetting.url.replace(
1838
- /\//g,
1839
- '-'
1840
- )}`}
1893
+ idProperty="id"
1841
1894
  minRowHeight={40}
1895
+ multiSelect={true}
1842
1896
  onFilterValueChange={(filterValue) => {
1843
1897
  handleFilterChange(filterValue);
1844
1898
  }}
1845
1899
  onLimitChange={setLimit}
1900
+ onSelectionChange={handleSelectionChnage}
1846
1901
  onSortInfoChange={handleSort}
1847
1902
  onRenderRow={onRenderRow}
1848
1903
  pagination
1849
1904
  renderColumnContextMenu={renderColumnContextMenu}
1850
1905
  rowClassName="vs-datagrid--row"
1851
1906
  rowHeight={null}
1907
+ selected={selected}
1852
1908
  showZebraRows={true}
1853
1909
  sortInfo={
1854
1910
  ajaxSetting.sortBy && ajaxSetting.sort
@@ -1864,7 +1920,7 @@ const DataGrid = forwardRef(
1864
1920
  style={gridStyle}
1865
1921
  />
1866
1922
  </>
1867
- )}
1923
+ ) : null}
1868
1924
  {audioSource && <audio src={audioSource} ref={audioRef} />}
1869
1925
  <Popup
1870
1926
  open={modalShow}
@@ -216,6 +216,8 @@ function Field({
216
216
  'colour',
217
217
  'plaindate',
218
218
  'plaindatetime',
219
+ 'plainrelation',
220
+ 'plainrelationarray',
219
221
  'plaintext',
220
222
  ].includes(settings.type)
221
223
  ) {
@@ -227,6 +229,42 @@ function Field({
227
229
  }
228
230
  };
229
231
 
232
+ const renderRelationText = (value) => {
233
+ let text = '';
234
+
235
+ const processKeys = (obj) => {
236
+ let tmpValue = obj;
237
+ settings.keys.forEach((key) => {
238
+ tmpValue = tmpValue[key];
239
+ });
240
+ return tmpValue;
241
+ };
242
+
243
+ if (Array.isArray(value) && value[0] && settings.keys) {
244
+ text = processKeys(value[0]);
245
+ } else if (typeof value === 'object' && value !== null) {
246
+ text = processKeys(value);
247
+ }
248
+
249
+ return text;
250
+ };
251
+
252
+ const renderRelationArrayText = (value) => {
253
+ let text = '';
254
+
255
+ if (value && settings.nameFrom) {
256
+ value.forEach((a, b) => {
257
+ if (b > 0) {
258
+ text += ', ';
259
+ }
260
+
261
+ text += a[settings.nameFrom];
262
+ });
263
+ }
264
+
265
+ return text;
266
+ };
267
+
230
268
  const renderInput = () => {
231
269
  switch (settings.type) {
232
270
  case 'text':
@@ -248,25 +286,78 @@ function Field({
248
286
  );
249
287
  case 'plaindate':
250
288
  return (
251
- <span className="fi__span">
289
+ <div
290
+ className="fi__div"
291
+ style={
292
+ settings.height
293
+ ? {
294
+ height: settings.height,
295
+ }
296
+ : {}
297
+ }
298
+ >
252
299
  {inputValue && inputValue !== 'null'
253
300
  ? moment(inputValue).format('DD-MM-YYYY')
254
301
  : ''}
255
- </span>
302
+ </div>
256
303
  );
257
304
  case 'plaindatetime':
258
305
  return (
259
- <span className="fi__span">
306
+ <div
307
+ className="fi__div"
308
+ style={
309
+ settings.height ? { height: settings.height } : {}
310
+ }
311
+ >
260
312
  {inputValue && inputValue !== 'null'
261
313
  ? moment(inputValue).format('DD-MM-YYYY hh:mm A')
262
314
  : ''}
263
- </span>
315
+ </div>
264
316
  );
265
317
  case 'plaintext':
266
318
  return (
267
- <span className="fi__span">
319
+ <div
320
+ className="fi__div"
321
+ style={
322
+ settings.height
323
+ ? {
324
+ height: settings.height,
325
+ }
326
+ : {}
327
+ }
328
+ >
268
329
  {inputValue && inputValue !== 'null' ? inputValue : ''}
269
- </span>
330
+ </div>
331
+ );
332
+ case 'plainrelation':
333
+ return (
334
+ <div
335
+ className="fi__div"
336
+ style={
337
+ settings.height
338
+ ? {
339
+ height: settings.height,
340
+ }
341
+ : {}
342
+ }
343
+ >
344
+ {renderRelationText(inputValue)}
345
+ </div>
346
+ );
347
+ case 'plainrelationarray':
348
+ return (
349
+ <div
350
+ className="fi__div"
351
+ style={
352
+ settings.height
353
+ ? {
354
+ height: settings.height,
355
+ }
356
+ : {}
357
+ }
358
+ >
359
+ {renderRelationArrayText(inputValue)}
360
+ </div>
270
361
  );
271
362
  case 'phone':
272
363
  case 'mobile':
@@ -532,6 +623,8 @@ function Field({
532
623
  'colour',
533
624
  'plaindate',
534
625
  'plaindatetime',
626
+ 'plainrelation',
627
+ 'plainrelationarray',
535
628
  'plaintext',
536
629
  ].includes(settings.type)
537
630
  ) {
@@ -8,18 +8,19 @@ import { CircleX } from 'akar-icons';
8
8
 
9
9
  import CustomFetch from './Fetch';
10
10
  import Field from './Field';
11
-
12
- function Form(props) {
13
- const {
14
- closeModal,
15
- columnId,
16
- formType,
17
- formSettings,
18
- fetchTable,
19
- style,
20
- updateForm,
21
- type,
22
- } = props;
11
+ import Table from './DataGrid';
12
+
13
+ function Form({
14
+ closeModal,
15
+ columnId,
16
+ formType,
17
+ formSettings,
18
+ fetchTable,
19
+ style,
20
+ type,
21
+ updateForm,
22
+ userProfile,
23
+ }) {
23
24
  const [inputClass, setInputClass] = useState({});
24
25
  const [formData, setFormData] = useState({});
25
26
  const [fetchTrigger, setFetchTrigger] = useState(false);
@@ -157,8 +158,6 @@ function Form(props) {
157
158
  }));
158
159
  };
159
160
 
160
- console.info(inputValue, action, id);
161
-
162
161
  switch (action.action) {
163
162
  case 'select-option':
164
163
  updateFormData({ [id]: inputValue });
@@ -221,66 +220,73 @@ function Form(props) {
221
220
 
222
221
  const id = e.target.dataset.jsonkey || e.target.dataset.name;
223
222
 
224
- const updateFormData = (dataToUpdate) => {
223
+ if (formData.hasOwnProperty(id)) {
225
224
  setFormData((prevState) => ({
226
225
  ...prevState,
227
- ...dataToUpdate,
226
+ [id]: e.target.checked,
228
227
  }));
229
- };
230
-
231
- const updatePostalData = (checked) => {
232
- const postalData = checked
233
- ? {
234
- postal_address1: formData.address1,
235
- postal_address2: formData.address2,
236
- postal_suburb: formData.suburb,
237
- postal_postcode: formData.postcode,
238
- postal_state: formData.state,
239
- postal_country: formData.country,
240
- }
241
- : {
242
- postal_address1: '',
243
- postal_address2: '',
244
- postal_suburb: '',
245
- postal_postcode: '',
246
- postal_state: '',
247
- postal_country: '',
248
- };
249
-
250
- updateFormData(postalData);
251
- };
252
-
253
- if (id === 'same_address') {
254
- updatePostalData(e.target.checked);
255
- }
228
+ } else {
229
+ const updateFormData = (dataToUpdate) => {
230
+ setFormData((prevState) => ({
231
+ ...prevState,
232
+ ...dataToUpdate,
233
+ }));
234
+ };
235
+
236
+ const updatePostalData = (checked) => {
237
+ const postalData = checked
238
+ ? {
239
+ postal_address1: formData.address1,
240
+ postal_address2: formData.address2,
241
+ postal_suburb: formData.suburb,
242
+ postal_postcode: formData.postcode,
243
+ postal_state: formData.state,
244
+ postal_country: formData.country,
245
+ }
246
+ : {
247
+ postal_address1: '',
248
+ postal_address2: '',
249
+ postal_suburb: '',
250
+ postal_postcode: '',
251
+ postal_state: '',
252
+ postal_country: '',
253
+ };
254
+
255
+ updateFormData(postalData);
256
+ };
257
+
258
+ if (id === 'same_address') {
259
+ updatePostalData(e.target.checked);
260
+ }
256
261
 
257
- if (e.target.dataset.show) {
258
- const _show = e.target.dataset.show
259
- ? JSON.parse(e.target.dataset.show)
260
- : [];
262
+ if (e.target.dataset.show) {
263
+ const _show = e.target.dataset.show
264
+ ? JSON.parse(e.target.dataset.show)
265
+ : [];
261
266
 
262
- if (_show.length > 0) {
263
- const updatedFields = formSettings.fields.map((field) => {
264
- const showObject = _show.find(
265
- (showItem) => showItem.id === field.id
266
- );
267
- if (
268
- showObject &&
269
- showObject.value &&
270
- showObject.value.length > 0
271
- ) {
272
- field.hide = !showObject.value.includes(
273
- e.target.checked
267
+ if (_show.length > 0) {
268
+ const updatedFields = formSettings.fields.map((field) => {
269
+ const showObject = _show.find(
270
+ (showItem) => showItem.id === field.id
274
271
  );
275
- }
276
- return field;
277
- });
272
+ if (
273
+ showObject &&
274
+ showObject.value &&
275
+ showObject.value.length > 0
276
+ ) {
277
+ field.hide = !showObject.value.includes(
278
+ e.target.checked
279
+ );
280
+ }
281
+ return field;
282
+ });
278
283
 
279
- if (updateForm) {
280
- updateForm((prevState) => ({
281
- ...prevState,
282
- fields: updatedFields,
283
- }));
284
+ if (updateForm) {
285
+ updateForm((prevState) => ({
286
+ ...prevState,
287
+ fields: updatedFields,
288
+ }));
289
+ }
284
290
  }
285
291
  }
286
292
  }
@@ -699,7 +705,8 @@ function Form(props) {
699
705
  }
700
706
  } else if (
701
707
  field &&
702
- field.hasOwnProperty('relation')
708
+ field.hasOwnProperty('relation') &&
709
+ !field.multi
703
710
  ) {
704
711
  const relationTypes = [
705
712
  'multi-dropdown',
@@ -830,35 +837,71 @@ function Form(props) {
830
837
  ) {
831
838
  return null;
832
839
  }
833
- return (
834
- <Field
835
- settings={item}
836
- key={
837
- index +
838
- '-fields-' +
839
- (item.hasOwnProperty('key')
840
- ? item.id + '-' + item.key
841
- : item.id)
842
- }
843
- inputValue={
844
- item.hasOwnProperty('key')
845
- ? formData[item.key]
846
- : formData[item.id]
847
- }
848
- inputClass={inputClass}
849
- onChange={handleChange}
850
- onChangeDate={handleChangeDate}
851
- onChangeSelect={handleChangeSelect}
852
- onChangeRicheditor={handleChangeRicheditor}
853
- onChangeToggle={handleChangeToggle}
854
- onChangeNumberFormat={handleChangeNumberFormat}
855
- autocompleteCallback={autocompleteSelect}
856
- childDropdownCallback={childDropdownCallback}
857
- uploadProgress={uploadProgress}
858
- setFormData={setFormData}
859
- style={style}
860
- />
861
- );
840
+
841
+ switch (item.type) {
842
+ case 'table':
843
+ return (
844
+ <div
845
+ className="grid__full"
846
+ key={
847
+ index +
848
+ '-fields-' +
849
+ (item.hasOwnProperty('key')
850
+ ? item.id + '-' + item.key
851
+ : item.id)
852
+ }
853
+ >
854
+ <Table
855
+ ajaxSetting={item.ajaxSetting}
856
+ form={item.form}
857
+ columns={item.columns}
858
+ settings={item.settings}
859
+ style={
860
+ userProfile?.settings?.style ||
861
+ {}
862
+ }
863
+ />
864
+ </div>
865
+ );
866
+ default:
867
+ return (
868
+ <Field
869
+ settings={item}
870
+ key={
871
+ index +
872
+ '-fields-' +
873
+ (item.hasOwnProperty('key')
874
+ ? item.id + '-' + item.key
875
+ : item.id)
876
+ }
877
+ inputValue={
878
+ item.hasOwnProperty('key')
879
+ ? formData[item.key]
880
+ : formData[item.id]
881
+ }
882
+ inputClass={inputClass}
883
+ onChange={handleChange}
884
+ onChangeDate={handleChangeDate}
885
+ onChangeSelect={handleChangeSelect}
886
+ onChangeRicheditor={
887
+ handleChangeRicheditor
888
+ }
889
+ onChangeToggle={handleChangeToggle}
890
+ onChangeNumberFormat={
891
+ handleChangeNumberFormat
892
+ }
893
+ autocompleteCallback={
894
+ autocompleteSelect
895
+ }
896
+ childDropdownCallback={
897
+ childDropdownCallback
898
+ }
899
+ uploadProgress={uploadProgress}
900
+ setFormData={setFormData}
901
+ style={style}
902
+ />
903
+ );
904
+ }
862
905
  })}
863
906
  <div className="formItem fwItem">
864
907
  <motion.div
@@ -2,7 +2,7 @@ import React from 'react';
2
2
 
3
3
  function TableFilter({ filters, setFilters, setSettings, type }) {
4
4
  const filterTable = (e) => {
5
- const { id, parent, type, url, value } = e.target.dataset;
5
+ const { id, parent, filtertype, url, value } = e.target.dataset;
6
6
 
7
7
  const updateFilter = (filter) => {
8
8
  let isActive = false;
@@ -14,7 +14,7 @@ function TableFilter({ filters, setFilters, setSettings, type }) {
14
14
  }
15
15
 
16
16
  const isParent =
17
- parent && type === 'children' && filter.id === parent;
17
+ parent && filtertype === 'children' && filter.id === parent;
18
18
 
19
19
  const updatedChildren = isParent
20
20
  ? filter.children.map((child) => ({
@@ -95,7 +95,7 @@ function TableFilter({ filters, setFilters, setSettings, type }) {
95
95
  <li className={d.class}>
96
96
  <button
97
97
  data-id={d.id}
98
- data-type="parent"
98
+ data-filtertype="parent"
99
99
  data-url={d.url || ''}
100
100
  data-value={d.hasOwnProperty('value') ? d.value : ''}
101
101
  onClick={filterTable}
@@ -109,7 +109,7 @@ function TableFilter({ filters, setFilters, setSettings, type }) {
109
109
  <li>
110
110
  <a
111
111
  data-id={d.id}
112
- data-type="parent"
112
+ data-filtertype="parent"
113
113
  data-url={d.url || ''}
114
114
  data-value={d.hasOwnProperty('value') ? d.value : ''}
115
115
  onClick={filterTable}
@@ -130,7 +130,7 @@ function TableFilter({ filters, setFilters, setSettings, type }) {
130
130
  : ''
131
131
  }
132
132
  data-url={child.url || ''}
133
- data-type="children"
133
+ data-filtertype="children"
134
134
  data-parent={d.id}
135
135
  onClick={filterTable}
136
136
  >
@@ -14,6 +14,7 @@ import CustomFetch from '../Fetch';
14
14
  import Form from '../Form';
15
15
  import Table from '../DataGrid';
16
16
  import TableFilter from '../TableFilter';
17
+ import { toast } from 'react-toastify';
17
18
 
18
19
  function GenericDetail({ setting, urlParam, userProfile }) {
19
20
  const routeParams = useParams();
@@ -32,6 +33,39 @@ function GenericDetail({ setting, urlParam, userProfile }) {
32
33
  const [windowHeight, setWindowHeight] = useState(window.innerHeight);
33
34
 
34
35
  /** General Functions */
36
+ const handleStageUpdate = async (stage) => {
37
+ const activeTab = subnav.find((s) => s.show === true);
38
+
39
+ if (activeTab) {
40
+ const activeTabConfig = tabs.find((t) => t.id === activeTab.id);
41
+
42
+ if (
43
+ activeTabConfig.stages.url &&
44
+ activeTabConfig.stages.url !== '' &&
45
+ activeTabConfig.stages.key &&
46
+ activeTabConfig.stages.key !== '' &&
47
+ activeTabConfig.stages.urlParam &&
48
+ activeTabConfig.stages.urlParam !== ''
49
+ ) {
50
+ const url = `${activeTabConfig.stages.url}/${routeParams[urlParam]}`;
51
+
52
+ const res = await CustomFetch(url, 'POST', {
53
+ [activeTabConfig.stages.key]: stage.id,
54
+ });
55
+
56
+ if (res.data.error === '') {
57
+ toast.success(
58
+ `Job stage has been updated to ${stage.label}`
59
+ );
60
+
61
+ handleReload();
62
+ } else {
63
+ toast.error(res.data.error);
64
+ }
65
+ }
66
+ }
67
+ };
68
+
35
69
  const handleReload = () => {
36
70
  const min = 0;
37
71
  const max = 999999999;
@@ -296,56 +330,118 @@ function GenericDetail({ setting, urlParam, userProfile }) {
296
330
  </div>
297
331
  );
298
332
  case 'form':
333
+ const updateValueFromParam = (item, value) => {
334
+ if (item && item.urlParam) {
335
+ item.value = value;
336
+ }
337
+ };
338
+
339
+ const updateFieldValues = (
340
+ formConfig,
341
+ routeParams
342
+ ) => {
343
+ formConfig.stages.data.forEach((stage) => {
344
+ const stageFormFields =
345
+ stage.form?.fields || [];
346
+ const stageAjaxWhereFields = [];
347
+
348
+ stageFormFields.forEach((field) => {
349
+ if (field.type === 'table') {
350
+ if (
351
+ field.ajaxSetting &&
352
+ field.ajaxSetting.where
353
+ ) {
354
+ stageAjaxWhereFields.push(
355
+ ...field.ajaxSetting.where
356
+ );
357
+ }
358
+
359
+ const tableFormFields =
360
+ field.form?.fields || [];
361
+ tableFormFields.forEach(
362
+ (formField) => {
363
+ updateValueFromParam(
364
+ formField,
365
+ routeParams[
366
+ formField.urlParam
367
+ ]
368
+ );
369
+ }
370
+ );
371
+ }
372
+ });
373
+
374
+ stageAjaxWhereFields.forEach((where) => {
375
+ updateValueFromParam(
376
+ where,
377
+ routeParams[where.urlParam]
378
+ );
379
+ });
380
+ });
381
+ };
382
+
383
+ const formConfig = activeTabConfig;
384
+ updateFieldValues(formConfig, routeParams);
385
+
299
386
  return (
300
387
  <>
301
- {activeTabConfig.stages?.data?.length >
302
- 0 && (
388
+ {formConfig.stages?.data?.length > 0 && (
303
389
  <div className="grid">
304
390
  <div className="grid__row">
305
391
  <div className="grid__full">
306
- {activeTabConfig.stages.data.map(
392
+ {formConfig.stages.data.map(
307
393
  (stage) =>
308
- stage.form && (
309
- <div
310
- className="gridtxt"
394
+ data[
395
+ formConfig
396
+ .stages.key
397
+ ] === stage.id && (
398
+ <React.Fragment
311
399
  key={`stage-form-${stage.id}`}
312
400
  >
313
- {stage.form
314
- .update
315
- .title && (
316
- <div className="gridtxt__header">
317
- <span>
318
- {
319
- stage
320
- .form
321
- .update
322
- .title
401
+ {stage.form && (
402
+ <div className="gridtxt">
403
+ {stage
404
+ .form
405
+ .update
406
+ .title && (
407
+ <div className="gridtxt__header">
408
+ <span>
409
+ {
410
+ stage
411
+ .form
412
+ .update
413
+ .title
414
+ }
415
+ </span>
416
+ </div>
417
+ )}
418
+ <Form
419
+ columnId={
420
+ routeParams[
421
+ urlParam
422
+ ]
423
+ }
424
+ formSettings={
425
+ stage.form
426
+ }
427
+ formType="update"
428
+ style={
429
+ userProfile
430
+ ?.settings
431
+ ?.style ||
432
+ {}
323
433
  }
324
- </span>
434
+ type="inline"
435
+ updateForm={
436
+ setFormData
437
+ }
438
+ userProfile={
439
+ userProfile
440
+ }
441
+ />
325
442
  </div>
326
443
  )}
327
- <Form
328
- columnId={
329
- routeParams[
330
- urlParam
331
- ]
332
- }
333
- formSettings={
334
- stage.form
335
- }
336
- formType="update"
337
- style={
338
- userProfile
339
- ?.settings
340
- ?.style ||
341
- {}
342
- }
343
- updateForm={
344
- setFormData
345
- }
346
- type="inline"
347
- />
348
- </div>
444
+ </React.Fragment>
349
445
  )
350
446
  )}
351
447
  </div>
@@ -353,18 +449,14 @@ function GenericDetail({ setting, urlParam, userProfile }) {
353
449
  </div>
354
450
  )}
355
451
 
356
- {activeTabConfig.form && (
452
+ {formConfig.form && (
357
453
  <div className="gridtxt">
358
454
  <div className="gridtxt__header">
359
- <span>
360
- {activeTabConfig.title}
361
- </span>
455
+ <span>{formConfig.title}</span>
362
456
  </div>
363
457
  <Form
364
458
  columnId={routeParams[urlParam]}
365
- formSettings={
366
- activeTabConfig.form
367
- }
459
+ formSettings={formConfig.form}
368
460
  formType="update"
369
461
  style={
370
462
  userProfile?.settings
@@ -713,6 +805,11 @@ function GenericDetail({ setting, urlParam, userProfile }) {
713
805
  ? 'opp-complete'
714
806
  : ''
715
807
  }
808
+ onClick={(e) => {
809
+ e.preventDefault();
810
+
811
+ handleStageUpdate(stage);
812
+ }}
716
813
  >
717
814
  <a>
718
815
  {stage.label}
@@ -1,25 +1,55 @@
1
1
  import React, { useEffect, useState } from 'react';
2
2
  import { Outlet } from 'react-router-dom';
3
+ import { toast } from 'react-toastify';
3
4
  import Table from '../DataGrid';
4
5
  import TableFilter from '../TableFilter';
5
6
 
6
7
  function GenericIndex({ layout = 'full', setting, userProfile }) {
7
- const { ajaxSetting, columns, form, filters, page, settings, tabs } =
8
- setting;
8
+ const {
9
+ ajaxSetting,
10
+ columns,
11
+ filters,
12
+ form,
13
+ functions,
14
+ page,
15
+ settings,
16
+ tableSetting,
17
+ tabs,
18
+ } = setting;
9
19
 
10
20
  const [config, setConfig] = useState({});
21
+ const [rowsSelected, setRowsSelected] = useState({});
11
22
  const [subnav, setSubnav] = useState(filters || []);
12
23
  const [total, setTotal] = useState(0);
13
24
  const [windowHeight, setWindowHeight] = useState(window.innerHeight);
14
25
  const pageTitle = page?.title;
15
26
 
27
+ const isObjectEmpty = (obj) => {
28
+ return Object.keys(obj).length === 0;
29
+ };
30
+
31
+ const handleCheckboxUpdate = (e) => {
32
+ e.preventDefault();
33
+ const message = functions.checkboxUpdate.message;
34
+
35
+ console.info(isObjectEmpty(rowsSelected), rowsSelected);
36
+
37
+ if (isObjectEmpty(rowsSelected)) {
38
+ toast.warning(message.warning);
39
+ } else {
40
+ console.info(rowsSelected);
41
+ }
42
+ };
43
+
16
44
  useEffect(() => {
17
45
  if (!tabs || tabs.length === 0) {
18
46
  setConfig(() => ({
19
47
  ajaxSetting,
20
48
  form,
21
49
  columns,
50
+ setRowsSelected,
22
51
  settings,
52
+ tableSetting,
23
53
  }));
24
54
  }
25
55
 
@@ -38,7 +68,9 @@ function GenericIndex({ layout = 'full', setting, userProfile }) {
38
68
  ajaxSetting: activeTabConfig.ajaxSetting,
39
69
  form: activeTabConfig.form,
40
70
  columns: activeTabConfig.columns,
71
+ setRowsSelected,
41
72
  settings: activeTabConfig.settings,
73
+ tableSetting,
42
74
  }));
43
75
  }
44
76
  }
@@ -74,16 +106,40 @@ function GenericIndex({ layout = 'full', setting, userProfile }) {
74
106
  </div>
75
107
 
76
108
  {config.ajaxSetting &&
77
- config.form &&
78
- config.columns &&
79
- config.settings && (
80
- <>
81
- <div className="grid">
82
- {subnav && subnav.length > 0 ? (
83
- <div className="grid__subrow">
84
- {layout === 'full' ? (
85
- <>
86
- <div className={`grid__subnav`}>
109
+ config.form &&
110
+ config.columns &&
111
+ config.settings ? (
112
+ <>
113
+ <div className="grid">
114
+ {subnav && subnav.length > 0 ? (
115
+ <div className="grid__subrow">
116
+ {layout === 'full' ? (
117
+ <>
118
+ <div className={`grid__subnav`}>
119
+ <TableFilter
120
+ filters={subnav}
121
+ setFilters={setSubnav}
122
+ setSettings={setConfig}
123
+ type={layout}
124
+ />
125
+ </div>
126
+ <div className={`grid__subcontent`}>
127
+ <Table
128
+ {...config}
129
+ gridHeight={windowHeight * 0.65}
130
+ setConfig={setConfig}
131
+ setTotal={setTotal}
132
+ style={
133
+ userProfile?.settings
134
+ ?.style || {}
135
+ }
136
+ />
137
+ </div>
138
+ </>
139
+ ) : (
140
+ <div className="grid__full">
141
+ <div className="tabcontent">
142
+ <div className={`tabcontent__menu`}>
87
143
  <TableFilter
88
144
  filters={subnav}
89
145
  setFilters={setSubnav}
@@ -91,7 +147,7 @@ function GenericIndex({ layout = 'full', setting, userProfile }) {
91
147
  type={layout}
92
148
  />
93
149
  </div>
94
- <div className={`grid__subcontent`}>
150
+ <div className={`tabcontent__main`}>
95
151
  <Table
96
152
  {...config}
97
153
  gridHeight={
@@ -105,58 +161,35 @@ function GenericIndex({ layout = 'full', setting, userProfile }) {
105
161
  }
106
162
  />
107
163
  </div>
108
- </>
109
- ) : (
110
- <div className="grid__full">
111
- <div className="tabcontent">
112
- <div
113
- className={`tabcontent__menu`}
114
- >
115
- <TableFilter
116
- filters={subnav}
117
- setFilters={setSubnav}
118
- setSettings={setConfig}
119
- type={layout}
120
- />
121
- </div>
122
- <div
123
- className={`tabcontent__main`}
124
- >
125
- <Table
126
- {...config}
127
- gridHeight={
128
- windowHeight * 0.65
129
- }
130
- setConfig={setConfig}
131
- setTotal={setTotal}
132
- style={
133
- userProfile
134
- ?.settings
135
- ?.style || {}
136
- }
137
- />
138
- </div>
139
- </div>
140
164
  </div>
141
- )}
142
- </div>
143
- ) : (
144
- <div className="grid__full">
145
- <Table
146
- {...config}
147
- gridHeight={windowHeight * 0.65}
148
- setConfig={setConfig}
149
- setTotal={setTotal}
150
- style={
151
- userProfile?.settings?.style || {}
152
- }
153
- />
154
- </div>
155
- )}
156
- </div>
157
- <Outlet />
158
- </>
159
- )}
165
+ </div>
166
+ )}
167
+ </div>
168
+ ) : (
169
+ <div className="grid__full">
170
+ <Table
171
+ {...config}
172
+ gridHeight={windowHeight * 0.65}
173
+ setConfig={setConfig}
174
+ setTotal={setTotal}
175
+ style={userProfile?.settings?.style || {}}
176
+ />
177
+ </div>
178
+ )}
179
+ </div>
180
+ <Outlet />
181
+ </>
182
+ ) : null}
183
+
184
+ {functions && functions.checkboxUpdate ? (
185
+ <div className="polActions">
186
+ <button className="btn" onClick={handleCheckboxUpdate}>
187
+ {functions.checkboxUpdate.label
188
+ ? functions.checkboxUpdate.label
189
+ : 'Update'}
190
+ </button>
191
+ </div>
192
+ ) : null}
160
193
  </>
161
194
  );
162
195
  }
package/package.json CHANGED
@@ -42,7 +42,7 @@
42
42
  "react-dom": "^17.0.1"
43
43
  },
44
44
  "name": "@visns-studio/visns-components",
45
- "version": "2.5.12",
45
+ "version": "2.5.14",
46
46
  "description": "Various packages to assist in the development of our Custom Applications.",
47
47
  "main": "index.js",
48
48
  "scripts": {