@visns-studio/visns-components 5.0.0 → 5.0.1-8.1

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 (37) hide show
  1. package/package.json +3 -1
  2. package/src/components/crm/AsyncSelect.jsx +121 -15
  3. package/src/components/crm/DataGrid.jsx +26 -5
  4. package/src/components/crm/Field.jsx +174 -148
  5. package/src/components/crm/Form.jsx +46 -33
  6. package/src/components/crm/MultiSelect.jsx +96 -29
  7. package/src/components/crm/Navigation.jsx +2 -81
  8. package/src/components/crm/QrCode.jsx +12 -8
  9. package/src/components/crm/auth/Login.jsx +9 -1
  10. package/src/components/crm/auth/Profile.jsx +4 -2
  11. package/src/components/crm/auth/styles/Login.module.scss +52 -82
  12. package/src/components/crm/auth/styles/Profile.module.scss +150 -44
  13. package/src/components/crm/auth/styles/Reset.module.scss +55 -37
  14. package/src/components/crm/auth/styles/Verify.module.scss +55 -76
  15. package/src/components/crm/generic/GenericAuth.jsx +17 -6
  16. package/src/components/crm/generic/GenericDetail.jsx +509 -18
  17. package/src/components/crm/generic/GenericEditableTable.jsx +407 -0
  18. package/src/components/crm/generic/styles/AuditLog.module.scss +4 -37
  19. package/src/components/crm/generic/styles/AuditLogs.module.scss +0 -51
  20. package/src/components/crm/generic/styles/GenericDetail.module.scss +263 -193
  21. package/src/components/crm/generic/styles/GenericDynamic.module.scss +100 -198
  22. package/src/components/crm/generic/styles/GenericEditableTable.module.scss +178 -0
  23. package/src/components/crm/generic/styles/GenericFormBuilder.module.scss +63 -96
  24. package/src/components/crm/generic/styles/GenericIndex.module.scss +46 -66
  25. package/src/components/crm/generic/styles/GenericMain.module.scss +7 -7
  26. package/src/components/crm/generic/styles/GenericSort.module.scss +0 -36
  27. package/src/components/crm/generic/styles/NotificationList.module.scss +1 -32
  28. package/src/components/crm/styles/Autocomplete.module.scss +12 -20
  29. package/src/components/crm/styles/DataGrid.module.scss +6 -89
  30. package/src/components/crm/styles/Field.module.scss +72 -42
  31. package/src/components/crm/styles/Form.module.scss +148 -379
  32. package/src/components/crm/styles/Navigation.module.scss +356 -501
  33. package/src/components/crm/styles/Notification.module.scss +1 -0
  34. package/src/components/crm/styles/QrCode.module.scss +4 -0
  35. package/src/components/crm/styles/SwitchAccount.module.scss +0 -1
  36. package/src/components/crm/styles/TableFilter.module.scss +2 -1
  37. package/src/components/styles/global.css +164 -34
@@ -1,54 +1,120 @@
1
- import React, { useEffect, useState } from 'react';
2
- import SelectList from './SelectList';
3
- import Select, { createFilter } from 'react-select';
1
+ import React, { useEffect, useMemo, useState } from 'react';
2
+ import CustomFetch from './Fetch';
3
+ import Select, { createFilter, components } from 'react-select';
4
+ import CreatableSelect from 'react-select/creatable';
4
5
  import styles from './styles/AsyncSelect.module.scss';
5
6
 
7
+ const CustomOption = (props) => (
8
+ <components.Option {...props}>
9
+ <div>
10
+ {props.data.label}
11
+ {props.data.description && props.data.description !== '' && (
12
+ <div style={{ fontSize: '0.85em', color: '#888' }}>
13
+ {props.data.description}
14
+ </div>
15
+ )}
16
+ </div>
17
+ </components.Option>
18
+ );
19
+
6
20
  function MultiSelect({
7
21
  className,
8
- inputValue,
22
+ inputValue = [],
9
23
  multi,
10
24
  onChange,
11
- options,
25
+ options = [],
12
26
  placeholder,
13
27
  settings,
14
28
  style,
29
+ isCreatable = false,
30
+ creatableConfig = {},
15
31
  }) {
16
32
  const [selectOptions, setSelectOptions] = useState([]);
33
+ const [selectValue, setSelectValue] = useState([]);
34
+
35
+ // Memoize transformed options
36
+ const memoizedOptions = useMemo(() => {
37
+ return options
38
+ .filter((a) => a?.label || a?.name || a?.description) // Filter out options with no label, name, or description
39
+ .map((a) => ({
40
+ value: a?.id,
41
+ label: a?.label || a?.name || a?.description || 'Unknown',
42
+ description: a?.description || '',
43
+ ...a,
44
+ }));
45
+ }, [options]);
46
+
47
+ // Memoize transformed input values
48
+ const memoizedInputValue = useMemo(() => {
49
+ const normalizeInputValue = (value) =>
50
+ Array.isArray(value) ? value : value ? [value] : [];
17
51
 
52
+ return normalizeInputValue(inputValue)
53
+ .filter((a) => a?.label || a?.name || a?.description) // Filter out values with no label, name, or description
54
+ .map((a) => ({
55
+ value: a?.id,
56
+ label: a?.label || a?.name || a?.description || 'Unknown',
57
+ description: a?.description || '',
58
+ ...a,
59
+ }));
60
+ }, [inputValue]);
61
+
62
+ // Set options when memoizedOptions changes
18
63
  useEffect(() => {
19
- let _options = [];
64
+ if (JSON.stringify(selectOptions) !== JSON.stringify(memoizedOptions)) {
65
+ setSelectOptions(memoizedOptions);
66
+ }
67
+ }, [memoizedOptions, selectOptions]);
20
68
 
21
- if (options.length > 0) {
22
- options.forEach((a) => {
23
- let _optionItem = {
24
- value: a.id,
25
- label: a.label,
26
- };
69
+ // Set value when memoizedInputValue changes
70
+ useEffect(() => {
71
+ if (
72
+ JSON.stringify(selectValue) !== JSON.stringify(memoizedInputValue)
73
+ ) {
74
+ setSelectValue(memoizedInputValue);
75
+ }
76
+ }, [memoizedInputValue, selectValue]);
27
77
 
28
- Object.keys(a).forEach((b) => {
29
- if (b !== 'id') {
30
- _optionItem = {
31
- ..._optionItem,
32
- [b]: a[b],
33
- };
34
- }
35
- });
78
+ const handleCreate = async (inputValue) => {
79
+ if (creatableConfig.url && creatableConfig.method) {
80
+ const res = await CustomFetch(
81
+ creatableConfig.url,
82
+ creatableConfig.method,
83
+ { label: inputValue }
84
+ );
85
+
86
+ if (res && res.data) {
87
+ const newOption = {
88
+ value: res.data.id,
89
+ label: res.data.label || inputValue,
90
+ description: res.data.description || 'Newly created item',
91
+ ...res.data,
92
+ };
36
93
 
37
- _options.push(_optionItem);
38
- });
94
+ setSelectOptions((prevOptions) => [...prevOptions, newOption]);
95
+ setSelectValue((prevValues) =>
96
+ multi ? [...prevValues, newOption] : [newOption]
97
+ );
39
98
 
40
- setSelectOptions(_options);
99
+ onChange(
100
+ multi ? [...selectValue, newOption] : newOption,
101
+ { action: 'create-option' },
102
+ settings.id
103
+ );
104
+ }
41
105
  }
42
- }, [options]);
106
+ };
107
+
108
+ const SelectComponent = isCreatable ? CreatableSelect : Select;
43
109
 
44
110
  return (
45
- <Select
46
- closeMenuOnSelect={multi ? false : true}
111
+ <SelectComponent
112
+ closeMenuOnSelect={!multi}
47
113
  isClearable
48
114
  isSearchable
49
115
  isMulti={multi}
50
116
  filterOption={createFilter({ ignoreAccents: false })}
51
- components={{ SelectList }}
117
+ components={{ Option: CustomOption }}
52
118
  options={selectOptions}
53
119
  onChange={(inputValue, action) => {
54
120
  onChange(inputValue, action, settings.id);
@@ -69,8 +135,9 @@ function MultiSelect({
69
135
  : '52px',
70
136
  }),
71
137
  }}
72
- value={inputValue}
73
- placeholder={placeholder ? placeholder : 'Select...'}
138
+ value={selectValue}
139
+ onCreateOption={isCreatable ? handleCreate : undefined}
140
+ placeholder={placeholder || 'Select...'}
74
141
  />
75
142
  );
76
143
  }
@@ -1,45 +1,13 @@
1
1
  import React, { useEffect, useRef, useState, useCallback } from 'react';
2
2
  import { Link, useLocation, useNavigate } from 'react-router-dom';
3
3
  import {
4
- ArrowCycle,
5
- BookClose,
6
- BookOpen,
7
- Briefcase,
8
4
  Bug,
9
5
  Calendar,
10
- ChatDots,
11
- Clipboard,
12
- DoubleSword,
13
6
  Draft,
14
- Envelope,
15
- File,
16
- Folder,
17
- FolderAdd,
18
- Gear,
19
- Grid,
20
- Home,
21
- HomeAlt1,
22
- Inbox,
23
- Info,
24
- LightBulb,
25
- Microphone,
26
- Money,
27
- Newspaper,
28
- Paper,
29
- PeopleGroup,
30
- PeopleMultiple,
31
7
  Person,
32
- Question,
33
- QuestionFill,
34
8
  Search,
35
- SettingsVertical,
36
- ShippingBoxV1,
37
9
  SignOut,
38
10
  Shield,
39
- StatisticUp,
40
- TextAlignLeft,
41
- Ticket,
42
- Utensils,
43
11
  } from 'akar-icons';
44
12
  import CustomFetch from './Fetch';
45
13
  import Notification from './Notification';
@@ -294,47 +262,6 @@ function Navigation({
294
262
  };
295
263
 
296
264
  const renderNav = (n) => {
297
- const iconComponents = {
298
- arrowCycle: ArrowCycle,
299
- bookClose: BookClose,
300
- bookOpen: BookOpen,
301
- briefcase: Briefcase,
302
- calendar: Calendar,
303
- chatDots: ChatDots,
304
- clipboard: Clipboard,
305
- doubleSword: DoubleSword,
306
- draft: Draft,
307
- envelope: Envelope,
308
- file: File,
309
- folder: Folder,
310
- folderAdd: FolderAdd,
311
- gear: Gear,
312
- grid: Grid,
313
- home: Home,
314
- homeAlt1: HomeAlt1,
315
- inbox: Inbox,
316
- info: Info,
317
- lightBulb: LightBulb,
318
- microphone: Microphone,
319
- money: Money,
320
- newspaper: Newspaper,
321
- paper: Paper,
322
- peopleGroup: PeopleGroup,
323
- peopleMultiple: PeopleMultiple,
324
- question: Question,
325
- questionFill: QuestionFill,
326
- search: Search,
327
- settingsVertical: SettingsVertical,
328
- signOut: SignOut,
329
- shippingBoxV1: ShippingBoxV1,
330
- statisticUp: StatisticUp,
331
- textAlignLeft: TextAlignLeft,
332
- ticket: Ticket,
333
- utensils: Utensils,
334
- };
335
-
336
- const IconComponent = iconComponents[n.icon];
337
-
338
265
  let navItemClasses;
339
266
 
340
267
  if (n.class.includes('active')) {
@@ -375,15 +302,9 @@ function Navigation({
375
302
  return (
376
303
  <li className={navItemClasses}>
377
304
  {n.url ? (
378
- <Link to={n.url}>
379
- <IconComponent strokeWidth={2} size={18} />
380
- {n.label}
381
- </Link>
305
+ <Link to={n.url}>{n.label}</Link>
382
306
  ) : (
383
- <span title={n.label}>
384
- <IconComponent strokeWidth={2} size={18} />
385
- {n.label}
386
- </span>
307
+ <span title={n.label}>{n.label}</span>
387
308
  )}
388
309
  {renderChildren()}
389
310
  </li>
@@ -1,11 +1,12 @@
1
1
  import { useEffect, useRef, useState } from 'react';
2
- import ReactToPrint from 'react-to-print';
2
+ import { useReactToPrint } 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 printComponentRef = useRef();
8
+ const contentRef = useRef(null);
9
+ const reactToPrintFn = useReactToPrint({ contentRef });
9
10
  const [qrCodeData, setQrCodeData] = useState({
10
11
  logo: '',
11
12
  qrCode: '',
@@ -26,9 +27,13 @@ const QrCode = ({ config, dataId }) => {
26
27
  fetchQrCode(config);
27
28
  }, []);
28
29
 
30
+ useEffect(() => {
31
+ console.info(qrCodeData);
32
+ }, [qrCodeData]);
33
+
29
34
  return (
30
35
  <>
31
- <div className={styles.gridtxt} ref={printComponentRef}>
36
+ <div className={styles.gridtxt} ref={contentRef}>
32
37
  {qrCodeData.logo && qrCodeData.logo !== '' && (
33
38
  <ul className={styles.customer__overview}>
34
39
  <li
@@ -52,7 +57,7 @@ const QrCode = ({ config, dataId }) => {
52
57
  textAlign: 'center',
53
58
  }}
54
59
  >
55
- {qrCodeData.title}
60
+ {qrCodeData.title || ''}
56
61
  </li>
57
62
  </ul>
58
63
  <ul className={styles.customer__overview}>
@@ -71,10 +76,9 @@ const QrCode = ({ config, dataId }) => {
71
76
  </ul>
72
77
  </div>
73
78
  <div className={styles.polActions}>
74
- <ReactToPrint
75
- trigger={() => <button className="btn">Print</button>}
76
- content={() => printComponentRef.current}
77
- />
79
+ <button className={styles.btn} onClick={() => reactToPrintFn()}>
80
+ Print
81
+ </button>
78
82
  </div>
79
83
  </>
80
84
  );
@@ -28,7 +28,15 @@ const Login = ({ logo, providers, setSystemAuth, setUserProfile }) => {
28
28
  let content =
29
29
  providers && providers.length > 0 ? (
30
30
  <div className="formItem fwItem lastItem forgotten">
31
- <span>OR</span>
31
+ <span
32
+ style={{
33
+ display: 'block',
34
+ textAlign: 'center',
35
+ width: '100%',
36
+ }}
37
+ >
38
+ OR
39
+ </span>
32
40
  </div>
33
41
  ) : null;
34
42
 
@@ -101,10 +101,12 @@ const Profile = ({ userProfile, setUserProfile }) => {
101
101
  payload
102
102
  );
103
103
 
104
+ const updatedUserData = res.data.user ? res.data.user : res.data;
105
+
104
106
  setUserProfile((prevState) => ({
105
107
  ...prevState,
106
- name: res.data.user.name,
107
- signature: res.data.user.signature,
108
+ name: updatedUserData.name,
109
+ signature: updatedUserData.signature,
108
110
  }));
109
111
  toast.success('Your profile has been successfully updated.');
110
112
  } catch (err) {
@@ -1,21 +1,22 @@
1
1
  .formItem {
2
2
  width: 50%;
3
- padding: 0.5em;
4
3
  position: relative;
5
4
  box-sizing: border-box;
6
5
 
7
6
  .fi__label {
8
7
  position: relative;
8
+ display: flex;
9
+ align-items: center;
9
10
  }
10
11
 
11
12
  .fi__span {
12
13
  position: absolute;
13
14
  transition: all 200ms;
14
- opacity: 0.8;
15
+ opacity: 1;
15
16
  left: 0;
16
- padding: 19px 20px;
17
17
  transform-origin: top left;
18
18
  cursor: text;
19
+ padding: 0 1rem;
19
20
  }
20
21
 
21
22
  .fi__div {
@@ -23,14 +24,14 @@
23
24
  opacity: 0.8;
24
25
  left: 0;
25
26
  padding: 19px 13px;
26
- border-radius: var(--br);
27
- border: 1px solid rgba(var(--paragraph-color-rgb), 0.15);
27
+ border-radius: var(--radius);
28
+ border: 1px solid rgba(var(--paragraph-rgb), 0.15);
28
29
  box-shadow: none;
29
30
  }
30
31
 
31
32
  .fi__toggle {
32
33
  width: max-content;
33
- padding: 0px 10px 0px 0;
34
+ padding: 0 10px 0 0;
34
35
  box-sizing: border-box;
35
36
  display: inline;
36
37
  }
@@ -43,45 +44,43 @@
43
44
  }
44
45
  }
45
46
 
46
- .fwItem {
47
- flex-basis: 100%;
47
+ .fwItem,
48
+ .fwBuilderItem {
49
+ grid-column: 1 / -1;
50
+ width: 100%;
48
51
  }
49
52
 
50
- .lastItem {
51
- .btn {
52
- width: 100%;
53
- }
53
+ .lastItem .btn {
54
+ width: 100%;
54
55
  }
55
56
 
56
57
  .loginlogo {
57
58
  display: block;
58
59
  width: 100%;
59
- max-width: 140px;
60
+ max-width: 200px;
60
61
  height: auto;
61
62
  margin: 0 auto;
62
63
  padding-bottom: 1rem;
63
64
  }
64
65
 
65
66
  .logincontainer {
67
+ width: 100%;
66
68
  min-height: 100vh;
67
- flex-grow: 1;
68
- display: flex;
69
- align-items: center;
70
- justify-content: center;
71
- align-content: center;
72
- flex-wrap: wrap;
69
+ display: grid;
70
+ place-items: center;
71
+ padding: 1rem;
73
72
  }
74
73
 
75
74
  .login {
76
75
  width: 35rem;
77
- height: auto;
78
76
  padding: 1rem;
79
77
  margin: 0;
80
- display: flex;
81
- flex-wrap: wrap;
78
+ display: grid;
79
+ grid-template-columns: 1fr;
80
+ gap: 0.5rem;
82
81
 
83
82
  > .formItem {
84
- padding: 0.25em 0;
83
+ padding: 0;
85
84
 
86
85
  small {
87
86
  position: absolute;
@@ -94,12 +93,11 @@
94
93
  }
95
94
 
96
95
  > .loginitem {
97
- width: 100%;
96
+ grid-column: 1 / -1;
98
97
  padding: 0.25em 0;
99
98
 
100
99
  label {
101
100
  width: 100%;
102
- padding-bottom: 0.25rem;
103
101
  }
104
102
 
105
103
  button {
@@ -108,27 +106,28 @@
108
106
  }
109
107
  }
110
108
 
111
- .forgotten {
112
- span {
113
- display: block;
114
- text-align: center;
115
- color: var(--paragraph-color);
116
- padding: 1rem 0;
109
+ .forgotten span {
110
+ display: block;
111
+ text-align: center;
112
+ color: var(--paragraph-color);
113
+ padding: 1rem 0;
117
114
 
118
- a {
119
- color: var(--highlight-color);
120
- text-decoration: none;
121
- }
115
+ a {
116
+ color: var(--secondary-color);
117
+ text-decoration: none;
122
118
  }
123
119
  }
124
120
 
125
121
  .lcontainer {
126
- --sidebar-width: 35%;
127
122
  min-height: 100vh;
123
+ display: flex;
124
+ align-items: center;
125
+ justify-content: center;
128
126
  }
129
127
 
130
128
  .lwrap {
131
- display: flex;
129
+ display: grid;
130
+ grid-template-columns: var(--sidebar-width) 1fr;
132
131
  }
133
132
 
134
133
  .aside {
@@ -139,14 +138,6 @@
139
138
  width: var(--sidebar-width);
140
139
  }
141
140
 
142
- .fwItem {
143
- flex-basis: 100%;
144
- }
145
-
146
- .lastItem .btn {
147
- width: 100%;
148
- }
149
-
150
141
  .btn {
151
142
  width: max-content;
152
143
  display: inline-block;
@@ -154,51 +145,30 @@
154
145
  padding: 0.65rem 1rem;
155
146
  cursor: pointer;
156
147
  font-size: 1rem;
157
- color: var(--tertiary-color);
148
+ color: var(--third-color);
158
149
  text-decoration: none;
159
150
  overflow: hidden;
160
151
  background: var(--primary-color);
161
- border: 1px solid rgba(var(--primary-color--rgb), 1.1);
162
- border-radius: var(--br);
152
+ border: 1px solid rgba(var(--primary-rgb), 1.1);
153
+ border-radius: var(--radius);
163
154
  outline: none;
164
- transition: all 0.2s cubic-bezier(0.85, 0, 0.15, 1) 0s;
165
- font-size: 1.25em;
155
+ transition: all 0.2s cubic-bezier(0.85, 0, 0.15, 1);
156
+
157
+ &:hover {
158
+ color: var(--bg-color);
159
+ background: var(--secondary-color);
160
+ border: 1px solid rgba(var(--secondary-rgb), 1.05);
161
+ }
166
162
  }
167
163
 
168
164
  input:not(:placeholder-shown) + .fi__span,
169
165
  textarea:not(:placeholder-shown) + .fi__span,
170
166
  select:not(:placeholder-shown) + .fi__span {
171
- transform: translateY(-40%) translateX(10px) scale(0.75);
167
+ transform: translateY(-135%) translateX(10px) scale(0.9);
172
168
  opacity: 1;
173
- padding: 0 4px;
174
- background: var(--tertiary-color);
175
- font-weight: 300;
176
- }
177
-
178
- @media (max-width: 1024px) {
179
- body {
180
- font-size: 85%;
181
- }
182
-
183
- .aside {
184
- width: 100%;
185
- transform: translateX(-2000px);
186
- transition: all 0.55s cubic-bezier(0.25, 0.8, 0.25, 1);
187
- will-change: transform;
188
- z-index: 500;
189
- }
190
-
191
- .logincontainer {
192
- min-height: 100vh;
193
- flex-grow: 1;
194
- display: flex;
195
- align-items: center;
196
- justify-content: center;
197
- align-content: center;
198
- flex-wrap: wrap;
199
- }
200
-
201
- .login {
202
- width: 95%;
203
- }
169
+ padding: 0;
170
+ background: var(--bg-color);
171
+ font-weight: 400;
172
+ transition: all 0.5s cubic-bezier(0.5, 0.5, 0, 1);
173
+ color: rgba(var(--paragraph-rgb), 0.65) !important;
204
174
  }