@visns-studio/visns-components 5.11.22 → 5.12.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.
- package/README.md +191 -0
- package/package.json +3 -3
- package/src/components/DataGrid.jsx +39 -5
- package/src/components/Form.jsx +64 -29
- package/src/utils/ConfirmDialog.jsx +1 -1
package/README.md
CHANGED
|
@@ -625,6 +625,197 @@ You can add summary calculations to grouped data:
|
|
|
625
625
|
/>
|
|
626
626
|
```
|
|
627
627
|
|
|
628
|
+
### Group-Level Bulk Edit Operations (Enhanced v5.13.0+)
|
|
629
|
+
|
|
630
|
+
The DataGrid component now supports powerful bulk editing capabilities through group-based operations, allowing users to efficiently manage multiple items simultaneously.
|
|
631
|
+
|
|
632
|
+
#### How Group Bulk Edit Works
|
|
633
|
+
|
|
634
|
+
When data is grouped using the `groupBy` or `defaultGroupBy` properties, each group header can display action icons for common operations. The new bulk edit functionality adds an "edit" icon that opens a form modal for updating all items in the group simultaneously.
|
|
635
|
+
|
|
636
|
+
#### Configuration Structure
|
|
637
|
+
|
|
638
|
+
Group operations are configured through the `ajaxSetting.groupBySetting.icons` array:
|
|
639
|
+
|
|
640
|
+
```jsx
|
|
641
|
+
<DataGrid
|
|
642
|
+
ajaxSetting={{
|
|
643
|
+
url: '/api/data',
|
|
644
|
+
groupBy: ['category'],
|
|
645
|
+
groupBySetting: {
|
|
646
|
+
icons: [
|
|
647
|
+
{
|
|
648
|
+
id: 'edit',
|
|
649
|
+
label: 'Edit Group',
|
|
650
|
+
formModal: {
|
|
651
|
+
title: 'Bulk Edit Items',
|
|
652
|
+
url: '/ajax/data/bulkFormUpdate',
|
|
653
|
+
method: 'POST',
|
|
654
|
+
groupKey: 'category',
|
|
655
|
+
fields: ['assigned_user_id', 'status'],
|
|
656
|
+
data: {
|
|
657
|
+
stage: 'processing'
|
|
658
|
+
}
|
|
659
|
+
},
|
|
660
|
+
show: [
|
|
661
|
+
{
|
|
662
|
+
id: 'status',
|
|
663
|
+
value: 'pending'
|
|
664
|
+
}
|
|
665
|
+
]
|
|
666
|
+
}
|
|
667
|
+
]
|
|
668
|
+
}
|
|
669
|
+
}}
|
|
670
|
+
// Other props...
|
|
671
|
+
/>
|
|
672
|
+
```
|
|
673
|
+
|
|
674
|
+
#### Configuration Properties
|
|
675
|
+
|
|
676
|
+
**Icon Configuration:**
|
|
677
|
+
- `id`: Must be set to `'edit'` to enable bulk edit functionality
|
|
678
|
+
- `label`: Display text for the action button
|
|
679
|
+
- `formModal`: Configuration object for the bulk edit form
|
|
680
|
+
- `show`: Optional conditions to control when the icon appears
|
|
681
|
+
|
|
682
|
+
**Form Modal Configuration:**
|
|
683
|
+
- `title`: Title displayed in the bulk edit modal
|
|
684
|
+
- `url`: API endpoint for bulk update requests
|
|
685
|
+
- `method`: HTTP method (typically 'POST')
|
|
686
|
+
- `groupKey`: Field name used for grouping (matches the groupBy value)
|
|
687
|
+
- `fields`: Array of field IDs that can be bulk edited
|
|
688
|
+
- `data`: Additional data to send with the bulk update request
|
|
689
|
+
|
|
690
|
+
#### Form Integration
|
|
691
|
+
|
|
692
|
+
The bulk edit feature integrates seamlessly with the existing Form component. When the edit icon is clicked:
|
|
693
|
+
|
|
694
|
+
1. A form modal opens with the specified title
|
|
695
|
+
2. Only the fields listed in the `fields` array are displayed
|
|
696
|
+
3. The form shows an indication that this is a bulk operation
|
|
697
|
+
4. On submission, all items in the group are updated simultaneously
|
|
698
|
+
|
|
699
|
+
#### Backend Integration
|
|
700
|
+
|
|
701
|
+
The bulk edit functionality expects a specific API endpoint structure:
|
|
702
|
+
|
|
703
|
+
```javascript
|
|
704
|
+
// POST /ajax/data/bulkFormUpdate
|
|
705
|
+
{
|
|
706
|
+
"bulkEdit": true,
|
|
707
|
+
"groupValue": "Category Name",
|
|
708
|
+
"groupKey": "category",
|
|
709
|
+
"rowIds": [1, 2, 3, 4],
|
|
710
|
+
"assigned_user_id": 5,
|
|
711
|
+
"status": "in_progress",
|
|
712
|
+
// Additional form fields...
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
// Expected Response:
|
|
716
|
+
{
|
|
717
|
+
"error": "",
|
|
718
|
+
"message": "Bulk update completed successfully",
|
|
719
|
+
"data": {
|
|
720
|
+
"updated_count": 4,
|
|
721
|
+
"group_value": "Category Name",
|
|
722
|
+
"updated_items": [...]
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
```
|
|
726
|
+
|
|
727
|
+
#### Features and Benefits
|
|
728
|
+
|
|
729
|
+
**User Experience:**
|
|
730
|
+
- **No Confirmation Dialog**: Edit actions open the form immediately (unlike timer operations)
|
|
731
|
+
- **No Toast Notifications**: Silent operation that focuses on the form interaction
|
|
732
|
+
- **Visual Group Context**: Modal title includes group information and item count
|
|
733
|
+
- **Field Filtering**: Only relevant fields are shown for bulk editing
|
|
734
|
+
|
|
735
|
+
**Technical Features:**
|
|
736
|
+
- **Transaction Safety**: All updates wrapped in database transactions
|
|
737
|
+
- **Partial Updates**: Only changed fields are updated
|
|
738
|
+
- **Audit Trail**: Full change tracking for compliance
|
|
739
|
+
- **Error Handling**: Graceful handling of validation errors and conflicts
|
|
740
|
+
- **Performance Optimized**: Efficient bulk operations with minimal database calls
|
|
741
|
+
|
|
742
|
+
#### Advanced Example
|
|
743
|
+
|
|
744
|
+
```jsx
|
|
745
|
+
// Manufacturing workflow with bulk edit capabilities
|
|
746
|
+
<DataGrid
|
|
747
|
+
ajaxSetting={{
|
|
748
|
+
url: '/ajax/manufacturing/items',
|
|
749
|
+
groupBy: ['work_order'],
|
|
750
|
+
groupBySetting: {
|
|
751
|
+
icons: [
|
|
752
|
+
// Bulk edit for user assignment
|
|
753
|
+
{
|
|
754
|
+
id: 'edit',
|
|
755
|
+
label: 'Assign Operator',
|
|
756
|
+
formModal: {
|
|
757
|
+
title: 'Bulk Assign Operator',
|
|
758
|
+
url: '/ajax/manufacturing/bulkAssign',
|
|
759
|
+
method: 'POST',
|
|
760
|
+
groupKey: 'work_order',
|
|
761
|
+
fields: ['operator_id', 'priority'],
|
|
762
|
+
data: {
|
|
763
|
+
stage: 'production',
|
|
764
|
+
update_type: 'assignment'
|
|
765
|
+
}
|
|
766
|
+
},
|
|
767
|
+
show: [
|
|
768
|
+
{ id: 'status', value: 'ready' }
|
|
769
|
+
]
|
|
770
|
+
},
|
|
771
|
+
// Timer operations (existing functionality)
|
|
772
|
+
{
|
|
773
|
+
id: 'clock',
|
|
774
|
+
label: 'Start Production',
|
|
775
|
+
fetch: {
|
|
776
|
+
url: '/ajax/manufacturing/groupTimer',
|
|
777
|
+
method: 'POST',
|
|
778
|
+
data: {
|
|
779
|
+
action: 'start_production',
|
|
780
|
+
timestamp: 'now()'
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
]
|
|
785
|
+
}
|
|
786
|
+
}}
|
|
787
|
+
columns={[
|
|
788
|
+
{ name: 'item_code', headerName: 'Item Code' },
|
|
789
|
+
{ name: 'operator', headerName: 'Operator' },
|
|
790
|
+
{ name: 'status', headerName: 'Status' },
|
|
791
|
+
{ name: 'priority', headerName: 'Priority' }
|
|
792
|
+
]}
|
|
793
|
+
form={{
|
|
794
|
+
fields: [
|
|
795
|
+
{
|
|
796
|
+
id: 'operator_id',
|
|
797
|
+
label: 'Production Operator',
|
|
798
|
+
type: 'dropdown-ajax',
|
|
799
|
+
url: '/ajax/users/operators',
|
|
800
|
+
required: true
|
|
801
|
+
},
|
|
802
|
+
{
|
|
803
|
+
id: 'priority',
|
|
804
|
+
label: 'Priority Level',
|
|
805
|
+
type: 'select',
|
|
806
|
+
options: [
|
|
807
|
+
{ value: 'normal', label: 'Normal' },
|
|
808
|
+
{ value: 'high', label: 'High' },
|
|
809
|
+
{ value: 'urgent', label: 'Urgent' }
|
|
810
|
+
]
|
|
811
|
+
}
|
|
812
|
+
]
|
|
813
|
+
}}
|
|
814
|
+
/>
|
|
815
|
+
```
|
|
816
|
+
|
|
817
|
+
This implementation provides a powerful and intuitive way to manage bulk operations in grouped data scenarios, significantly improving workflow efficiency for users managing large datasets.
|
|
818
|
+
|
|
628
819
|
##### Customizing Group Rendering
|
|
629
820
|
|
|
630
821
|
You can customize how groups are displayed:
|
package/package.json
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"@tinymce/miniature": "^6.0.0",
|
|
15
15
|
"@tinymce/tinymce-react": "^6.2.1",
|
|
16
16
|
"@uiw/react-color": "^2.6.0",
|
|
17
|
-
"@visns-studio/visns-datagrid-community": "
|
|
18
|
-
"@visns-studio/visns-datagrid-enterprise": "
|
|
17
|
+
"@visns-studio/visns-datagrid-community": "1.1.0",
|
|
18
|
+
"@visns-studio/visns-datagrid-enterprise": "1.1.0",
|
|
19
19
|
"@vitejs/plugin-react": "^4.6.0",
|
|
20
20
|
"add": "^2.0.6",
|
|
21
21
|
"array-move": "^4.0.0",
|
|
@@ -87,7 +87,7 @@
|
|
|
87
87
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
88
88
|
},
|
|
89
89
|
"name": "@visns-studio/visns-components",
|
|
90
|
-
"version": "5.
|
|
90
|
+
"version": "5.12.1",
|
|
91
91
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
92
92
|
"main": "src/index.js",
|
|
93
93
|
"files": [
|
|
@@ -275,6 +275,8 @@ const DataGrid = forwardRef(
|
|
|
275
275
|
const [formType, setFormType] = useState('');
|
|
276
276
|
const [formId, setFormId] = useState(0);
|
|
277
277
|
const [modalShow, setModalShow] = useState(false);
|
|
278
|
+
const [bulkEditMode, setBulkEditMode] = useState(false);
|
|
279
|
+
const [bulkEditGroupData, setBulkEditGroupData] = useState(null);
|
|
278
280
|
|
|
279
281
|
/** Modal Functions */
|
|
280
282
|
const modalOpen = (formType, formId) => {
|
|
@@ -285,6 +287,8 @@ const DataGrid = forwardRef(
|
|
|
285
287
|
|
|
286
288
|
const modalClose = () => {
|
|
287
289
|
setModalShow(false);
|
|
290
|
+
setBulkEditMode(false);
|
|
291
|
+
setBulkEditGroupData(null);
|
|
288
292
|
};
|
|
289
293
|
|
|
290
294
|
useEffect(() => {
|
|
@@ -1209,7 +1213,13 @@ const DataGrid = forwardRef(
|
|
|
1209
1213
|
// Get all rows for this group
|
|
1210
1214
|
const groupRows = getGroupRows(groupValue);
|
|
1211
1215
|
|
|
1212
|
-
//
|
|
1216
|
+
// Skip confirmation dialog for edit actions - open modal directly
|
|
1217
|
+
if (iconConfig.id === 'edit' && iconConfig.formModal) {
|
|
1218
|
+
executeGroupAction(groupValue, iconConfig);
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// Show confirmation dialog for other actions to prevent accidental clicks
|
|
1213
1223
|
const confirmMessage = `Are you sure you want to perform "${iconConfig.label}" action on group "${groupValue}" (${groupRows.length} rows)?`;
|
|
1214
1224
|
|
|
1215
1225
|
confirmDialog({
|
|
@@ -1310,7 +1320,10 @@ const DataGrid = forwardRef(
|
|
|
1310
1320
|
}
|
|
1311
1321
|
} else {
|
|
1312
1322
|
// Fallback to existing switch logic if no fetch config
|
|
1313
|
-
toast
|
|
1323
|
+
// Skip toast notification for edit actions
|
|
1324
|
+
if (iconConfig.id !== 'edit') {
|
|
1325
|
+
toast.info(`${iconConfig.label} - Group: ${groupValue}`);
|
|
1326
|
+
}
|
|
1314
1327
|
|
|
1315
1328
|
switch (iconConfig.id) {
|
|
1316
1329
|
case 'clock':
|
|
@@ -1329,9 +1342,28 @@ const DataGrid = forwardRef(
|
|
|
1329
1342
|
);
|
|
1330
1343
|
break;
|
|
1331
1344
|
case 'edit':
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1345
|
+
// Handle bulk edit action
|
|
1346
|
+
if (iconConfig.formModal) {
|
|
1347
|
+
// Get all rows in the group for bulk editing
|
|
1348
|
+
const groupRows = getGroupRows(groupValue);
|
|
1349
|
+
const groupRowIds = groupRows.map(row => row[form?.primaryKey || 'id']);
|
|
1350
|
+
|
|
1351
|
+
// Set bulk edit data
|
|
1352
|
+
setBulkEditMode(true);
|
|
1353
|
+
setBulkEditGroupData({
|
|
1354
|
+
groupValue: groupValue,
|
|
1355
|
+
groupRows: groupRows,
|
|
1356
|
+
groupRowIds: groupRowIds,
|
|
1357
|
+
iconConfig: iconConfig
|
|
1358
|
+
});
|
|
1359
|
+
|
|
1360
|
+
// Open modal for bulk editing
|
|
1361
|
+
modalOpen('update', null);
|
|
1362
|
+
} else {
|
|
1363
|
+
console.log(
|
|
1364
|
+
`Bulk edit action for group: ${groupValue}`
|
|
1365
|
+
);
|
|
1366
|
+
}
|
|
1335
1367
|
break;
|
|
1336
1368
|
case 'envelope':
|
|
1337
1369
|
console.log(
|
|
@@ -3621,6 +3653,8 @@ const DataGrid = forwardRef(
|
|
|
3621
3653
|
style={style}
|
|
3622
3654
|
updateForm={setFormData}
|
|
3623
3655
|
userProfile={userProfile}
|
|
3656
|
+
bulkEditMode={bulkEditMode}
|
|
3657
|
+
bulkEditGroupData={bulkEditGroupData}
|
|
3624
3658
|
/>
|
|
3625
3659
|
</div>
|
|
3626
3660
|
</div>
|
package/src/components/Form.jsx
CHANGED
|
@@ -80,6 +80,8 @@ function Form({
|
|
|
80
80
|
type,
|
|
81
81
|
updateForm,
|
|
82
82
|
userProfile,
|
|
83
|
+
bulkEditMode = false,
|
|
84
|
+
bulkEditGroupData = null,
|
|
83
85
|
}) {
|
|
84
86
|
const navigate = useNavigate();
|
|
85
87
|
const [fetchTrigger, setFetchTrigger] = useState(false);
|
|
@@ -888,34 +890,42 @@ function Form({
|
|
|
888
890
|
}
|
|
889
891
|
method = formSettings.customMethod;
|
|
890
892
|
} else {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
893
|
+
// Check if this is bulk edit mode
|
|
894
|
+
if (bulkEditMode && bulkEditGroupData?.iconConfig?.formModal) {
|
|
895
|
+
// Use bulk edit endpoint configuration
|
|
896
|
+
const formModalConfig = bulkEditGroupData.iconConfig.formModal;
|
|
897
|
+
url = formModalConfig.url || url;
|
|
898
|
+
method = formModalConfig.method || 'POST';
|
|
899
|
+
} else {
|
|
900
|
+
switch (formType) {
|
|
901
|
+
case 'create':
|
|
902
|
+
if (formSettings.json) {
|
|
903
|
+
url += '/json/store';
|
|
904
|
+
}
|
|
905
|
+
break;
|
|
906
|
+
case 'update':
|
|
907
|
+
case 'delete':
|
|
908
|
+
if (formSettings.json) {
|
|
909
|
+
url +=
|
|
910
|
+
formType === 'update'
|
|
911
|
+
? '/json/update'
|
|
912
|
+
: `/json/delete/${
|
|
913
|
+
formData[
|
|
914
|
+
formSettings
|
|
915
|
+
.primaryKey
|
|
916
|
+
]
|
|
917
|
+
}`;
|
|
918
|
+
} else {
|
|
919
|
+
url += `/${
|
|
920
|
+
formData[formSettings.primaryKey]
|
|
921
|
+
}`;
|
|
922
|
+
}
|
|
923
|
+
method =
|
|
901
924
|
formType === 'update'
|
|
902
|
-
? '
|
|
903
|
-
:
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
.primaryKey
|
|
907
|
-
]
|
|
908
|
-
}`;
|
|
909
|
-
} else {
|
|
910
|
-
url += `/${
|
|
911
|
-
formData[formSettings.primaryKey]
|
|
912
|
-
}`;
|
|
913
|
-
}
|
|
914
|
-
method =
|
|
915
|
-
formType === 'update'
|
|
916
|
-
? 'PUT'
|
|
917
|
-
: 'DELETE';
|
|
918
|
-
break;
|
|
925
|
+
? 'PUT'
|
|
926
|
+
: 'DELETE';
|
|
927
|
+
break;
|
|
928
|
+
}
|
|
919
929
|
}
|
|
920
930
|
}
|
|
921
931
|
|
|
@@ -1046,7 +1056,7 @@ function Form({
|
|
|
1046
1056
|
}
|
|
1047
1057
|
|
|
1048
1058
|
// Transform keys with dots into nested objects
|
|
1049
|
-
|
|
1059
|
+
let payload = Object.keys(updatedFormData).reduce(
|
|
1050
1060
|
(acc, key) => {
|
|
1051
1061
|
if (key.includes('.')) {
|
|
1052
1062
|
const [root, sub] = key.split('.');
|
|
@@ -1060,6 +1070,18 @@ function Form({
|
|
|
1060
1070
|
{}
|
|
1061
1071
|
);
|
|
1062
1072
|
|
|
1073
|
+
// Add bulk edit data to payload if in bulk edit mode
|
|
1074
|
+
if (bulkEditMode && bulkEditGroupData) {
|
|
1075
|
+
payload = {
|
|
1076
|
+
...payload,
|
|
1077
|
+
bulkEdit: true,
|
|
1078
|
+
groupValue: bulkEditGroupData.groupValue,
|
|
1079
|
+
groupKey: bulkEditGroupData.iconConfig.formModal?.groupKey || ajaxSetting?.groupBy?.[0],
|
|
1080
|
+
rowIds: bulkEditGroupData.groupRowIds,
|
|
1081
|
+
...bulkEditGroupData.iconConfig.formModal?.data
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1063
1085
|
const res = await CustomFetch(url, method, payload);
|
|
1064
1086
|
|
|
1065
1087
|
if (res.data.error === '') {
|
|
@@ -2233,7 +2255,12 @@ function Form({
|
|
|
2233
2255
|
<div className={styles.modalwrap}>
|
|
2234
2256
|
<div className={styles.modal}>
|
|
2235
2257
|
<div className={styles.modal__header}>
|
|
2236
|
-
<h1>
|
|
2258
|
+
<h1>
|
|
2259
|
+
{bulkEditMode && bulkEditGroupData
|
|
2260
|
+
? `${bulkEditGroupData.iconConfig.formModal?.title || 'Bulk Edit'} - ${bulkEditGroupData.groupValue} (${bulkEditGroupData.groupRows.length} items)`
|
|
2261
|
+
: formSettings[formType]?.title
|
|
2262
|
+
}
|
|
2263
|
+
</h1>
|
|
2237
2264
|
<button
|
|
2238
2265
|
className={styles.modal__close}
|
|
2239
2266
|
onClick={closeModal}
|
|
@@ -2265,6 +2292,14 @@ function Form({
|
|
|
2265
2292
|
) {
|
|
2266
2293
|
return null;
|
|
2267
2294
|
}
|
|
2295
|
+
// In bulk edit mode, only show fields specified in the formModal configuration
|
|
2296
|
+
if (
|
|
2297
|
+
bulkEditMode &&
|
|
2298
|
+
bulkEditGroupData?.iconConfig?.formModal?.fields &&
|
|
2299
|
+
!bulkEditGroupData.iconConfig.formModal.fields.includes(item.id)
|
|
2300
|
+
) {
|
|
2301
|
+
return null;
|
|
2302
|
+
}
|
|
2268
2303
|
|
|
2269
2304
|
return (
|
|
2270
2305
|
<Field
|