@visns-studio/visns-components 4.10.47 → 5.0.0

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.
Files changed (41) hide show
  1. package/package.json +28 -30
  2. package/src/components/cms/Field.jsx +39 -63
  3. package/src/components/crm/Breadcrumb.jsx +2 -18
  4. package/src/components/crm/DataGrid.jsx +23 -379
  5. package/src/components/crm/Field.jsx +16 -215
  6. package/src/components/crm/Form.jsx +81 -79
  7. package/src/components/crm/MultiSelect.jsx +25 -71
  8. package/src/components/crm/Navigation.jsx +7 -11
  9. package/src/components/crm/QrCode.jsx +8 -12
  10. package/src/components/crm/QuickAction.jsx +1 -0
  11. package/src/components/crm/generic/GenericAuth.jsx +2 -6
  12. package/src/components/crm/generic/GenericDashboard.jsx +30 -340
  13. package/src/components/crm/generic/GenericDetail.jsx +37 -80
  14. package/src/components/crm/generic/GenericDynamic.jsx +76 -94
  15. package/src/components/crm/generic/GenericFormBuilder.jsx +19 -79
  16. package/src/components/crm/generic/GenericIndex.jsx +1 -2
  17. package/src/components/crm/generic/styles/GenericDetail.module.scss +0 -51
  18. package/src/components/crm/generic/styles/GenericFormBuilder.module.scss +0 -5
  19. package/src/components/crm/styles/DataGrid.module.scss +25 -0
  20. package/src/components/crm/styles/Field.module.scss +0 -210
  21. package/src/components/crm/styles/Form.module.scss +0 -78
  22. package/src/components/crm/styles/Navigation.module.scss +4 -10
  23. package/src/components/crm/styles/QrCode.module.scss +0 -18
  24. package/src/index.js +0 -8
  25. package/src/components/crm/generic/styles/GenericDashboard.module.scss +0 -325
  26. package/src/components/crm/sketch/SketchField.jsx +0 -395
  27. package/src/components/crm/sketch/arrow.jsx +0 -108
  28. package/src/components/crm/sketch/circle.jsx +0 -75
  29. package/src/components/crm/sketch/default-tool.jsx +0 -16
  30. package/src/components/crm/sketch/fabrictool.jsx +0 -22
  31. package/src/components/crm/sketch/history.jsx +0 -144
  32. package/src/components/crm/sketch/json/config.json +0 -14
  33. package/src/components/crm/sketch/line.jsx +0 -64
  34. package/src/components/crm/sketch/pan.jsx +0 -48
  35. package/src/components/crm/sketch/pencil.jsx +0 -36
  36. package/src/components/crm/sketch/rectangle-label-object.jsx +0 -69
  37. package/src/components/crm/sketch/rectangle-label.jsx +0 -93
  38. package/src/components/crm/sketch/rectangle.jsx +0 -76
  39. package/src/components/crm/sketch/select.jsx +0 -16
  40. package/src/components/crm/sketch/tools.jsx +0 -11
  41. package/src/components/crm/sketch/utils.jsx +0 -67
@@ -2,7 +2,7 @@ import '../styles/global.css';
2
2
 
3
3
  import React, { useEffect, useRef, useState } from 'react';
4
4
  import DatePicker from 'react-datepicker';
5
- import _, { forEach } from 'lodash';
5
+ import _ from 'lodash';
6
6
  import Toggle from 'react-toggle';
7
7
  import moment from 'moment';
8
8
  import parse from 'html-react-parser';
@@ -11,18 +11,7 @@ import { BlockPicker } from 'react-color';
11
11
  import { CopyToClipboard } from 'react-copy-to-clipboard';
12
12
  import { NumericFormat, PatternFormat } from 'react-number-format';
13
13
  import { Editor } from '@tinymce/tinymce-react';
14
- import {
15
- Edit,
16
- ChevronRight,
17
- Circle,
18
- CircleFill,
19
- CirclePlus,
20
- CloudDownload,
21
- Minus,
22
- ArrowBack,
23
- TrashCan,
24
- CircleX,
25
- } from 'akar-icons';
14
+ import { CirclePlus, CloudDownload, Copy, TrashCan } from 'akar-icons';
26
15
  import { toast } from 'react-toastify';
27
16
  import styles from './styles/Field.module.scss';
28
17
 
@@ -30,15 +19,13 @@ import CustomFetch from './Fetch';
30
19
  import Form from './Form';
31
20
  import MultiSelect from './MultiSelect';
32
21
  import Select from './Select';
33
- import SketchConfig from './sketch/json/config.json';
34
- import SketchField from './sketch/SketchField';
35
- import Tools from './sketch/tools';
36
22
  import VisnsAsyncSelect from './AsyncSelect';
37
23
  import VisnsAutocomplete from './Autocomplete';
38
24
  import VisnsDropZone from '../cms/DropZone';
39
25
 
40
26
  import 'react-toggle/style.css';
41
27
  import 'react-datepicker/dist/react-datepicker.css';
28
+ import Item from '../cms/sorting/Item';
42
29
 
43
30
  function Field({
44
31
  api,
@@ -51,7 +38,6 @@ function Field({
51
38
  inputClass,
52
39
  mapbox,
53
40
  onChange,
54
- onChangeCanvas,
55
41
  onChangeCheckbox,
56
42
  onChangeCheckboxManual,
57
43
  onChangeColour,
@@ -79,30 +65,6 @@ function Field({
79
65
  const [formItemClass, setFormItemClass] = useState('');
80
66
  const [options, setOptions] = useState([]);
81
67
 
82
- /** Canvas States */
83
- const [canvasPopupOpen, setCanvasPopupOpen] = useState(false);
84
- const [canvasUrl, setCanvasUrl] = useState('');
85
- const [sketchTool, setSketchTool] = useState(Tools.Pencil);
86
- const [toolClass, setToolClass] = useState('pencil');
87
- const [lineWidth, setLineWidth] = useState(2);
88
- const [lineColor, setLineColor] = useState('rgba(0, 0, 0, 1)');
89
- const sketchRef = useRef(null);
90
-
91
- /** Canvas Functions */
92
- const handleCanvasSave = async () => {
93
- // Save the current canvas state
94
- const canvasState = sketchRef.current.saveCanvasState();
95
-
96
- // Export the canvas image data as a Base64 string
97
- const imageData = await sketchRef.current.exportCanvasImage(); // Use await here
98
-
99
- // Call the onChangeCanvas function with both values
100
- onChangeCanvas(settings, canvasState, imageData);
101
-
102
- // Close the canvas popup
103
- setCanvasPopupOpen(false);
104
- };
105
-
106
68
  const cover = {
107
69
  position: 'fixed',
108
70
  top: '0px',
@@ -192,13 +154,6 @@ function Field({
192
154
  setFormItemClass(`${styles.formItem}`);
193
155
  break;
194
156
  }
195
-
196
- if (settings.type === 'canvas') {
197
- const canvasType = SketchConfig.canvasTypes.find(
198
- (a) => a.id === settings.canvasType
199
- );
200
- setCanvasUrl(canvasType.url);
201
- }
202
157
  }, [settings, counter]);
203
158
 
204
159
  const fetchChildDropdownData = (s, v) => {
@@ -227,12 +182,6 @@ function Field({
227
182
  filter.orderBy = a.orderBy;
228
183
  }
229
184
 
230
- if (a.where) {
231
- forEach(a.where, (w) => {
232
- filter.where.push(w);
233
- });
234
- }
235
-
236
185
  CustomFetch(a.url, 'POST', filter, function (result) {
237
186
  childDropdownCallback({
238
187
  id: a.id,
@@ -258,12 +207,6 @@ function Field({
258
207
  filter.orderBy = s.orderBy;
259
208
  }
260
209
 
261
- if (s.where) {
262
- forEach(s.where, (w) => {
263
- filter.where.push(w);
264
- });
265
- }
266
-
267
210
  CustomFetch(s.child.url, 'POST', filter, function (result) {
268
211
  childDropdownCallback({
269
212
  id: s.child.id,
@@ -315,7 +258,6 @@ function Field({
315
258
  'address',
316
259
  'async-dropdown-ajax',
317
260
  'checkbox',
318
- 'canvas',
319
261
  'colour',
320
262
  'copytoclipboard',
321
263
  'date',
@@ -444,17 +386,6 @@ function Field({
444
386
  dataOptions={dataOptions}
445
387
  />
446
388
  );
447
- case 'canvas':
448
- console.info('a');
449
- return canvasUrl ? (
450
- <div
451
- onClick={() => setCanvasPopupOpen(true)}
452
- style={{ cursor: 'pointer' }}
453
- >
454
- <img src={canvasUrl} width="200" alt="Canvas Preview" />
455
- </div>
456
- ) : null;
457
-
458
389
  case 'checkbox':
459
390
  return (
460
391
  <div
@@ -742,8 +673,9 @@ function Field({
742
673
  className={inputClass[settings.id]}
743
674
  onChange={onChange}
744
675
  />
676
+ {console.info(inputValue)}
745
677
  {inputValue && inputValue.length > 0 && (
746
- <ul className={styles['file-list']}>
678
+ <ul className="file-list">
747
679
  {inputValue.map((file, index) => (
748
680
  <li
749
681
  key={`file-${settings.id}-${index}`}
@@ -754,9 +686,7 @@ function Field({
754
686
  ? file.file_name
755
687
  : ''}
756
688
  <button
757
- className={
758
- styles['trash-button']
759
- }
689
+ className="trash-button"
760
690
  onClick={(e) => {
761
691
  e.preventDefault();
762
692
  e.stopPropagation();
@@ -1042,16 +972,6 @@ function Field({
1042
972
  }
1043
973
  dataOptions={dataOptions}
1044
974
  style={style}
1045
- isCreatable={
1046
- settings.hasOwnProperty('isCreatable')
1047
- ? settings.isCreatable
1048
- : false
1049
- }
1050
- creatableConfig={
1051
- settings.hasOwnProperty('creatableConfig')
1052
- ? settings.creatableConfig
1053
- : {}
1054
- }
1055
975
  />
1056
976
  {settings.create && (
1057
977
  <button
@@ -1513,9 +1433,7 @@ function Field({
1513
1433
  <th></th>
1514
1434
  {tableRadioColumns.map((column, columnKey) => (
1515
1435
  <th
1516
- key={`${settings.id}-${
1517
- column.id
1518
- }-${Math.random()}`}
1436
+ key={`${settings.id}-${column.id}`}
1519
1437
  style={{
1520
1438
  width: '250px',
1521
1439
  whiteSpace: 'nowrap',
@@ -1536,17 +1454,21 @@ function Field({
1536
1454
  </tr>
1537
1455
  </thead>
1538
1456
  <tbody>
1539
- {tableRadioRows.map((row, rowKey) => (
1457
+ {tableRadioRows.map((row) => (
1540
1458
  <tr
1541
1459
  key={`table-radio-row-${settings.id}-${row.id}`}
1542
1460
  >
1543
- <td>{row.label}</td>
1461
+ <td
1462
+ style={{
1463
+ whiteSpace: 'nowrap',
1464
+ }}
1465
+ >
1466
+ {row.label}
1467
+ </td>
1544
1468
  {tableRadioColumns.map((cell, cellKey) => (
1545
1469
  <td
1546
- key={`table-radio-cell-${settings.id}-${row.id}-${cell.id}-${cellKey}-${rowKey}`}
1470
+ key={`table-radio-cell-${settings.id}-${row.id}-${cell.id}`}
1547
1471
  style={{
1548
- width: '250px',
1549
- whiteSpace: 'nowrap',
1550
1472
  textAlign:
1551
1473
  tableRadioColumns[cellKey]
1552
1474
  .type === '' ||
@@ -1776,7 +1698,6 @@ function Field({
1776
1698
  'address',
1777
1699
  'async-dropdown-ajax',
1778
1700
  'checkbox',
1779
- 'canvas',
1780
1701
  'colour',
1781
1702
  'copytoclipboard',
1782
1703
  'date',
@@ -1974,126 +1895,6 @@ function Field({
1974
1895
  updateForm={setChildForm}
1975
1896
  />
1976
1897
  </Popup>
1977
- <Popup
1978
- open={canvasPopupOpen}
1979
- onClose={() => setCanvasPopupOpen(false)}
1980
- closeOnDocumentClick={false}
1981
- >
1982
- <div className={`${styles.modalwrap} ${styles['top--modal']}`}>
1983
- <div className={styles.modal}>
1984
- <div className={styles.modal__header}>
1985
- <h1>Edit Canvas</h1>
1986
- <button
1987
- className={styles.modal__close}
1988
- onClick={() => setCanvasPopupOpen(false)}
1989
- >
1990
- <CircleX size={24} color="#FFF" />
1991
- </button>
1992
- </div>
1993
- <div
1994
- className={`${styles.modal__content} ${styles.drawDiv}`}
1995
- >
1996
- {/* Sketch Tools */}
1997
- <ul className={styles.drawactions}>
1998
- <li>
1999
- <button
2000
- onClick={() => {
2001
- handleCanvasSave();
2002
- }}
2003
- className={styles['drawactions--save']}
2004
- >
2005
- <CloudDownload size={16} /> Save
2006
- </button>
2007
- </li>
2008
- <li>
2009
- <button
2010
- onClick={() => {
2011
- setSketchTool(Tools.Pencil);
2012
- setToolClass('pencil');
2013
- }}
2014
- className={`${
2015
- styles['drawaction-pencil']
2016
- } ${
2017
- toolClass === 'pencil'
2018
- ? styles['drawactive']
2019
- : ''
2020
- }`}
2021
- >
2022
- <Edit size={16} /> Pencil
2023
- </button>
2024
- </li>
2025
- <li>
2026
- <button
2027
- onClick={() => {
2028
- setSketchTool(Tools.Line);
2029
-
2030
- setToolClass('line');
2031
- }}
2032
- className={`${
2033
- styles['drawaction-line']
2034
- } ${
2035
- toolClass === 'line'
2036
- ? styles['drawactive']
2037
- : ''
2038
- }`}
2039
- >
2040
- <Minus size={16} /> Line
2041
- </button>
2042
- </li>
2043
- <li>
2044
- <button
2045
- onClick={() => {
2046
- setSketchTool(Tools.Circle);
2047
-
2048
- setToolClass('circle');
2049
- }}
2050
- className={`${
2051
- styles['drawaction-circle']
2052
- } ${
2053
- toolClass === 'circle'
2054
- ? styles['drawactive']
2055
- : ''
2056
- }`}
2057
- >
2058
- <Circle size={16} /> Circle
2059
- </button>
2060
- </li>
2061
- <li>
2062
- <button
2063
- onClick={() => sketchRef.current.undo()}
2064
- className={styles['drawaction-undo']}
2065
- >
2066
- <ArrowBack size={16} /> Undo
2067
- </button>
2068
- </li>
2069
- <li>
2070
- <button
2071
- onClick={() =>
2072
- sketchRef.current.clearCanvas()
2073
- }
2074
- className={styles['drawactions--clear']}
2075
- >
2076
- <TrashCan size={16} /> Clear
2077
- </button>
2078
- </li>
2079
- </ul>
2080
- {(canvasUrl || inputValue?.state) && (
2081
- <SketchField
2082
- ref={sketchRef}
2083
- width={740}
2084
- height={800}
2085
- tool={sketchTool}
2086
- lineColor={lineColor}
2087
- lineWidth={lineWidth}
2088
- className={styles.drawcontainer}
2089
- backgroundImage={canvasUrl}
2090
- defaultValue={inputValue?.state || null}
2091
- />
2092
- )}
2093
- </div>
2094
- </div>
2095
- </div>
2096
- </Popup>
2097
1898
  </div>
2098
1899
  );
2099
1900
  }
@@ -151,8 +151,6 @@ function Form({
151
151
  }
152
152
  };
153
153
 
154
- const handleChangeCanvas = (id, value) => {};
155
-
156
154
  const handleChangeCheckbox = (e) => {
157
155
  const { checked, name, value } = e.target;
158
156
 
@@ -269,28 +267,12 @@ function Form({
269
267
  let contentValue = inputValue[field.trigger.key];
270
268
 
271
269
  if (field.trigger.url && field.trigger.payload) {
272
- let setContentPayload;
273
-
274
- if (field.trigger?.payload?.key?.id) {
275
- setContentPayload = {
276
- [field.trigger.payload.key.id]:
277
- inputValue[field.trigger.payload.value.id],
278
- [field.trigger.payload.key.content]:
279
- inputValue[field.trigger.payload.value.content],
280
- };
281
- } else {
282
- setContentPayload = {
283
- formData: formData,
284
- [field.trigger.payload.key.content]:
285
- inputValue[field.trigger.payload.value.content],
286
- };
287
- }
288
-
289
- const res = await CustomFetch(
290
- field.trigger.url,
291
- 'POST',
292
- setContentPayload
293
- );
270
+ const res = await CustomFetch(field.trigger.url, 'POST', {
271
+ [field.trigger.payload.key.id]:
272
+ inputValue[field.trigger.payload.value.id],
273
+ [field.trigger.payload.key.content]:
274
+ inputValue[field.trigger.payload.value.content],
275
+ });
294
276
 
295
277
  if (res.data.content) {
296
278
  contentValue = res.data.content;
@@ -619,6 +601,7 @@ function Form({
619
601
  validation += `${item.label} is a required field.<br/>`;
620
602
  _inputClass[item.id] = styles.inputError;
621
603
  } else {
604
+ console.info('b', formData, item.id);
622
605
  validation += `${item.label} is a required field.<br/>`;
623
606
  _inputClass[item.id] = styles.inputError;
624
607
  }
@@ -688,14 +671,7 @@ function Form({
688
671
 
689
672
  if (!_.isEmpty(formData)) {
690
673
  Object.keys(formData).forEach((item) => {
691
- const value = formData[item];
692
-
693
- // Check if it's a single File or an array of File objects
694
- if (
695
- value instanceof File ||
696
- (Array.isArray(value) &&
697
- value.every((file) => file instanceof File))
698
- ) {
674
+ if (formData[item] instanceof File) {
699
675
  fileUpload = true;
700
676
  fileKey = item;
701
677
 
@@ -710,55 +686,87 @@ function Form({
710
686
  });
711
687
  }
712
688
 
713
- const files = Array.isArray(formData[fileKey])
714
- ? formData[fileKey]
715
- : [formData[fileKey]];
689
+ if (fileUpload && formData[fileKey]) {
690
+ if (Array.isArray(formData[fileKey])) {
691
+ const uploadPromises = formData[fileKey].map(
692
+ async (file) => {
693
+ const toastId = toast.info(
694
+ `Uploading ${file.name}`,
695
+ {
696
+ progress: 0,
697
+ autoClose: false,
698
+ }
699
+ );
700
+
701
+ const fileRes = await Vapor.store(file, {
702
+ progress: (progress) => {
703
+ setUploadProgress(progress * 100);
704
+ toast.update(toastId, {
705
+ progress,
706
+ });
707
+ },
708
+ });
709
+
710
+ toast.update(toastId, {
711
+ render: `${file.name} uploaded successfully!`,
712
+ type: 'success',
713
+ autoClose: 3000,
714
+ });
716
715
 
717
- if (fileUpload && files.length > 0) {
718
- const uploadPromises = files.map(async (file) => {
716
+ return {
717
+ uuid: fileRes.uuid,
718
+ key: fileRes.key,
719
+ bucket: fileRes.bucket,
720
+ filename: file.name,
721
+ filesize: file.size,
722
+ extension: fileRes.extension,
723
+ file_relationship: fileKey,
724
+ fileable_field:
725
+ fileFieldSetting.fileable_field,
726
+ };
727
+ }
728
+ );
729
+
730
+ formData['uploadedFiles'] = await Promise.all(
731
+ uploadPromises
732
+ );
733
+ } else {
719
734
  const toastId = toast.info(
720
- `Uploading ${file.name}`,
735
+ `Uploading ${formData[fileKey].name}`,
721
736
  {
722
737
  progress: 0,
723
738
  autoClose: false,
724
739
  }
725
740
  );
726
741
 
727
- const fileRes = await Vapor.store(file, {
728
- progress: (progress) => {
729
- setUploadProgress(progress * 100);
730
- toast.update(toastId, { progress });
731
- },
732
- });
742
+ const fileRes = await Vapor.store(
743
+ formData[fileKey],
744
+ {
745
+ progress: (progress) => {
746
+ setUploadProgress(progress * 100);
747
+ toast.update(toastId, {
748
+ progress,
749
+ });
750
+ },
751
+ }
752
+ );
733
753
 
734
754
  toast.update(toastId, {
735
- render: `${file.name} uploaded successfully!`,
755
+ render: `${formData[fileKey].name} uploaded successfully!`,
736
756
  type: 'success',
737
757
  autoClose: 3000,
738
758
  });
739
759
 
740
- return {
741
- uuid: fileRes.uuid,
742
- key: fileRes.key,
743
- bucket: fileRes.bucket,
744
- filename: file.name,
745
- filesize: file.size,
746
- extension: fileRes.extension,
747
- file_relationship: fileKey,
748
- fileable_field:
749
- fileFieldSetting.fileable_field ||
750
- fileFieldSetting.id,
751
- };
752
- });
753
-
754
- // Wait for all uploads to complete
755
- const uploadedFiles = await Promise.all(uploadPromises);
756
-
757
- // If there are multiple files, store them as an array, otherwise as a single object
758
- formData['uploadedFiles'] =
759
- uploadedFiles.length > 1
760
- ? uploadedFiles
761
- : uploadedFiles[0];
760
+ formData['uuid'] = fileRes.uuid;
761
+ formData['key'] = fileRes.key;
762
+ formData['bucket'] = fileRes.bucket;
763
+ formData['filename'] = formData[fileKey].name;
764
+ formData['filesize'] = formData[fileKey].size;
765
+ formData['extension'] = fileRes.extension;
766
+ formData['file_relationship'] = fileKey;
767
+ formData['fileable_field'] =
768
+ fileFieldSetting.fileable_field;
769
+ }
762
770
  }
763
771
 
764
772
  // Transform keys with dots into nested objects
@@ -857,9 +865,7 @@ function Form({
857
865
  _formData[id] =
858
866
  newValue instanceof Date
859
867
  ? newValue
860
- : newValue !== undefined
861
- ? moment(newValue).toDate()
862
- : '';
868
+ : moment(newValue).toDate();
863
869
  } else {
864
870
  _formData[id] = newValue;
865
871
  }
@@ -1361,18 +1367,15 @@ function Form({
1361
1367
  );
1362
1368
  } else {
1363
1369
  return (
1364
- <div className={`${styles.modalwrap} ${styles['top--modal']}`}>
1365
- <div className={styles.modal}>
1366
- <div className={styles.modal__header}>
1370
+ <div className="modalwrap top--modal">
1371
+ <div className="modal">
1372
+ <div className="modal__header">
1367
1373
  <h1>{formSettings[formType]?.title}</h1>
1368
- <button
1369
- className={styles.modal__close}
1370
- onClick={closeModal}
1371
- >
1374
+ <button className="modal__close" onClick={closeModal}>
1372
1375
  <CircleX strokeWidth={1} size={24} />
1373
1376
  </button>
1374
1377
  </div>
1375
- <div className={styles.modal__content}>
1378
+ <div className="modal__content">
1376
1379
  <div className={styles.formcontainer}>
1377
1380
  <form className={styles.modalForm}>
1378
1381
  {formSettings?.fields?.map((item, index) => {
@@ -1423,7 +1426,6 @@ function Form({
1423
1426
  }
1424
1427
  mapbox={mapbox}
1425
1428
  onChange={handleChange}
1426
- onChangeCanvas={handleChangeCanvas}
1427
1429
  onChangeCheckbox={
1428
1430
  handleChangeCheckbox
1429
1431
  }