@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
@@ -1,94 +1,49 @@
1
- import React, { useEffect, useState, useRef } from 'react';
2
- import CustomFetch from './Fetch';
1
+ import React, { useEffect, useState } from 'react';
3
2
  import SelectList from './SelectList';
4
3
  import Select, { createFilter } from 'react-select';
5
- import CreatableSelect from 'react-select/creatable';
6
4
  import styles from './styles/AsyncSelect.module.scss';
7
5
 
8
6
  function MultiSelect({
9
7
  className,
10
- inputValue = [], // Default to an empty array
8
+ inputValue,
11
9
  multi,
12
10
  onChange,
13
- options = [], // Default to an empty array
11
+ options,
14
12
  placeholder,
15
13
  settings,
16
14
  style,
17
- isCreatable = false,
18
- creatableConfig = {},
19
15
  }) {
20
16
  const [selectOptions, setSelectOptions] = useState([]);
21
- const [selectValue, setSelectValue] = useState([]);
22
- const prevInputValueRef = useRef();
23
17
 
24
18
  useEffect(() => {
25
- const _options = Array.isArray(options)
26
- ? options.map((a) => ({
27
- value: a.id,
28
- label: a.label || a.name || a.description || 'Unknown',
29
- ...a,
30
- }))
31
- : [];
32
- setSelectOptions(_options);
33
- }, [options]);
19
+ let _options = [];
34
20
 
35
- useEffect(() => {
36
- // Only update if `inputValue` content has changed
37
- if (
38
- JSON.stringify(prevInputValueRef.current) !==
39
- JSON.stringify(inputValue)
40
- ) {
41
- const _values = Array.isArray(inputValue)
42
- ? inputValue.map((a) => ({
43
- value: a.id,
44
- label: a.label || a.name || a.description || 'Unknown',
45
- ...a,
46
- }))
47
- : inputValue && typeof inputValue === 'object'
48
- ? [
49
- {
50
- value: inputValue.id,
51
- label:
52
- inputValue.label ||
53
- inputValue.name ||
54
- inputValue.description ||
55
- 'Unknown',
56
- ...inputValue,
57
- },
58
- ]
59
- : [];
21
+ if (options.length > 0) {
22
+ options.forEach((a) => {
23
+ let _optionItem = {
24
+ value: a.id,
25
+ label: a.label,
26
+ };
60
27
 
61
- setSelectValue(_values);
62
- prevInputValueRef.current = inputValue;
63
- }
64
- }, [inputValue]);
28
+ Object.keys(a).forEach((b) => {
29
+ if (b !== 'id') {
30
+ _optionItem = {
31
+ ..._optionItem,
32
+ [b]: a[b],
33
+ };
34
+ }
35
+ });
65
36
 
66
- const handleCreate = async (inputValue) => {
67
- if (creatableConfig.url && creatableConfig.method) {
68
- const res = await CustomFetch(
69
- creatableConfig.url,
70
- creatableConfig.method,
71
- { label: inputValue }
72
- );
37
+ _options.push(_optionItem);
38
+ });
73
39
 
74
- if (res && res.data) {
75
- const newOption = {
76
- value: res.data.id,
77
- label: res.data.label || inputValue,
78
- ...res.data,
79
- };
80
-
81
- setSelectOptions((prevOptions) => [...prevOptions, newOption]);
82
- setSelectValue((prevValues) => [...prevValues, newOption]);
83
- }
40
+ setSelectOptions(_options);
84
41
  }
85
- };
86
-
87
- const SelectComponent = isCreatable ? CreatableSelect : Select;
42
+ }, [options]);
88
43
 
89
44
  return (
90
- <SelectComponent
91
- closeMenuOnSelect={!multi}
45
+ <Select
46
+ closeMenuOnSelect={multi ? false : true}
92
47
  isClearable
93
48
  isSearchable
94
49
  isMulti={multi}
@@ -114,8 +69,7 @@ function MultiSelect({
114
69
  : '52px',
115
70
  }),
116
71
  }}
117
- value={selectValue}
118
- onCreateOption={handleCreate}
72
+ value={inputValue}
119
73
  placeholder={placeholder ? placeholder : 'Select...'}
120
74
  />
121
75
  );
@@ -9,7 +9,6 @@ import {
9
9
  Calendar,
10
10
  ChatDots,
11
11
  Clipboard,
12
- CloudUpload,
13
12
  DoubleSword,
14
13
  Draft,
15
14
  Envelope,
@@ -236,8 +235,6 @@ function Navigation({
236
235
  };
237
236
 
238
237
  const renderSetting = (n) => {
239
- const isActive = location.pathname === n.url; // Check if current path matches setting URL
240
-
241
238
  const iconComponents = {
242
239
  bug: Bug,
243
240
  calendar: Calendar,
@@ -247,18 +244,15 @@ function Navigation({
247
244
  signOut: SignOut,
248
245
  };
249
246
 
250
- const IconComponent = iconComponents[n.icon];
251
- const settingClasses = isActive ? `${styles.active}` : ''; // Add active class if URLs match
252
-
253
247
  if (n.id === 'notifications') {
254
248
  return (
255
- <li className={settingClasses}>
249
+ <li>
256
250
  <Notification setSystemAuth={setSystemAuth} />
257
251
  </li>
258
252
  );
259
253
  } else if (n.id === 'switchAccount') {
260
254
  return (
261
- <li className={settingClasses}>
255
+ <li>
262
256
  <SwitchAccount
263
257
  setSystemAuth={setSystemAuth}
264
258
  setting={n}
@@ -267,8 +261,9 @@ function Navigation({
267
261
  </li>
268
262
  );
269
263
  } else if (n.id === 'logout') {
264
+ const IconComponent = iconComponents[n.icon];
270
265
  return (
271
- <li className={settingClasses}>
266
+ <li>
272
267
  <button
273
268
  title={n.label}
274
269
  data-tooltip-id="system-tooltip"
@@ -280,8 +275,10 @@ function Navigation({
280
275
  </li>
281
276
  );
282
277
  } else {
278
+ const IconComponent = iconComponents[n.icon];
279
+
283
280
  return (
284
- <li className={settingClasses}>
281
+ <li>
285
282
  <Link
286
283
  to={n.url}
287
284
  data-tooltip-id="system-tooltip"
@@ -305,7 +302,6 @@ function Navigation({
305
302
  calendar: Calendar,
306
303
  chatDots: ChatDots,
307
304
  clipboard: Clipboard,
308
- cloudUpload: CloudUpload,
309
305
  doubleSword: DoubleSword,
310
306
  draft: Draft,
311
307
  envelope: Envelope,
@@ -1,12 +1,11 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import { useReactToPrint } from 'react-to-print';
2
+ import ReactToPrint from 'react-to-print';
3
3
 
4
4
  import CustomFetch from './Fetch';
5
5
  import styles from './styles/QrCode.module.scss';
6
6
 
7
7
  const QrCode = ({ config, dataId }) => {
8
- const contentRef = useRef(null);
9
- const reactToPrintFn = useReactToPrint({ contentRef });
8
+ const printComponentRef = useRef();
10
9
  const [qrCodeData, setQrCodeData] = useState({
11
10
  logo: '',
12
11
  qrCode: '',
@@ -27,13 +26,9 @@ const QrCode = ({ config, dataId }) => {
27
26
  fetchQrCode(config);
28
27
  }, []);
29
28
 
30
- useEffect(() => {
31
- console.info(qrCodeData);
32
- }, [qrCodeData]);
33
-
34
29
  return (
35
30
  <>
36
- <div className={styles.gridtxt} ref={contentRef}>
31
+ <div className={styles.gridtxt} ref={printComponentRef}>
37
32
  {qrCodeData.logo && qrCodeData.logo !== '' && (
38
33
  <ul className={styles.customer__overview}>
39
34
  <li
@@ -57,7 +52,7 @@ const QrCode = ({ config, dataId }) => {
57
52
  textAlign: 'center',
58
53
  }}
59
54
  >
60
- {qrCodeData.title || ''}
55
+ {qrCodeData.title}
61
56
  </li>
62
57
  </ul>
63
58
  <ul className={styles.customer__overview}>
@@ -76,9 +71,10 @@ const QrCode = ({ config, dataId }) => {
76
71
  </ul>
77
72
  </div>
78
73
  <div className={styles.polActions}>
79
- <button className={styles.btn} onClick={() => reactToPrintFn()}>
80
- Print
81
- </button>
74
+ <ReactToPrint
75
+ trigger={() => <button className="btn">Print</button>}
76
+ content={() => printComponentRef.current}
77
+ />
82
78
  </div>
83
79
  </>
84
80
  );
@@ -398,6 +398,7 @@ const QuickAction = ({ childRef, navConfig }) => {
398
398
  <button
399
399
  onClick={() => {
400
400
  if (location.pathname === '/leads/create') {
401
+ console.info('aa');
401
402
  childRef.current.contactOpen();
402
403
  } else {
403
404
  contactModalOpen('create', 0);
@@ -51,12 +51,8 @@ const GenericAuth = ({
51
51
  const updateAuthStatus = async () => {
52
52
  try {
53
53
  const { data } = await CustomFetch('/ajax/user/profile', 'GET');
54
-
55
- if (data && data.id) {
56
- setUserProfile(data);
57
- setIsAuthenticated(true);
58
- }
59
-
54
+ setUserProfile(data);
55
+ setIsAuthenticated(true);
60
56
  setIsLoading(false);
61
57
 
62
58
  if (location.pathname.split('/')[1] === 'login') {
@@ -1,352 +1,42 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { ResponsiveBar } from '@nivo/bar';
3
- import Select from 'react-select';
4
- import styles from './styles/GenericDashboard.module.scss';
1
+ import React, { useState, useEffect } from 'react';
2
+ import { toast } from 'react-toastify';
3
+ import CustomFetch from '../../crm/Fetch';
5
4
 
6
- import CustomFetch from '../Fetch';
7
-
8
- function GenericDashboard({ setting, userProfile }) {
5
+ const GenericDashboard = ({ userProfile }) => {
9
6
  const [data, setData] = useState([]);
10
- const [dropdown, setDropdown] = useState({ filters: [] });
11
- const [selectedFilters, setSelectedFilters] = useState({});
12
- const [filterValues, setFilterValues] = useState({});
13
- const [errorMessage, setErrorMessage] = useState('');
14
-
15
- const customStyles = {
16
- control: (base) => ({
17
- ...base,
18
- height: '51px',
19
- minHeight: '51px',
20
- padding: '0',
21
- borderColor: '#ced4da',
22
- borderRadius: '5px',
23
- boxShadow: 'none',
24
- '&:hover': {
25
- borderColor: '#80bdff',
26
- },
27
- }),
28
- valueContainer: (base) => ({
29
- ...base,
30
- height: '51px',
31
- padding: '0 8px',
32
- display: 'flex',
33
- alignItems: 'center',
34
- }),
35
- };
36
-
37
- const fetchDropdownData = async () => {
38
- try {
39
- const response = await CustomFetch(
40
- setting.api.dropdownUrl,
41
- 'POST',
42
- {}
43
- );
44
- setDropdown((prevState) => ({
45
- ...prevState,
46
- filters: response?.data?.data || [],
47
- }));
48
- } catch (error) {
49
- console.error('Error fetching dropdown data:', error);
50
- }
51
- };
52
-
53
- const fetchData = async () => {
54
- try {
55
- const filters = Object.keys(filterValues).reduce((acc, key) => {
56
- if (filterValues[key]) {
57
- acc[key] = filterValues[key];
58
- }
59
- return acc;
60
- }, {});
61
-
62
- const response = await CustomFetch(
63
- setting.api.fetchDataUrl,
64
- 'POST',
65
- filters
66
- );
67
- setData(response?.data?.data || []);
68
- } catch (error) {
69
- console.error('Error fetching data:', error);
70
- }
71
- };
72
-
73
- const handleFilterChange = (filterId, value) => {
74
- setFilterValues((prevValues) => ({
75
- ...prevValues,
76
- [filterId]: value,
77
- }));
78
- };
79
-
80
- useEffect(() => {
81
- fetchDropdownData();
82
- }, []);
83
-
84
- useEffect(() => {
85
- fetchData();
86
- }, [filterValues]);
87
7
 
88
8
  return (
89
- <div className={styles.grid}>
90
- <div className={styles.filterContainer}>
91
- <div className={styles.filterHeader}>
92
- <h3>{setting.filters?.title || 'Filters'}</h3>
93
- </div>
94
- <div className={styles.filterFields}>
95
- {setting.filters?.filters?.map((filter, index) => {
96
- switch (filter.type) {
97
- case 'dateRange':
98
- return (
99
- <div
100
- key={index}
101
- className={styles.filterField}
102
- >
103
- <label htmlFor={filter.startDateId}>
104
- {filter.startDateLabel ||
105
- 'Start Date'}
106
- </label>
107
- <input
108
- id={filter.startDateId}
109
- type="date"
110
- value={
111
- filterValues[
112
- filter.startDateId
113
- ] || ''
114
- }
115
- onChange={(e) =>
116
- handleFilterChange(
117
- filter.startDateId,
118
- e.target.value
119
- )
120
- }
121
- />
122
- <label htmlFor={filter.endDateId}>
123
- {filter.endDateLabel || 'End Date'}
124
- </label>
125
- <input
126
- id={filter.endDateId}
127
- type="date"
128
- value={
129
- filterValues[
130
- filter.endDateId
131
- ] || ''
132
- }
133
- onChange={(e) =>
134
- handleFilterChange(
135
- filter.endDateId,
136
- e.target.value
137
- )
138
- }
139
- />
140
- </div>
141
- );
142
- case 'dropdown':
143
- return (
144
- <div
145
- key={index}
146
- className={styles.filterField}
147
- >
148
- <label htmlFor={filter.id}>
149
- {filter.label || 'Select Option'}
150
- </label>
151
- <Select
152
- id={filter.id}
153
- isMulti={filter.isMulti}
154
- options={dropdown.filters.map(
155
- (option) => ({
156
- value: option.id,
157
- label: option.label,
158
- })
159
- )}
160
- styles={customStyles}
161
- value={selectedFilters[filter.id]}
162
- onChange={(value) =>
163
- handleFilterChange(
164
- filter.id,
165
- value
166
- )
167
- }
168
- />
169
- </div>
170
- );
171
- default:
172
- return null;
173
- }
174
- })}
175
- <div className={styles.filterActions}>
176
- <button onClick={fetchData} className="btn btn-primary">
177
- {setting.filters?.applyButtonLabel ||
178
- 'Apply Filters'}
179
- </button>
180
- <button
181
- onClick={() => {
182
- setFilterValues({});
183
- setErrorMessage('');
184
- }}
185
- className="btn btn-secondary"
186
- >
187
- {setting.filters?.clearButtonLabel ||
188
- 'Clear Filters'}
189
- </button>
9
+ <div className="grid">
10
+ <div className="grid__row">
11
+ <div className="grid__three dashtopwidge">
12
+ <div className="dashtopwidge__left">
13
+ <h2>
14
+ Current
15
+ <span>
16
+ <strong>Enquiries</strong>
17
+ </span>
18
+ </h2>
19
+ </div>
20
+ <div className="dashtopwidge__right">
21
+ <span>0</span>
190
22
  </div>
191
23
  </div>
192
- {errorMessage && (
193
- <div className={styles.errorMessage}>
194
- <p>{errorMessage}</p>
24
+ <div className="grid__three dashtopwidge">
25
+ <div className="dashtopwidge__left">
26
+ <h2>
27
+ Current{' '}
28
+ <span>
29
+ <strong>Enquiries</strong>
30
+ </span>
31
+ </h2>
195
32
  </div>
196
- )}
33
+ <div className="dashtopwidge__right">
34
+ <span>0</span>
35
+ </div>
36
+ </div>
197
37
  </div>
198
-
199
- {setting.widgets?.map((widget, index) => {
200
- switch (widget.type) {
201
- case 'chart':
202
- return (
203
- <div key={index} className={styles.grid__row}>
204
- <div className={styles.grid__dashfull}>
205
- <div className={styles.widgetTitle}>
206
- <h2>
207
- {widget.title || 'Chart Widget'}
208
- </h2>
209
- </div>
210
- <div style={{ height: 600 }}>
211
- {data.length > 0 ? (
212
- <ResponsiveBar
213
- data={data}
214
- keys={widget.keys || []}
215
- indexBy={
216
- widget.indexBy ||
217
- 'supervisor'
218
- }
219
- margin={
220
- widget.margin || {
221
- top: 20,
222
- right: 200,
223
- bottom: 110,
224
- left: 80,
225
- }
226
- }
227
- padding={0.2}
228
- valueScale={{ type: 'linear' }}
229
- indexScale={{
230
- type: 'band',
231
- round: true,
232
- }}
233
- colors={{
234
- scheme:
235
- widget.colorScheme ||
236
- 'set3',
237
- }}
238
- borderColor={{
239
- from: 'color',
240
- modifiers: [
241
- ['darker', 1.6],
242
- ],
243
- }}
244
- axisBottom={
245
- widget.axisBottom || {
246
- tickSize: 5,
247
- tickPadding: 5,
248
- tickRotation: 45,
249
- }
250
- }
251
- axisLeft={
252
- widget.axisLeft || {
253
- tickSize: 5,
254
- tickPadding: 5,
255
- }
256
- }
257
- legends={widget.legends || []}
258
- />
259
- ) : (
260
- <div>Loading chart...</div>
261
- )}
262
- </div>
263
- </div>
264
- </div>
265
- );
266
- case 'table':
267
- return (
268
- <div key={index} className={styles.grid__row}>
269
- <div className={styles.grid__dashfull}>
270
- <div className={styles.widgetTitle}>
271
- <h2>
272
- {widget.title || 'Table Widget'}
273
- </h2>
274
- </div>
275
- <div className={styles.tableContainer}>
276
- {data.length > 0 ? (
277
- <table>
278
- <thead>
279
- <tr>
280
- <th>
281
- {widget.tableHeader ||
282
- 'Data'}
283
- </th>
284
- {data.map(
285
- (row, index) => (
286
- <th key={index}>
287
- {
288
- row.supervisor
289
- }
290
- </th>
291
- )
292
- )}
293
- </tr>
294
- </thead>
295
- <tbody>
296
- {widget.keys?.map(
297
- (key, index) => {
298
- let total = 0;
299
- return (
300
- <tr key={index}>
301
- <td>
302
- {key}
303
- </td>
304
- {data.map(
305
- (
306
- row
307
- ) => {
308
- const count =
309
- row[
310
- key
311
- ] ||
312
- 0;
313
- total +=
314
- count;
315
- return (
316
- <td
317
- key={
318
- row.supervisor
319
- }
320
- >
321
- {
322
- count
323
- }
324
- </td>
325
- );
326
- }
327
- )}
328
- <td>
329
- {total}
330
- </td>
331
- </tr>
332
- );
333
- }
334
- )}
335
- </tbody>
336
- </table>
337
- ) : (
338
- <div>No data available</div>
339
- )}
340
- </div>
341
- </div>
342
- </div>
343
- );
344
- default:
345
- return null;
346
- }
347
- })}
348
38
  </div>
349
39
  );
350
- }
40
+ };
351
41
 
352
42
  export default GenericDashboard;