@visns-studio/visns-components 5.1.17 → 5.1.19
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
CHANGED
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
78
78
|
},
|
|
79
79
|
"name": "@visns-studio/visns-components",
|
|
80
|
-
"version": "5.1.
|
|
80
|
+
"version": "5.1.19",
|
|
81
81
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
82
82
|
"main": "src/index.js",
|
|
83
83
|
"files": [
|
|
@@ -11,7 +11,19 @@ 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 {
|
|
14
|
+
import {
|
|
15
|
+
Edit,
|
|
16
|
+
ChevronRight,
|
|
17
|
+
Circle,
|
|
18
|
+
CircleFill,
|
|
19
|
+
CirclePlus,
|
|
20
|
+
CloudDownload,
|
|
21
|
+
Copy,
|
|
22
|
+
Minus,
|
|
23
|
+
ArrowBack,
|
|
24
|
+
TrashCan,
|
|
25
|
+
CircleX,
|
|
26
|
+
} from 'akar-icons';
|
|
15
27
|
import { toast } from 'react-toastify';
|
|
16
28
|
import styles from './styles/Field.module.scss';
|
|
17
29
|
|
|
@@ -19,6 +31,9 @@ import CustomFetch from './Fetch';
|
|
|
19
31
|
import Form from './Form';
|
|
20
32
|
import MultiSelect from './MultiSelect';
|
|
21
33
|
import Select from './Select';
|
|
34
|
+
import SketchConfig from './sketch/json/config.json';
|
|
35
|
+
import SketchField from './sketch/SketchField';
|
|
36
|
+
import Tools from './sketch/tools';
|
|
22
37
|
import VisnsAsyncSelect from './AsyncSelect';
|
|
23
38
|
import VisnsAutocomplete from './Autocomplete';
|
|
24
39
|
import VisnsDropZone from '../cms/DropZone';
|
|
@@ -63,6 +78,30 @@ function Field({
|
|
|
63
78
|
const [formItemClass, setFormItemClass] = useState('');
|
|
64
79
|
const [options, setOptions] = useState([]);
|
|
65
80
|
|
|
81
|
+
/** Canvas States */
|
|
82
|
+
const [canvasPopupOpen, setCanvasPopupOpen] = useState(false);
|
|
83
|
+
const [canvasUrl, setCanvasUrl] = useState('');
|
|
84
|
+
const [sketchTool, setSketchTool] = useState(Tools.Pencil);
|
|
85
|
+
const [toolClass, setToolClass] = useState('pencil');
|
|
86
|
+
const [lineWidth, setLineWidth] = useState(2);
|
|
87
|
+
const [lineColor, setLineColor] = useState('rgba(0, 0, 0, 1)');
|
|
88
|
+
const sketchRef = useRef(null);
|
|
89
|
+
|
|
90
|
+
/** Canvas Functions */
|
|
91
|
+
const handleCanvasSave = async () => {
|
|
92
|
+
// Save the current canvas state
|
|
93
|
+
const canvasState = sketchRef.current.saveCanvasState();
|
|
94
|
+
|
|
95
|
+
// Export the canvas image data as a Base64 string
|
|
96
|
+
const imageData = await sketchRef.current.exportCanvasImage(); // Use await here
|
|
97
|
+
|
|
98
|
+
// Call the onChangeCanvas function with both values
|
|
99
|
+
onChangeCanvas(settings, canvasState, imageData);
|
|
100
|
+
|
|
101
|
+
// Close the canvas popup
|
|
102
|
+
setCanvasPopupOpen(false);
|
|
103
|
+
};
|
|
104
|
+
|
|
66
105
|
const cover = {
|
|
67
106
|
position: 'fixed',
|
|
68
107
|
top: '0px',
|
|
@@ -203,6 +242,7 @@ function Field({
|
|
|
203
242
|
}
|
|
204
243
|
});
|
|
205
244
|
} else {
|
|
245
|
+
console.info(s);
|
|
206
246
|
filter = {
|
|
207
247
|
where: [
|
|
208
248
|
{
|
|
@@ -212,20 +252,22 @@ function Field({
|
|
|
212
252
|
],
|
|
213
253
|
};
|
|
214
254
|
|
|
215
|
-
if (s
|
|
216
|
-
filter.fields = s.fields;
|
|
255
|
+
if (s?.child?.fields) {
|
|
256
|
+
filter.fields = s.child.fields;
|
|
217
257
|
}
|
|
218
258
|
|
|
219
|
-
if (s
|
|
220
|
-
filter.orderBy = s.orderBy;
|
|
259
|
+
if (s?.child?.orderBy) {
|
|
260
|
+
filter.orderBy = s.child.orderBy;
|
|
221
261
|
}
|
|
222
262
|
|
|
223
|
-
if (s
|
|
224
|
-
forEach(s.where, (w) => {
|
|
263
|
+
if (s?.child?.where) {
|
|
264
|
+
forEach(s.child.where, (w) => {
|
|
225
265
|
filter.where.push(w);
|
|
226
266
|
});
|
|
227
267
|
}
|
|
228
268
|
|
|
269
|
+
console.info(filter, s.fields);
|
|
270
|
+
|
|
229
271
|
CustomFetch(s.child.url, 'POST', filter, function (result) {
|
|
230
272
|
childDropdownCallback({
|
|
231
273
|
id: s.child.id,
|
|
@@ -280,8 +322,6 @@ function Field({
|
|
|
280
322
|
'checkbox',
|
|
281
323
|
'colour',
|
|
282
324
|
'copytoclipboard',
|
|
283
|
-
'date',
|
|
284
|
-
'datetime',
|
|
285
325
|
'dropdown',
|
|
286
326
|
'dropdown-ajax',
|
|
287
327
|
'dynamicdata',
|
|
@@ -307,6 +347,14 @@ function Field({
|
|
|
307
347
|
{settings.label} {settings.required ? '*' : ''}
|
|
308
348
|
</span>
|
|
309
349
|
);
|
|
350
|
+
} else if (['date', 'datetime', 'time'].includes(settings.type)) {
|
|
351
|
+
return (
|
|
352
|
+
<span
|
|
353
|
+
className={`${styles.fi__span} ${styles.prefocusdate}`}
|
|
354
|
+
>
|
|
355
|
+
{settings.label} {settings.required ? '*' : ''}
|
|
356
|
+
</span>
|
|
357
|
+
);
|
|
310
358
|
} else if (['plaintext'].includes(settings.type)) {
|
|
311
359
|
if (settings.description) {
|
|
312
360
|
return (
|
|
@@ -687,7 +735,7 @@ function Field({
|
|
|
687
735
|
settings.multiple === true
|
|
688
736
|
) {
|
|
689
737
|
return (
|
|
690
|
-
|
|
738
|
+
<div className={styles['file-upload-container']}>
|
|
691
739
|
<input
|
|
692
740
|
type="file"
|
|
693
741
|
multiple
|
|
@@ -695,51 +743,53 @@ function Field({
|
|
|
695
743
|
className={inputClass[settings.id]}
|
|
696
744
|
onChange={onChange}
|
|
697
745
|
/>
|
|
746
|
+
|
|
698
747
|
{inputValue && inputValue.length > 0 && (
|
|
699
|
-
<ul className=
|
|
748
|
+
<ul className={styles['file-list']}>
|
|
700
749
|
{inputValue.map((file, index) => (
|
|
701
750
|
<li
|
|
702
751
|
key={`file-${settings.id}-${index}`}
|
|
703
752
|
>
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
753
|
+
<span
|
|
754
|
+
className={styles['file-name']}
|
|
755
|
+
>
|
|
756
|
+
{file.name ||
|
|
757
|
+
file.file_name ||
|
|
758
|
+
''}
|
|
759
|
+
</span>
|
|
709
760
|
<button
|
|
710
|
-
className=
|
|
761
|
+
className={
|
|
762
|
+
styles['trash-button']
|
|
763
|
+
}
|
|
711
764
|
onClick={(e) => {
|
|
712
765
|
e.preventDefault();
|
|
713
766
|
e.stopPropagation();
|
|
714
|
-
|
|
715
|
-
const updatedFiles =
|
|
716
|
-
inputValue.filter(
|
|
717
|
-
(_, fileIndex) =>
|
|
718
|
-
fileIndex !==
|
|
719
|
-
index
|
|
720
|
-
);
|
|
721
|
-
|
|
722
767
|
setFormData(
|
|
723
768
|
(prevFormData) => ({
|
|
724
769
|
...prevFormData,
|
|
725
770
|
[settings.id]:
|
|
726
|
-
|
|
771
|
+
inputValue.filter(
|
|
772
|
+
(
|
|
773
|
+
_,
|
|
774
|
+
fileIndex
|
|
775
|
+
) =>
|
|
776
|
+
fileIndex !==
|
|
777
|
+
index
|
|
778
|
+
),
|
|
727
779
|
})
|
|
728
780
|
);
|
|
729
781
|
}}
|
|
730
782
|
>
|
|
731
783
|
<TrashCan
|
|
732
|
-
data-tooltip-id="system-tooltip"
|
|
733
|
-
data-tooltip-content="Remove File"
|
|
734
784
|
strokeWidth={2}
|
|
735
|
-
size={
|
|
785
|
+
size={20}
|
|
736
786
|
/>
|
|
737
787
|
</button>
|
|
738
788
|
</li>
|
|
739
789
|
))}
|
|
740
790
|
</ul>
|
|
741
791
|
)}
|
|
742
|
-
|
|
792
|
+
</div>
|
|
743
793
|
);
|
|
744
794
|
} else {
|
|
745
795
|
return (
|
|
@@ -1922,6 +1972,126 @@ function Field({
|
|
|
1922
1972
|
updateForm={setChildForm}
|
|
1923
1973
|
/>
|
|
1924
1974
|
</Popup>
|
|
1975
|
+
<Popup
|
|
1976
|
+
open={canvasPopupOpen}
|
|
1977
|
+
onClose={() => setCanvasPopupOpen(false)}
|
|
1978
|
+
closeOnDocumentClick={false}
|
|
1979
|
+
>
|
|
1980
|
+
<div className={`${styles.modalwrap} ${styles['top--modal']}`}>
|
|
1981
|
+
<div className={styles.modal}>
|
|
1982
|
+
<div className={styles.modal__header}>
|
|
1983
|
+
<h1>Edit Canvas</h1>
|
|
1984
|
+
<button
|
|
1985
|
+
className={styles.modal__close}
|
|
1986
|
+
onClick={() => setCanvasPopupOpen(false)}
|
|
1987
|
+
>
|
|
1988
|
+
<CircleX size={24} color="#FFF" />
|
|
1989
|
+
</button>
|
|
1990
|
+
</div>
|
|
1991
|
+
<div
|
|
1992
|
+
className={`${styles.modal__content} ${styles.drawDiv}`}
|
|
1993
|
+
>
|
|
1994
|
+
{/* Sketch Tools */}
|
|
1995
|
+
<ul className={styles.drawactions}>
|
|
1996
|
+
<li>
|
|
1997
|
+
<button
|
|
1998
|
+
onClick={() => {
|
|
1999
|
+
handleCanvasSave();
|
|
2000
|
+
}}
|
|
2001
|
+
className={styles['drawactions--save']}
|
|
2002
|
+
>
|
|
2003
|
+
<CloudDownload size={16} /> Save
|
|
2004
|
+
</button>
|
|
2005
|
+
</li>
|
|
2006
|
+
<li>
|
|
2007
|
+
<button
|
|
2008
|
+
onClick={() => {
|
|
2009
|
+
setSketchTool(Tools.Pencil);
|
|
2010
|
+
setToolClass('pencil');
|
|
2011
|
+
}}
|
|
2012
|
+
className={`${
|
|
2013
|
+
styles['drawaction-pencil']
|
|
2014
|
+
} ${
|
|
2015
|
+
toolClass === 'pencil'
|
|
2016
|
+
? styles['drawactive']
|
|
2017
|
+
: ''
|
|
2018
|
+
}`}
|
|
2019
|
+
>
|
|
2020
|
+
<Edit size={16} /> Pencil
|
|
2021
|
+
</button>
|
|
2022
|
+
</li>
|
|
2023
|
+
<li>
|
|
2024
|
+
<button
|
|
2025
|
+
onClick={() => {
|
|
2026
|
+
setSketchTool(Tools.Line);
|
|
2027
|
+
|
|
2028
|
+
setToolClass('line');
|
|
2029
|
+
}}
|
|
2030
|
+
className={`${
|
|
2031
|
+
styles['drawaction-line']
|
|
2032
|
+
} ${
|
|
2033
|
+
toolClass === 'line'
|
|
2034
|
+
? styles['drawactive']
|
|
2035
|
+
: ''
|
|
2036
|
+
}`}
|
|
2037
|
+
>
|
|
2038
|
+
<Minus size={16} /> Line
|
|
2039
|
+
</button>
|
|
2040
|
+
</li>
|
|
2041
|
+
<li>
|
|
2042
|
+
<button
|
|
2043
|
+
onClick={() => {
|
|
2044
|
+
setSketchTool(Tools.Circle);
|
|
2045
|
+
|
|
2046
|
+
setToolClass('circle');
|
|
2047
|
+
}}
|
|
2048
|
+
className={`${
|
|
2049
|
+
styles['drawaction-circle']
|
|
2050
|
+
} ${
|
|
2051
|
+
toolClass === 'circle'
|
|
2052
|
+
? styles['drawactive']
|
|
2053
|
+
: ''
|
|
2054
|
+
}`}
|
|
2055
|
+
>
|
|
2056
|
+
<Circle size={16} /> Circle
|
|
2057
|
+
</button>
|
|
2058
|
+
</li>
|
|
2059
|
+
<li>
|
|
2060
|
+
<button
|
|
2061
|
+
onClick={() => sketchRef.current.undo()}
|
|
2062
|
+
className={styles['drawaction-undo']}
|
|
2063
|
+
>
|
|
2064
|
+
<ArrowBack size={16} /> Undo
|
|
2065
|
+
</button>
|
|
2066
|
+
</li>
|
|
2067
|
+
<li>
|
|
2068
|
+
<button
|
|
2069
|
+
onClick={() =>
|
|
2070
|
+
sketchRef.current.clearCanvas()
|
|
2071
|
+
}
|
|
2072
|
+
className={styles['drawactions--clear']}
|
|
2073
|
+
>
|
|
2074
|
+
<TrashCan size={16} /> Clear
|
|
2075
|
+
</button>
|
|
2076
|
+
</li>
|
|
2077
|
+
</ul>
|
|
2078
|
+
{(canvasUrl || inputValue?.state) && (
|
|
2079
|
+
<SketchField
|
|
2080
|
+
ref={sketchRef}
|
|
2081
|
+
width={740}
|
|
2082
|
+
height={800}
|
|
2083
|
+
tool={sketchTool}
|
|
2084
|
+
lineColor={lineColor}
|
|
2085
|
+
lineWidth={lineWidth}
|
|
2086
|
+
className={styles.drawcontainer}
|
|
2087
|
+
backgroundImage={canvasUrl}
|
|
2088
|
+
defaultValue={inputValue?.state || null}
|
|
2089
|
+
/>
|
|
2090
|
+
)}
|
|
2091
|
+
</div>
|
|
2092
|
+
</div>
|
|
2093
|
+
</div>
|
|
2094
|
+
</Popup>
|
|
1925
2095
|
</div>
|
|
1926
2096
|
);
|
|
1927
2097
|
}
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import '../../styles/global.css';
|
|
2
2
|
|
|
3
3
|
import React, { useState, useEffect } from 'react';
|
|
4
|
-
import { Link } from 'react-router-dom';
|
|
4
|
+
import { Link, useNavigate } from 'react-router-dom';
|
|
5
5
|
import { ResponsiveBar } from '@nivo/bar';
|
|
6
6
|
import { ResponsiveLine } from '@nivo/line';
|
|
7
7
|
import { ResponsivePie } from '@nivo/pie';
|
|
8
8
|
import Popup from 'reactjs-popup';
|
|
9
9
|
import dayjs from 'dayjs';
|
|
10
|
+
import parse from 'html-react-parser';
|
|
10
11
|
import { CircleX } from 'akar-icons';
|
|
11
12
|
|
|
12
13
|
import CustomFetch from '../../crm/Fetch';
|
|
@@ -17,6 +18,7 @@ import styles from './styles/GenericDashboard.module.scss';
|
|
|
17
18
|
const toSnakeCase = (str) => str.replace(/[\s-]+/g, '_').toLowerCase();
|
|
18
19
|
|
|
19
20
|
function GenericDashboard({ setting }) {
|
|
21
|
+
const navigate = useNavigate();
|
|
20
22
|
const [data, setData] = useState({});
|
|
21
23
|
const [dropdowns, setDropdowns] = useState({});
|
|
22
24
|
const [filters, setFilters] = useState({}); // Track filter values for each widget
|
|
@@ -25,44 +27,69 @@ function GenericDashboard({ setting }) {
|
|
|
25
27
|
|
|
26
28
|
const fetchData = async (appliedFilters = {}) => {
|
|
27
29
|
try {
|
|
28
|
-
|
|
30
|
+
// Flatten the nested widgets array into a single array
|
|
31
|
+
const flattenedWidgets = setting.widgets.reduce(
|
|
32
|
+
(acc, row) => [...acc, ...row],
|
|
33
|
+
[]
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
// Map through the flattened array of widgets
|
|
37
|
+
const promises = flattenedWidgets.map(async (widget) => {
|
|
29
38
|
const widgetFilters = appliedFilters[widget.id] || {};
|
|
30
39
|
|
|
31
40
|
// Fetch dropdown data if required
|
|
32
41
|
if (widget.filters) {
|
|
33
42
|
for (const filter of widget.filters) {
|
|
34
43
|
if (filter.url) {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
44
|
+
try {
|
|
45
|
+
const initialFilterData = await CustomFetch(
|
|
46
|
+
filter.url,
|
|
47
|
+
'POST',
|
|
48
|
+
filter.params || {}
|
|
49
|
+
);
|
|
50
|
+
setDropdowns((prevFilters) => ({
|
|
51
|
+
...prevFilters,
|
|
52
|
+
[widget.id]: {
|
|
53
|
+
...prevFilters[widget.id],
|
|
54
|
+
[filter.id]:
|
|
55
|
+
initialFilterData?.data?.data || [],
|
|
56
|
+
},
|
|
57
|
+
}));
|
|
58
|
+
} catch (error) {
|
|
59
|
+
console.error(
|
|
60
|
+
`Error fetching dropdown for widget ${widget.id}:`,
|
|
61
|
+
error
|
|
62
|
+
);
|
|
63
|
+
}
|
|
48
64
|
}
|
|
49
65
|
}
|
|
50
66
|
}
|
|
51
67
|
|
|
52
|
-
//
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
// Check if widget.api exists before making a request
|
|
69
|
+
if (widget.api?.url && widget.api?.method) {
|
|
70
|
+
return CustomFetch(
|
|
71
|
+
widget.api.url,
|
|
72
|
+
widget.api.method,
|
|
73
|
+
widgetFilters
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return null; // Return null if no API request is needed
|
|
58
78
|
});
|
|
59
79
|
|
|
80
|
+
// Execute all API requests
|
|
60
81
|
const results = await Promise.all(promises);
|
|
61
82
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
// Build a data object keyed by widget id
|
|
84
|
+
const fetchedData = flattenedWidgets.reduce(
|
|
85
|
+
(acc, widget, index) => {
|
|
86
|
+
if (results[index]) {
|
|
87
|
+
acc[widget.id] = results[index]?.data;
|
|
88
|
+
}
|
|
89
|
+
return acc;
|
|
90
|
+
},
|
|
91
|
+
{}
|
|
92
|
+
);
|
|
66
93
|
|
|
67
94
|
setData(fetchedData);
|
|
68
95
|
} catch (error) {
|
|
@@ -136,20 +163,28 @@ function GenericDashboard({ setting }) {
|
|
|
136
163
|
const initializeFilters = () => {
|
|
137
164
|
const initialFilters = {};
|
|
138
165
|
|
|
139
|
-
setting.widgets.forEach((
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
166
|
+
setting.widgets.forEach((row) => {
|
|
167
|
+
row.forEach((widget) => {
|
|
168
|
+
if (widget.filters) {
|
|
169
|
+
initialFilters[widget.id] = widget.filters.reduce(
|
|
170
|
+
(acc, filter) => {
|
|
171
|
+
if (filter.type === 'date' && filter.default) {
|
|
172
|
+
acc[filter.id] = getDefaultDate(
|
|
173
|
+
filter.default
|
|
174
|
+
);
|
|
175
|
+
} else if (
|
|
176
|
+
filter.type === 'multi-dropdown-ajax'
|
|
177
|
+
) {
|
|
178
|
+
acc[filter.id] = [];
|
|
179
|
+
} else if (filter.type === 'years') {
|
|
180
|
+
acc[filter.id] = dayjs().year();
|
|
181
|
+
}
|
|
182
|
+
return acc;
|
|
183
|
+
},
|
|
184
|
+
{}
|
|
185
|
+
);
|
|
186
|
+
}
|
|
187
|
+
});
|
|
153
188
|
});
|
|
154
189
|
|
|
155
190
|
setFilters(initialFilters);
|
|
@@ -176,7 +211,13 @@ function GenericDashboard({ setting }) {
|
|
|
176
211
|
case 'date':
|
|
177
212
|
return (
|
|
178
213
|
<div
|
|
179
|
-
className={
|
|
214
|
+
className={
|
|
215
|
+
widget.highlight
|
|
216
|
+
? styles[
|
|
217
|
+
'filter-field-highlight'
|
|
218
|
+
]
|
|
219
|
+
: styles['filter-field']
|
|
220
|
+
}
|
|
180
221
|
key={filter.id}
|
|
181
222
|
>
|
|
182
223
|
<label htmlFor={filter.id}>
|
|
@@ -204,7 +245,13 @@ function GenericDashboard({ setting }) {
|
|
|
204
245
|
case 'multi-dropdown-ajax':
|
|
205
246
|
return (
|
|
206
247
|
<div
|
|
207
|
-
className={
|
|
248
|
+
className={
|
|
249
|
+
widget.highlight
|
|
250
|
+
? styles[
|
|
251
|
+
'filter-field-highlight'
|
|
252
|
+
]
|
|
253
|
+
: styles['filter-field']
|
|
254
|
+
}
|
|
208
255
|
key={filter.id}
|
|
209
256
|
>
|
|
210
257
|
<label htmlFor={filter.id}>
|
|
@@ -232,6 +279,60 @@ function GenericDashboard({ setting }) {
|
|
|
232
279
|
/>
|
|
233
280
|
</div>
|
|
234
281
|
);
|
|
282
|
+
case 'years':
|
|
283
|
+
return (
|
|
284
|
+
<div
|
|
285
|
+
className={
|
|
286
|
+
widget.highlight
|
|
287
|
+
? styles[
|
|
288
|
+
'filter-field-highlight'
|
|
289
|
+
]
|
|
290
|
+
: styles['filter-field']
|
|
291
|
+
}
|
|
292
|
+
key={filter.id}
|
|
293
|
+
>
|
|
294
|
+
<label htmlFor={filter.id}>
|
|
295
|
+
{filter.label}
|
|
296
|
+
</label>
|
|
297
|
+
<select
|
|
298
|
+
name="year"
|
|
299
|
+
style={{ height: '51px' }}
|
|
300
|
+
value={
|
|
301
|
+
filters[widget.id]?.[
|
|
302
|
+
filter.id
|
|
303
|
+
] || ''
|
|
304
|
+
}
|
|
305
|
+
onChange={(e) => {
|
|
306
|
+
handleFilterChange(
|
|
307
|
+
widget.id,
|
|
308
|
+
filter.id,
|
|
309
|
+
e.target.value
|
|
310
|
+
);
|
|
311
|
+
e.target.blur(); // Closes the picker
|
|
312
|
+
}}
|
|
313
|
+
>
|
|
314
|
+
{Array.from(
|
|
315
|
+
{
|
|
316
|
+
length:
|
|
317
|
+
new Date().getFullYear() -
|
|
318
|
+
2021 +
|
|
319
|
+
3,
|
|
320
|
+
},
|
|
321
|
+
(_, i) => {
|
|
322
|
+
const yearOption = 2022 + i;
|
|
323
|
+
return (
|
|
324
|
+
<option
|
|
325
|
+
key={yearOption}
|
|
326
|
+
value={yearOption}
|
|
327
|
+
>
|
|
328
|
+
{yearOption}
|
|
329
|
+
</option>
|
|
330
|
+
);
|
|
331
|
+
}
|
|
332
|
+
)}
|
|
333
|
+
</select>
|
|
334
|
+
</div>
|
|
335
|
+
);
|
|
235
336
|
default:
|
|
236
337
|
return null;
|
|
237
338
|
}
|
|
@@ -269,22 +370,32 @@ function GenericDashboard({ setting }) {
|
|
|
269
370
|
return (
|
|
270
371
|
<>
|
|
271
372
|
<div className={styles.widgetNo}>
|
|
272
|
-
<span>
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
: styles.btn
|
|
280
|
-
}
|
|
281
|
-
onClick={() => {
|
|
282
|
-
navigate('/leads/follow-ups');
|
|
283
|
-
}}
|
|
284
|
-
>
|
|
285
|
-
View All
|
|
286
|
-
</button>
|
|
373
|
+
<span>
|
|
374
|
+
{(Array.isArray(widgetData) &&
|
|
375
|
+
widgetData.length === 0) ||
|
|
376
|
+
widgetData == null
|
|
377
|
+
? parse(` `)
|
|
378
|
+
: widgetData}
|
|
379
|
+
</span>
|
|
287
380
|
</div>
|
|
381
|
+
{widget.button && (
|
|
382
|
+
<div className={styles.widgetLink}>
|
|
383
|
+
<button
|
|
384
|
+
className={
|
|
385
|
+
widget.highlight
|
|
386
|
+
? styles['btn-alt']
|
|
387
|
+
: styles.btn
|
|
388
|
+
}
|
|
389
|
+
onClick={() => {
|
|
390
|
+
if (widget.button.url) {
|
|
391
|
+
navigate(widget.button.url);
|
|
392
|
+
}
|
|
393
|
+
}}
|
|
394
|
+
>
|
|
395
|
+
{widget.button.title}
|
|
396
|
+
</button>
|
|
397
|
+
</div>
|
|
398
|
+
)}
|
|
288
399
|
</>
|
|
289
400
|
);
|
|
290
401
|
case 'pie':
|
|
@@ -299,15 +410,25 @@ function GenericDashboard({ setting }) {
|
|
|
299
410
|
const barData =
|
|
300
411
|
widgetData.bar?.data || widgetData.data || widgetData;
|
|
301
412
|
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
<
|
|
305
|
-
|
|
306
|
-
|
|
413
|
+
if (barData?.length > 0) {
|
|
414
|
+
return (
|
|
415
|
+
<div style={{ height: widget.height || '600px' }}>
|
|
416
|
+
<ResponsiveBar
|
|
417
|
+
data={barData}
|
|
418
|
+
{...widget.props}
|
|
419
|
+
/>
|
|
420
|
+
</div>
|
|
421
|
+
);
|
|
422
|
+
}
|
|
307
423
|
case 'line':
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
424
|
+
if (widgetData.length > 0) {
|
|
425
|
+
return (
|
|
426
|
+
<ResponsiveLine
|
|
427
|
+
data={widgetData}
|
|
428
|
+
{...widget.props}
|
|
429
|
+
/>
|
|
430
|
+
);
|
|
431
|
+
}
|
|
311
432
|
case 'list':
|
|
312
433
|
return (
|
|
313
434
|
<ul className={styles.dashList}>
|
|
@@ -588,48 +709,54 @@ function GenericDashboard({ setting }) {
|
|
|
588
709
|
};
|
|
589
710
|
|
|
590
711
|
const renderRows = () => {
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
<button
|
|
619
|
-
className={`${styles.btn} ${styles.dmore}`}
|
|
620
|
-
onClick={() => openModal(widget.id)}
|
|
621
|
-
>
|
|
622
|
-
View all{' '}
|
|
623
|
-
<span>
|
|
624
|
-
({data[widget.id]?.length || 0})
|
|
625
|
-
</span>
|
|
626
|
-
</button>
|
|
712
|
+
return setting.widgets.map((row, rowIndex) => {
|
|
713
|
+
// Use the number of widgets in the row to determine the grid class
|
|
714
|
+
const columns = row.length;
|
|
715
|
+
const gridClass =
|
|
716
|
+
columns === 1
|
|
717
|
+
? styles.grid__full
|
|
718
|
+
: columns === 2
|
|
719
|
+
? styles.grid__half
|
|
720
|
+
: columns === 3
|
|
721
|
+
? styles.grid__three
|
|
722
|
+
: styles.grid__three; // fallback if more than 3 columns
|
|
723
|
+
|
|
724
|
+
return (
|
|
725
|
+
<div className={styles.grid__row} key={`row-${rowIndex}`}>
|
|
726
|
+
{row.map((widget) => (
|
|
727
|
+
<div
|
|
728
|
+
key={widget.id}
|
|
729
|
+
className={`${gridClass} ${
|
|
730
|
+
widget.type !== 'list'
|
|
731
|
+
? widget.highlight
|
|
732
|
+
? styles['dashwidget--highlight']
|
|
733
|
+
: styles.dashwidget
|
|
734
|
+
: ''
|
|
735
|
+
}`}
|
|
736
|
+
>
|
|
737
|
+
<div className={styles.widgetTitle}>
|
|
738
|
+
<h2>{widget.title}</h2>
|
|
627
739
|
</div>
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
740
|
+
{renderFilters(widget)}
|
|
741
|
+
<div>{renderWidget(widget)}</div>
|
|
742
|
+
{widget.modal && (
|
|
743
|
+
<div className={styles.widgetAction}>
|
|
744
|
+
<button
|
|
745
|
+
className={`${styles.btn} ${styles.dmore}`}
|
|
746
|
+
onClick={() => openModal(widget.id)}
|
|
747
|
+
>
|
|
748
|
+
View all{' '}
|
|
749
|
+
<span>
|
|
750
|
+
({data[widget.id]?.length || 0})
|
|
751
|
+
</span>
|
|
752
|
+
</button>
|
|
753
|
+
</div>
|
|
754
|
+
)}
|
|
755
|
+
</div>
|
|
756
|
+
))}
|
|
757
|
+
</div>
|
|
758
|
+
);
|
|
759
|
+
});
|
|
633
760
|
};
|
|
634
761
|
|
|
635
762
|
return (
|
|
@@ -182,7 +182,7 @@
|
|
|
182
182
|
|
|
183
183
|
.widgetTitle {
|
|
184
184
|
padding-bottom: 10px;
|
|
185
|
-
border-bottom: 2px solid rgba(var(--tertiary-rgb), 0.5);
|
|
185
|
+
border-bottom: 2px solid rgba(var(--tertiary-color-rgb), 0.5);
|
|
186
186
|
}
|
|
187
187
|
}
|
|
188
188
|
|
|
@@ -261,6 +261,7 @@
|
|
|
261
261
|
gap: 1rem; // Gap between fields
|
|
262
262
|
align-items: flex-end; // Align fields vertically
|
|
263
263
|
}
|
|
264
|
+
|
|
264
265
|
.filter-field {
|
|
265
266
|
flex: 1; // Ensures equal spacing for each field
|
|
266
267
|
display: flex;
|
|
@@ -292,6 +293,37 @@
|
|
|
292
293
|
}
|
|
293
294
|
}
|
|
294
295
|
|
|
296
|
+
.filter-field-highlight {
|
|
297
|
+
flex: 1; // Ensures equal spacing for each field
|
|
298
|
+
display: flex;
|
|
299
|
+
flex-direction: column;
|
|
300
|
+
min-width: 200px; // Sets a minimum width for smaller screens
|
|
301
|
+
|
|
302
|
+
label {
|
|
303
|
+
font-size: 0.875rem;
|
|
304
|
+
font-weight: 600;
|
|
305
|
+
margin-bottom: 0.5rem;
|
|
306
|
+
color: var(--tertiary-color);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
input,
|
|
310
|
+
select {
|
|
311
|
+
border: 1px solid #dadce0;
|
|
312
|
+
border-radius: 5px;
|
|
313
|
+
padding: 0.5rem 0.75rem;
|
|
314
|
+
font-size: 0.875rem;
|
|
315
|
+
color: #333;
|
|
316
|
+
outline: none;
|
|
317
|
+
background-color: #f9f9f9;
|
|
318
|
+
width: 100%; // Ensures full width within the container
|
|
319
|
+
|
|
320
|
+
&:focus {
|
|
321
|
+
border-color: #007bff;
|
|
322
|
+
box-shadow: 0 0 0 2px rgba(0, 123, 255, 0.25); // Focus effect
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
295
327
|
.filter-actions {
|
|
296
328
|
display: flex;
|
|
297
329
|
gap: 0.75rem; // Gap between buttons
|
|
@@ -152,6 +152,17 @@ select:not(:placeholder-shown) + .fi__span {
|
|
|
152
152
|
color: rgba(var(--paragraph-rgb), 0.65) !important;
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
+
.prefocusdate {
|
|
156
|
+
transform: translateX(10px) scale(0.9) !important;
|
|
157
|
+
opacity: 1 !important;
|
|
158
|
+
padding: 0 0.25rem !important;
|
|
159
|
+
background: var(--tertiary-color) !important;
|
|
160
|
+
font-weight: 400 !important;
|
|
161
|
+
transition: all 0.5s cubic-bezier(0.5, 0.5, 0, 1) !important;
|
|
162
|
+
z-index: 1 !important;
|
|
163
|
+
color: rgba(var(--paragraph-rgb), 0.65) !important;
|
|
164
|
+
}
|
|
165
|
+
|
|
155
166
|
.qtrItem {
|
|
156
167
|
flex-basis: 25%;
|
|
157
168
|
}
|
|
@@ -417,3 +428,228 @@ input[type='file']:hover {
|
|
|
417
428
|
color: var(--secondary-color);
|
|
418
429
|
}
|
|
419
430
|
}
|
|
431
|
+
|
|
432
|
+
.modalwrap {
|
|
433
|
+
background: var(--tertiary-color);
|
|
434
|
+
border-radius: 3px;
|
|
435
|
+
overflow: visible;
|
|
436
|
+
border-top-left-radius: var(--br);
|
|
437
|
+
border-top-right-radius: var(--br);
|
|
438
|
+
|
|
439
|
+
.modal {
|
|
440
|
+
width: 100%;
|
|
441
|
+
position: relative;
|
|
442
|
+
|
|
443
|
+
&__header {
|
|
444
|
+
width: 100%;
|
|
445
|
+
background: var(--primary-color);
|
|
446
|
+
border: 2px solid rgba(var(--primary-color), 1.05);
|
|
447
|
+
border-top-left-radius: var(--br);
|
|
448
|
+
border-top-right-radius: var(--br);
|
|
449
|
+
box-sizing: border-box;
|
|
450
|
+
|
|
451
|
+
h1 {
|
|
452
|
+
color: var(--secondary-color);
|
|
453
|
+
margin: 0;
|
|
454
|
+
padding: 0.75em 0.75em;
|
|
455
|
+
font-size: 1.15em;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
.modal__close {
|
|
459
|
+
width: max-content;
|
|
460
|
+
position: absolute;
|
|
461
|
+
right: 5px;
|
|
462
|
+
top: 5px;
|
|
463
|
+
box-sizing: border-box;
|
|
464
|
+
background: rgba(var(--primary-color), 1.08);
|
|
465
|
+
cursor: pointer;
|
|
466
|
+
padding: 8px 8px;
|
|
467
|
+
margin: 0;
|
|
468
|
+
outline: none;
|
|
469
|
+
border: none;
|
|
470
|
+
border-radius: var(--br);
|
|
471
|
+
display: block;
|
|
472
|
+
line-height: 1;
|
|
473
|
+
|
|
474
|
+
svg {
|
|
475
|
+
color: var(--secondary-color);
|
|
476
|
+
display: block;
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
&__content {
|
|
482
|
+
width: 100%;
|
|
483
|
+
position: relative;
|
|
484
|
+
padding: 0.25rem;
|
|
485
|
+
box-sizing: border-box;
|
|
486
|
+
border-top-left-radius: var(--br);
|
|
487
|
+
border-top-right-radius: var(--br);
|
|
488
|
+
|
|
489
|
+
.btn {
|
|
490
|
+
background: var(--primary-color);
|
|
491
|
+
border: 1px solid rgba(var(--primary-color), 1.05);
|
|
492
|
+
|
|
493
|
+
&:hover {
|
|
494
|
+
background: var(--secondary-color);
|
|
495
|
+
color: var(--tertiary-color);
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.top--modal {
|
|
503
|
+
width: 100%;
|
|
504
|
+
max-width: 1000px !important;
|
|
505
|
+
height: auto;
|
|
506
|
+
// display: none;
|
|
507
|
+
padding: 0 !important;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
// Canvas
|
|
511
|
+
|
|
512
|
+
.drawDiv {
|
|
513
|
+
width: 100%;
|
|
514
|
+
display: flex;
|
|
515
|
+
justify-content: space-between;
|
|
516
|
+
|
|
517
|
+
img {
|
|
518
|
+
width: 100%;
|
|
519
|
+
max-width: 470px;
|
|
520
|
+
display: block;
|
|
521
|
+
margin: 0 auto;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
.drawactions {
|
|
526
|
+
width: 100%;
|
|
527
|
+
height: auto;
|
|
528
|
+
list-style: none;
|
|
529
|
+
padding: 0 1em 0 0;
|
|
530
|
+
margin: 0;
|
|
531
|
+
flex: 1;
|
|
532
|
+
box-sizing: border-box;
|
|
533
|
+
|
|
534
|
+
li {
|
|
535
|
+
width: 100%;
|
|
536
|
+
margin: 0 0 2px 0;
|
|
537
|
+
|
|
538
|
+
button {
|
|
539
|
+
width: 100%;
|
|
540
|
+
color: var(--paragraph-color);
|
|
541
|
+
border: 1px solid transparent;
|
|
542
|
+
padding: 0.5em 1em;
|
|
543
|
+
border-radius: 6px;
|
|
544
|
+
text-align: left;
|
|
545
|
+
font-size: 0.875em;
|
|
546
|
+
|
|
547
|
+
.octicon {
|
|
548
|
+
margin-right: 8px;
|
|
549
|
+
transform: scale(0.8) translateY(-1px);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
.drawactions--save {
|
|
554
|
+
background: #4ff1ba;
|
|
555
|
+
color: var(--paragraph-color);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.drawactions--clear {
|
|
559
|
+
background: #ff6a6a;
|
|
560
|
+
color: var(--paragraph-color);
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.drawactive {
|
|
564
|
+
border: 1px solid rgba(var(--paragraph-color-rgb), 1.4);
|
|
565
|
+
background: rgba(var(--paragraph-color-rgb), 0.2);
|
|
566
|
+
color: rgba(var(--paragraph-color-rgb), 1.05);
|
|
567
|
+
}
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
.drawcontainer {
|
|
572
|
+
display: block;
|
|
573
|
+
flex: 0 1 80%;
|
|
574
|
+
margin: 0;
|
|
575
|
+
border: 1px dashed rgba(var(--primary-color-rgb), 1.2);
|
|
576
|
+
padding: 5px;
|
|
577
|
+
overflow: hidden;
|
|
578
|
+
box-sizing: border-box;
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
.canvasWrap {
|
|
582
|
+
display: block;
|
|
583
|
+
margin: 0 auto;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.canvas-container {
|
|
587
|
+
display: block;
|
|
588
|
+
margin: 0 auto;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* File List Styling */
|
|
592
|
+
.file-upload-container {
|
|
593
|
+
display: flex;
|
|
594
|
+
flex-direction: column;
|
|
595
|
+
gap: 0.5em;
|
|
596
|
+
width: 100%;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
.file-upload-container input[type='file'] {
|
|
600
|
+
padding: 8px;
|
|
601
|
+
border: 1px solid var(--border-color);
|
|
602
|
+
border-radius: var(--br);
|
|
603
|
+
width: auto;
|
|
604
|
+
background: var(--tertiary-color);
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
.file-list {
|
|
608
|
+
width: 100%;
|
|
609
|
+
border: 1px solid var(--border-color);
|
|
610
|
+
border-radius: var(--br);
|
|
611
|
+
background: var(--tertiary-color);
|
|
612
|
+
max-height: 200px;
|
|
613
|
+
overflow-y: auto;
|
|
614
|
+
padding: 0.5em;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.file-list li {
|
|
618
|
+
display: flex;
|
|
619
|
+
justify-content: space-between;
|
|
620
|
+
align-items: center;
|
|
621
|
+
padding: 0.75em 1em;
|
|
622
|
+
border-bottom: 1px solid var(--border-color);
|
|
623
|
+
font-size: 0.9rem;
|
|
624
|
+
background: white;
|
|
625
|
+
border-radius: 5px;
|
|
626
|
+
transition: background 0.3s;
|
|
627
|
+
margin-bottom: 5px;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
.file-list li:last-child {
|
|
631
|
+
border-bottom: none;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
.file-list li:hover {
|
|
635
|
+
background: rgba(var(--paragraph-color-rgb), 0.1);
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
.trash-button {
|
|
639
|
+
background: transparent;
|
|
640
|
+
border: none;
|
|
641
|
+
cursor: pointer;
|
|
642
|
+
color: var(--primary-color);
|
|
643
|
+
display: flex;
|
|
644
|
+
align-items: center;
|
|
645
|
+
transition: color 0.3s;
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
.trash-button:hover {
|
|
649
|
+
color: var(--secondary-color);
|
|
650
|
+
}
|
|
651
|
+
|
|
652
|
+
.trash-button svg {
|
|
653
|
+
width: 1.2em;
|
|
654
|
+
height: 1.2em;
|
|
655
|
+
}
|