@visns-studio/visns-components 5.5.3 → 5.5.4
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/README.md +153 -7
- package/package.json +1 -1
- package/src/components/crm/Form.jsx +124 -61
package/README.md
CHANGED
|
@@ -14,28 +14,34 @@ VISNS Components is a React-based UI component library that provides a set of re
|
|
|
14
14
|
### Authentication System
|
|
15
15
|
|
|
16
16
|
- **Login** - Email/password authentication
|
|
17
|
-
- **Two-Factor Authentication (2FA)** - Additional security layer
|
|
17
|
+
- **Two-Factor Authentication (2FA)** - Additional security layer with QR code and manual setup
|
|
18
18
|
- **Password Reset** - Secure password recovery flow
|
|
19
19
|
- **Profile Management** - User profile editing
|
|
20
20
|
- **SSO Integration** - Support for Microsoft Azure
|
|
21
|
+
- **Remember Me** - Persistent login option that works with 2FA
|
|
21
22
|
|
|
22
23
|
### Data Visualization
|
|
23
24
|
|
|
24
25
|
- **DataGrid** - Powerful data table with sorting, filtering, and pagination
|
|
25
26
|
- **Charts** - Various chart types for data visualization
|
|
27
|
+
- **Report Builder** - Custom report creation with table joins, column selection, and filtering
|
|
28
|
+
- **Sketch Field** - Interactive drawing tools for diagrams and annotations
|
|
26
29
|
|
|
27
30
|
### Form Components
|
|
28
31
|
|
|
29
32
|
- **Form Builder** - Dynamic form creation and validation
|
|
30
33
|
- **Field Components** - Text inputs, selects, checkboxes, etc.
|
|
31
|
-
- **Multi-select** - Enhanced select with multiple selection support
|
|
34
|
+
- **Multi-select** - Enhanced select with multiple selection support and selection limits
|
|
32
35
|
- **Autocomplete** - Mapbox integration for location selection
|
|
33
36
|
- **Dropzone** - File upload with drag and drop support
|
|
37
|
+
- **AsyncSelect** - Asynchronous data loading for select components
|
|
38
|
+
- **Creatable Select** - Select components that allow creating new options
|
|
34
39
|
|
|
35
40
|
### Navigation
|
|
36
41
|
|
|
37
42
|
- **Header/Navigation** - Application header with navigation
|
|
38
43
|
- **Sub Navigation** - Secondary navigation options
|
|
44
|
+
- **Breadcrumb** - Path-based navigation component
|
|
39
45
|
|
|
40
46
|
### Utility Components
|
|
41
47
|
|
|
@@ -44,6 +50,7 @@ VISNS Components is a React-based UI component library that provides a set of re
|
|
|
44
50
|
- **Loader** - Loading indicators
|
|
45
51
|
- **Notification** - Toast notifications
|
|
46
52
|
- **Call Popup** - Modal dialogs
|
|
53
|
+
- **QR Code** - QR code generation and printing
|
|
47
54
|
|
|
48
55
|
### Generic Pages
|
|
49
56
|
|
|
@@ -51,6 +58,7 @@ VISNS Components is a React-based UI component library that provides a set of re
|
|
|
51
58
|
- **Detail Pages** - Item detail templates
|
|
52
59
|
- **Notification Pages** - User notification center
|
|
53
60
|
- **Sorting Pages** - Content ordering interfaces
|
|
61
|
+
- **Report Pages** - Custom report building and execution
|
|
54
62
|
|
|
55
63
|
## Installation
|
|
56
64
|
|
|
@@ -230,7 +238,7 @@ A flexible form component with validation.
|
|
|
230
238
|
|
|
231
239
|
#### AsyncSelect
|
|
232
240
|
|
|
233
|
-
Asynchronous select component with search functionality.
|
|
241
|
+
Asynchronous select component with search functionality and dynamic data loading.
|
|
234
242
|
|
|
235
243
|
```jsx
|
|
236
244
|
<AsyncSelect
|
|
@@ -239,23 +247,53 @@ Asynchronous select component with search functionality.
|
|
|
239
247
|
labelKey="name"
|
|
240
248
|
placeholder="Select an option"
|
|
241
249
|
onChange={handleChange}
|
|
242
|
-
|
|
250
|
+
inputValue={selectedValue}
|
|
251
|
+
settings={{
|
|
252
|
+
id: 'myAsyncSelect',
|
|
253
|
+
params: { additionalParam: 'value' }, // Optional: additional parameters to send with the request
|
|
254
|
+
}}
|
|
255
|
+
isCreatable={false} // Optional: allow creating new options
|
|
256
|
+
creatableConfig={{
|
|
257
|
+
// Configuration for creatable options
|
|
258
|
+
url: '/api/options/create',
|
|
259
|
+
method: 'POST',
|
|
260
|
+
}}
|
|
243
261
|
/>
|
|
244
262
|
```
|
|
245
263
|
|
|
264
|
+
The AsyncSelect component supports these features:
|
|
265
|
+
|
|
266
|
+
- **Dynamic Data Loading**: Loads options from an API endpoint as the user types
|
|
267
|
+
- **Debounced Requests**: Prevents excessive API calls during typing
|
|
268
|
+
- **Custom Parameters**: Send additional parameters with the API request
|
|
269
|
+
- **Creatable Options**: Allow users to create new options that don't exist in the API
|
|
270
|
+
|
|
246
271
|
#### MultiSelect
|
|
247
272
|
|
|
248
|
-
Select component with multiple selection support.
|
|
273
|
+
Select component with multiple selection support and optional selection limits.
|
|
249
274
|
|
|
250
275
|
```jsx
|
|
251
276
|
<MultiSelect
|
|
252
277
|
options={options}
|
|
253
|
-
|
|
278
|
+
inputValue={selectedValues}
|
|
254
279
|
onChange={handleChange}
|
|
255
280
|
placeholder="Select options"
|
|
281
|
+
multi={true}
|
|
282
|
+
settings={{
|
|
283
|
+
id: 'myMultiSelect',
|
|
284
|
+
limit: 3, // Optional: limit the number of selections
|
|
285
|
+
}}
|
|
286
|
+
isCreatable={false} // Optional: allow creating new options
|
|
287
|
+
creatableConfig={{}} // Configuration for creatable options
|
|
256
288
|
/>
|
|
257
289
|
```
|
|
258
290
|
|
|
291
|
+
The MultiSelect component supports these features:
|
|
292
|
+
|
|
293
|
+
- **Selection Limits**: Set a maximum number of items that can be selected
|
|
294
|
+
- **Single Item Mode**: When `limit={1}` with `multi={true}`, it keeps only the most recently selected item
|
|
295
|
+
- **Creatable Options**: Allow users to create new options that don't exist in the list
|
|
296
|
+
|
|
259
297
|
#### Autocomplete
|
|
260
298
|
|
|
261
299
|
Location autocomplete with Mapbox integration.
|
|
@@ -289,6 +327,10 @@ File upload component with drag and drop support.
|
|
|
289
327
|
Data fetching utility with error handling.
|
|
290
328
|
|
|
291
329
|
```jsx
|
|
330
|
+
// Using async/await
|
|
331
|
+
const result = await CustomFetch('/api/data', 'GET', { param1: 'value1' });
|
|
332
|
+
|
|
333
|
+
// Using callbacks
|
|
292
334
|
CustomFetch(
|
|
293
335
|
'/api/data',
|
|
294
336
|
'GET',
|
|
@@ -332,6 +374,24 @@ Toast notification component.
|
|
|
332
374
|
/>
|
|
333
375
|
```
|
|
334
376
|
|
|
377
|
+
#### QrCode
|
|
378
|
+
|
|
379
|
+
QR code generation and printing component.
|
|
380
|
+
|
|
381
|
+
```jsx
|
|
382
|
+
<QrCode config={{ url: '/api/qrcode' }} dataId={123} />
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
The QrCode component fetches QR code data from the specified URL and displays it with a print button. The API response should include:
|
|
386
|
+
|
|
387
|
+
```json
|
|
388
|
+
{
|
|
389
|
+
"logo": "optional-logo-url",
|
|
390
|
+
"qrCode": "base64-encoded-svg-data",
|
|
391
|
+
"title": "QR Code Title"
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
335
395
|
## Two-Factor Authentication (2FA)
|
|
336
396
|
|
|
337
397
|
### How 2FA Works
|
|
@@ -342,6 +402,15 @@ Toast notification component.
|
|
|
342
402
|
4. User enters the verification code
|
|
343
403
|
5. If the code is valid, the user is authenticated and redirected to the main application
|
|
344
404
|
|
|
405
|
+
### Setting Up 2FA
|
|
406
|
+
|
|
407
|
+
Users can set up 2FA through their profile page. The setup process includes:
|
|
408
|
+
|
|
409
|
+
1. Generating a QR code that can be scanned with authenticator apps
|
|
410
|
+
2. Providing a secret key for manual entry
|
|
411
|
+
3. Verifying the setup with a 6-digit code
|
|
412
|
+
4. Receiving recovery codes for backup access
|
|
413
|
+
|
|
345
414
|
### Enforcing 2FA
|
|
346
415
|
|
|
347
416
|
You can enforce 2FA for all users by setting the `enforce2FA` prop to `true` on the GenericAuth component:
|
|
@@ -367,6 +436,14 @@ When 2FA enforcement is disabled (default):
|
|
|
367
436
|
1. No alert is shown to users without 2FA
|
|
368
437
|
2. Users can still enable 2FA voluntarily through their profile page
|
|
369
438
|
|
|
439
|
+
### Remember Me Functionality
|
|
440
|
+
|
|
441
|
+
The login form includes a "Remember Me" checkbox that persists through the 2FA verification process. When enabled:
|
|
442
|
+
|
|
443
|
+
1. The user's login session will be remembered for a longer period
|
|
444
|
+
2. The "Remember Me" setting is passed from the login form to the 2FA verification process
|
|
445
|
+
3. The setting is applied only after successful 2FA verification
|
|
446
|
+
|
|
370
447
|
### API Integration for 2FA
|
|
371
448
|
|
|
372
449
|
#### Login Endpoint Response (when 2FA is required)
|
|
@@ -391,7 +468,8 @@ Request:
|
|
|
391
468
|
{
|
|
392
469
|
"user_id": "user_id",
|
|
393
470
|
"verification_code": "123456",
|
|
394
|
-
"previous_url": "/optional-redirect-url"
|
|
471
|
+
"previous_url": "/optional-redirect-url",
|
|
472
|
+
"remember": true
|
|
395
473
|
}
|
|
396
474
|
```
|
|
397
475
|
|
|
@@ -410,6 +488,74 @@ Response:
|
|
|
410
488
|
}
|
|
411
489
|
```
|
|
412
490
|
|
|
491
|
+
#### 2FA Setup Endpoint Response
|
|
492
|
+
|
|
493
|
+
```json
|
|
494
|
+
{
|
|
495
|
+
"two_factor_enabled": true,
|
|
496
|
+
"two_factor_confirmed": false,
|
|
497
|
+
"qr_code": "<SVG data for QR code>",
|
|
498
|
+
"secret_key": "ABCDEFGHIJKLMNOP",
|
|
499
|
+
"recovery_codes": ["code1", "code2", "code3", ...]
|
|
500
|
+
}
|
|
501
|
+
```
|
|
502
|
+
|
|
503
|
+
### Report Builder Component
|
|
504
|
+
|
|
505
|
+
#### GenericReport
|
|
506
|
+
|
|
507
|
+
A powerful report building component that allows users to create custom reports by selecting tables, columns, and configuring joins, filters, and sorting.
|
|
508
|
+
|
|
509
|
+
```jsx
|
|
510
|
+
<GenericReport
|
|
511
|
+
setting={{
|
|
512
|
+
tableUrl: '/ajax/reportBuilder/getTables',
|
|
513
|
+
columnUrl: '/ajax/reportBuilder/getTableColumns',
|
|
514
|
+
reportsUrl: '/ajax/reportBuilder/reports',
|
|
515
|
+
executeUrl: '/ajax/reportBuilder/execute',
|
|
516
|
+
suggestedJoinsUrl: '/ajax/reportBuilder/getSuggestedJoins',
|
|
517
|
+
}}
|
|
518
|
+
/>
|
|
519
|
+
```
|
|
520
|
+
|
|
521
|
+
The GenericReport component provides these features:
|
|
522
|
+
|
|
523
|
+
- **Table Selection**: Choose a main table for the report
|
|
524
|
+
- **Column Selection**: Select columns to include in the report
|
|
525
|
+
- **Table Joins**: Add related tables with automatic relationship detection
|
|
526
|
+
- **Filtering**: Apply filters to the report data
|
|
527
|
+
- **Sorting**: Sort the report results
|
|
528
|
+
- **Report Preview**: View a preview of the report data
|
|
529
|
+
- **Save/Load Reports**: Save reports for future use and load saved reports
|
|
530
|
+
- **Export**: Export reports in various formats
|
|
531
|
+
|
|
532
|
+
### Sketch Field Component
|
|
533
|
+
|
|
534
|
+
#### SketchField
|
|
535
|
+
|
|
536
|
+
An interactive drawing component based on Fabric.js that provides various drawing tools.
|
|
537
|
+
|
|
538
|
+
```jsx
|
|
539
|
+
<SketchField
|
|
540
|
+
width={800}
|
|
541
|
+
height={600}
|
|
542
|
+
tool={Tools.Pencil} // Available tools: Select, Pencil, Line, Arrow, Rectangle, Circle, Pan
|
|
543
|
+
lineColor="black"
|
|
544
|
+
lineWidth={3}
|
|
545
|
+
backgroundColor="transparent"
|
|
546
|
+
backgroundImage="/path/to/background-image.jpg" // Optional
|
|
547
|
+
onChange={handleChange}
|
|
548
|
+
/>
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
The SketchField component supports these features:
|
|
552
|
+
|
|
553
|
+
- **Multiple Drawing Tools**: Pencil, Line, Arrow, Rectangle, Circle, etc.
|
|
554
|
+
- **Background Images**: Add background images for annotation
|
|
555
|
+
- **Undo/Redo**: History management for drawing actions
|
|
556
|
+
- **Save/Load**: Save drawings as JSON and load them later
|
|
557
|
+
- **Customization**: Configure colors, line widths, and other properties
|
|
558
|
+
|
|
413
559
|
## Customization
|
|
414
560
|
|
|
415
561
|
### Theming
|
package/package.json
CHANGED
|
@@ -82,7 +82,7 @@
|
|
|
82
82
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
83
83
|
},
|
|
84
84
|
"name": "@visns-studio/visns-components",
|
|
85
|
-
"version": "5.5.
|
|
85
|
+
"version": "5.5.4",
|
|
86
86
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
87
87
|
"main": "src/index.js",
|
|
88
88
|
"files": [
|
|
@@ -882,7 +882,9 @@ function Form({
|
|
|
882
882
|
closeModal();
|
|
883
883
|
if (type === 'saveAnother') {
|
|
884
884
|
const { saveAnother } =
|
|
885
|
-
formSettings?.update ||
|
|
885
|
+
formSettings?.update ||
|
|
886
|
+
formSettings?.create ||
|
|
887
|
+
{};
|
|
886
888
|
|
|
887
889
|
if (
|
|
888
890
|
saveAnother?.url &&
|
|
@@ -919,6 +921,16 @@ function Form({
|
|
|
919
921
|
fetchData();
|
|
920
922
|
modalOpen('create', 0);
|
|
921
923
|
}
|
|
924
|
+
} else if (type === 'saveExit') {
|
|
925
|
+
const { saveExit } =
|
|
926
|
+
formSettings?.update ||
|
|
927
|
+
formSettings?.create ||
|
|
928
|
+
{};
|
|
929
|
+
|
|
930
|
+
if (saveExit?.redirectUrl) {
|
|
931
|
+
console.info('aa');
|
|
932
|
+
navigate(saveExit.redirectUrl);
|
|
933
|
+
}
|
|
922
934
|
}
|
|
923
935
|
} else {
|
|
924
936
|
toast.success(
|
|
@@ -1505,11 +1517,25 @@ function Form({
|
|
|
1505
1517
|
</button>
|
|
1506
1518
|
) : null}
|
|
1507
1519
|
|
|
1508
|
-
<button
|
|
1520
|
+
<button
|
|
1521
|
+
className={styles.saveBtn}
|
|
1522
|
+
onClick={handleSubmit}
|
|
1523
|
+
data-type="save"
|
|
1524
|
+
>
|
|
1509
1525
|
{formSettings?.save?.button?.label
|
|
1510
1526
|
? formSettings.save.button.label
|
|
1511
1527
|
: 'Save'}
|
|
1512
1528
|
</button>
|
|
1529
|
+
|
|
1530
|
+
{formSettings.hasOwnProperty('saveExit') && (
|
|
1531
|
+
<button
|
|
1532
|
+
className={styles.btn}
|
|
1533
|
+
onClick={handleSubmit}
|
|
1534
|
+
data-type="saveExit"
|
|
1535
|
+
>
|
|
1536
|
+
{formSettings.saveExit?.label || 'Save & Exit'}
|
|
1537
|
+
</button>
|
|
1538
|
+
)}
|
|
1513
1539
|
</div>
|
|
1514
1540
|
</div>
|
|
1515
1541
|
);
|
|
@@ -1621,65 +1647,102 @@ function Form({
|
|
|
1621
1647
|
}}
|
|
1622
1648
|
/>
|
|
1623
1649
|
</div>
|
|
1624
|
-
{
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
.
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1650
|
+
{(() => {
|
|
1651
|
+
if (formType === 'create') {
|
|
1652
|
+
return (
|
|
1653
|
+
<div
|
|
1654
|
+
className={`${styles.formItem} ${styles.fwItem} ${styles.buttonContainer}`}
|
|
1655
|
+
>
|
|
1656
|
+
<button
|
|
1657
|
+
className={styles.btn}
|
|
1658
|
+
onClick={handleSubmit}
|
|
1659
|
+
data-type="save"
|
|
1660
|
+
>
|
|
1661
|
+
Save & Close
|
|
1662
|
+
</button>
|
|
1663
|
+
{formSettings.create?.hasOwnProperty(
|
|
1664
|
+
'saveAnother'
|
|
1665
|
+
) && (
|
|
1666
|
+
<button
|
|
1667
|
+
className={styles.btn}
|
|
1668
|
+
onClick={handleSubmit}
|
|
1669
|
+
data-type="saveAnother"
|
|
1670
|
+
>
|
|
1671
|
+
Save & Create Another
|
|
1672
|
+
</button>
|
|
1673
|
+
)}
|
|
1674
|
+
{formSettings.create?.hasOwnProperty(
|
|
1675
|
+
'saveExit'
|
|
1676
|
+
) && (
|
|
1677
|
+
<button
|
|
1678
|
+
className={styles.btn}
|
|
1679
|
+
onClick={handleSubmit}
|
|
1680
|
+
data-type="saveExit"
|
|
1681
|
+
>
|
|
1682
|
+
{formSettings.create
|
|
1683
|
+
.saveExit?.label ||
|
|
1684
|
+
'Save & Exit'}
|
|
1685
|
+
</button>
|
|
1686
|
+
)}
|
|
1687
|
+
</div>
|
|
1688
|
+
);
|
|
1689
|
+
} else if (formType === 'update') {
|
|
1690
|
+
return (
|
|
1691
|
+
<div
|
|
1692
|
+
className={`${styles.formItem} ${styles.fwItem} ${styles.buttonContainer}`}
|
|
1693
|
+
>
|
|
1694
|
+
<button
|
|
1695
|
+
className={styles.btn}
|
|
1696
|
+
onClick={handleSubmit}
|
|
1697
|
+
data-type="save"
|
|
1698
|
+
>
|
|
1699
|
+
Save & Close
|
|
1700
|
+
</button>
|
|
1701
|
+
{formSettings.update?.hasOwnProperty(
|
|
1702
|
+
'saveAnother'
|
|
1703
|
+
) && (
|
|
1704
|
+
<button
|
|
1705
|
+
className={styles.btn}
|
|
1706
|
+
onClick={handleSubmit}
|
|
1707
|
+
data-type="saveAnother"
|
|
1708
|
+
>
|
|
1709
|
+
{formSettings.update
|
|
1710
|
+
.saveAnother
|
|
1711
|
+
?.label ||
|
|
1712
|
+
'Save & Create Another'}
|
|
1713
|
+
</button>
|
|
1714
|
+
)}
|
|
1715
|
+
{formSettings.update?.hasOwnProperty(
|
|
1716
|
+
'saveExit'
|
|
1717
|
+
) && (
|
|
1718
|
+
<button
|
|
1719
|
+
className={styles.btn}
|
|
1720
|
+
onClick={handleSubmit}
|
|
1721
|
+
data-type="saveExit"
|
|
1722
|
+
>
|
|
1723
|
+
{formSettings.update
|
|
1724
|
+
.saveExit?.label ||
|
|
1725
|
+
'Save & Exit'}
|
|
1726
|
+
</button>
|
|
1727
|
+
)}
|
|
1728
|
+
</div>
|
|
1729
|
+
);
|
|
1730
|
+
} else {
|
|
1731
|
+
return (
|
|
1732
|
+
<div
|
|
1733
|
+
className={`${styles.formItem} ${styles.fwItem} ${styles.lastItem}`}
|
|
1734
|
+
>
|
|
1735
|
+
<button
|
|
1736
|
+
className={styles.btn}
|
|
1737
|
+
onClick={handleSubmit}
|
|
1738
|
+
data-type="save"
|
|
1739
|
+
>
|
|
1740
|
+
Save
|
|
1741
|
+
</button>
|
|
1742
|
+
</div>
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
})()}
|
|
1683
1746
|
</form>
|
|
1684
1747
|
</div>
|
|
1685
1748
|
</div>
|