@visns-studio/visns-components 4.10.48 → 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 +25 -419
  5. package/src/components/crm/Field.jsx +16 -215
  6. package/src/components/crm/Form.jsx +86 -87
  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
 
@@ -218,14 +216,11 @@ function Form({
218
216
  updateFormData({ [id]: inputValue });
219
217
 
220
218
  // Update any matching form data properties
221
- Object.keys(inputValue)
222
- .filter(
223
- (key) =>
224
- formData.hasOwnProperty(key) &&
225
- key !== 'id' &&
226
- key !== 'label'
227
- )
228
- .forEach((key) => updateFormData({ [key]: inputValue[key] }));
219
+ Object.keys(inputValue).forEach((key) => {
220
+ if (formData.hasOwnProperty(key)) {
221
+ updateFormData({ [key]: inputValue[key] });
222
+ }
223
+ });
229
224
 
230
225
  formSettings.fields.forEach((field) => {
231
226
  if (field.id === id && field.trigger) {
@@ -272,28 +267,12 @@ function Form({
272
267
  let contentValue = inputValue[field.trigger.key];
273
268
 
274
269
  if (field.trigger.url && field.trigger.payload) {
275
- let setContentPayload;
276
-
277
- if (field.trigger?.payload?.key?.id) {
278
- setContentPayload = {
279
- [field.trigger.payload.key.id]:
280
- inputValue[field.trigger.payload.value.id],
281
- [field.trigger.payload.key.content]:
282
- inputValue[field.trigger.payload.value.content],
283
- };
284
- } else {
285
- setContentPayload = {
286
- formData: formData,
287
- [field.trigger.payload.key.content]:
288
- inputValue[field.trigger.payload.value.content],
289
- };
290
- }
291
-
292
- const res = await CustomFetch(
293
- field.trigger.url,
294
- 'POST',
295
- setContentPayload
296
- );
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
+ });
297
276
 
298
277
  if (res.data.content) {
299
278
  contentValue = res.data.content;
@@ -622,6 +601,7 @@ function Form({
622
601
  validation += `${item.label} is a required field.<br/>`;
623
602
  _inputClass[item.id] = styles.inputError;
624
603
  } else {
604
+ console.info('b', formData, item.id);
625
605
  validation += `${item.label} is a required field.<br/>`;
626
606
  _inputClass[item.id] = styles.inputError;
627
607
  }
@@ -691,14 +671,7 @@ function Form({
691
671
 
692
672
  if (!_.isEmpty(formData)) {
693
673
  Object.keys(formData).forEach((item) => {
694
- const value = formData[item];
695
-
696
- // Check if it's a single File or an array of File objects
697
- if (
698
- value instanceof File ||
699
- (Array.isArray(value) &&
700
- value.every((file) => file instanceof File))
701
- ) {
674
+ if (formData[item] instanceof File) {
702
675
  fileUpload = true;
703
676
  fileKey = item;
704
677
 
@@ -713,55 +686,87 @@ function Form({
713
686
  });
714
687
  }
715
688
 
716
- const files = Array.isArray(formData[fileKey])
717
- ? formData[fileKey]
718
- : [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
+ });
719
709
 
720
- if (fileUpload && files.length > 0) {
721
- const uploadPromises = files.map(async (file) => {
710
+ toast.update(toastId, {
711
+ render: `${file.name} uploaded successfully!`,
712
+ type: 'success',
713
+ autoClose: 3000,
714
+ });
715
+
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 {
722
734
  const toastId = toast.info(
723
- `Uploading ${file.name}`,
735
+ `Uploading ${formData[fileKey].name}`,
724
736
  {
725
737
  progress: 0,
726
738
  autoClose: false,
727
739
  }
728
740
  );
729
741
 
730
- const fileRes = await Vapor.store(file, {
731
- progress: (progress) => {
732
- setUploadProgress(progress * 100);
733
- toast.update(toastId, { progress });
734
- },
735
- });
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
+ );
736
753
 
737
754
  toast.update(toastId, {
738
- render: `${file.name} uploaded successfully!`,
755
+ render: `${formData[fileKey].name} uploaded successfully!`,
739
756
  type: 'success',
740
757
  autoClose: 3000,
741
758
  });
742
759
 
743
- return {
744
- uuid: fileRes.uuid,
745
- key: fileRes.key,
746
- bucket: fileRes.bucket,
747
- filename: file.name,
748
- filesize: file.size,
749
- extension: fileRes.extension,
750
- file_relationship: fileKey,
751
- fileable_field:
752
- fileFieldSetting.fileable_field ||
753
- fileFieldSetting.id,
754
- };
755
- });
756
-
757
- // Wait for all uploads to complete
758
- const uploadedFiles = await Promise.all(uploadPromises);
759
-
760
- // If there are multiple files, store them as an array, otherwise as a single object
761
- formData['uploadedFiles'] =
762
- uploadedFiles.length > 1
763
- ? uploadedFiles
764
- : 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
+ }
765
770
  }
766
771
 
767
772
  // Transform keys with dots into nested objects
@@ -860,9 +865,7 @@ function Form({
860
865
  _formData[id] =
861
866
  newValue instanceof Date
862
867
  ? newValue
863
- : newValue !== undefined
864
- ? moment(newValue).toDate()
865
- : '';
868
+ : moment(newValue).toDate();
866
869
  } else {
867
870
  _formData[id] = newValue;
868
871
  }
@@ -1364,18 +1367,15 @@ function Form({
1364
1367
  );
1365
1368
  } else {
1366
1369
  return (
1367
- <div className={`${styles.modalwrap} ${styles['top--modal']}`}>
1368
- <div className={styles.modal}>
1369
- <div className={styles.modal__header}>
1370
+ <div className="modalwrap top--modal">
1371
+ <div className="modal">
1372
+ <div className="modal__header">
1370
1373
  <h1>{formSettings[formType]?.title}</h1>
1371
- <button
1372
- className={styles.modal__close}
1373
- onClick={closeModal}
1374
- >
1374
+ <button className="modal__close" onClick={closeModal}>
1375
1375
  <CircleX strokeWidth={1} size={24} />
1376
1376
  </button>
1377
1377
  </div>
1378
- <div className={styles.modal__content}>
1378
+ <div className="modal__content">
1379
1379
  <div className={styles.formcontainer}>
1380
1380
  <form className={styles.modalForm}>
1381
1381
  {formSettings?.fields?.map((item, index) => {
@@ -1426,7 +1426,6 @@ function Form({
1426
1426
  }
1427
1427
  mapbox={mapbox}
1428
1428
  onChange={handleChange}
1429
- onChangeCanvas={handleChangeCanvas}
1430
1429
  onChangeCheckbox={
1431
1430
  handleChangeCheckbox
1432
1431
  }