@visns-studio/visns-components 5.6.10 → 5.6.12
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 +476 -12
- package/package.json +1 -1
- package/src/components/crm/DataGrid.jsx +95 -10
package/README.md
CHANGED
|
@@ -191,12 +191,28 @@ A powerful data table component with sorting, filtering, and pagination.
|
|
|
191
191
|
params: {},
|
|
192
192
|
}}
|
|
193
193
|
columns={[
|
|
194
|
-
{
|
|
195
|
-
{
|
|
194
|
+
{ name: 'id', headerName: 'ID', width: 90 },
|
|
195
|
+
{ name: 'name', headerName: 'Name', width: 150 },
|
|
196
|
+
{
|
|
197
|
+
name: 'department',
|
|
198
|
+
headerName: 'Department',
|
|
199
|
+
width: 180,
|
|
200
|
+
groupSummaryReducer: 'count',
|
|
201
|
+
groupSummaryRenderer: (count) => `${count} employees`,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
name: 'salary',
|
|
205
|
+
headerName: 'Salary',
|
|
206
|
+
width: 120,
|
|
207
|
+
type: 'number',
|
|
208
|
+
groupSummaryReducer: 'sum',
|
|
209
|
+
groupSummaryRenderer: (sum) => `$${sum.toLocaleString()}`,
|
|
210
|
+
},
|
|
196
211
|
// More columns...
|
|
197
212
|
]}
|
|
198
213
|
form={formConfig}
|
|
199
214
|
gridHeight={500}
|
|
215
|
+
defaultGroupBy={[{ name: 'department' }]}
|
|
200
216
|
settings={{
|
|
201
217
|
pagination: true,
|
|
202
218
|
pageSize: 25,
|
|
@@ -214,16 +230,26 @@ A powerful data table component with sorting, filtering, and pagination.
|
|
|
214
230
|
|
|
215
231
|
#### DataGrid Props
|
|
216
232
|
|
|
217
|
-
| Prop
|
|
218
|
-
|
|
|
219
|
-
| ajaxSetting
|
|
220
|
-
| columns
|
|
221
|
-
|
|
|
222
|
-
|
|
|
223
|
-
|
|
|
224
|
-
|
|
|
225
|
-
|
|
|
226
|
-
|
|
|
233
|
+
| Prop | Description | Type | Default |
|
|
234
|
+
| --------------------- | ------------------------------------------ | -------- | ------- |
|
|
235
|
+
| ajaxSetting | Configuration for data fetching | Object | - |
|
|
236
|
+
| columns | Column definitions | Array | - |
|
|
237
|
+
| defaultExpandedNodes | Initial expanded nodes for TreeGrid | Object | - |
|
|
238
|
+
| defaultGroupBy | Initial grouping configuration | Array | - |
|
|
239
|
+
| expandedNodes | Current expanded nodes for TreeGrid | Object | - |
|
|
240
|
+
| form | Form configuration for editing | Object | - |
|
|
241
|
+
| gridHeight | Height of the grid | Number | 500 |
|
|
242
|
+
| groupBy | Current grouping configuration | Array | - |
|
|
243
|
+
| loadNode | Function to load TreeGrid nodes on demand | Function | - |
|
|
244
|
+
| onExpandedNodesChange | Callback for TreeGrid node expand/collapse | Function | - |
|
|
245
|
+
| onGroupByChange | Callback for grouping changes | Function | - |
|
|
246
|
+
| renderGroupTitle | Custom renderer for group headers | Function | - |
|
|
247
|
+
| settings | Grid settings (pagination, etc.) | Object | - |
|
|
248
|
+
| setFilterData | Callback for filter changes | Function | - |
|
|
249
|
+
| setConfig | Callback for configuration changes | Function | - |
|
|
250
|
+
| style | Custom styles | Object | - |
|
|
251
|
+
| treeColumn | Column name to display TreeGrid hierarchy | String | - |
|
|
252
|
+
| treeNestingSize | Indentation size for TreeGrid levels | Number | 20 |
|
|
227
253
|
|
|
228
254
|
#### Auto-Refresh Functionality
|
|
229
255
|
|
|
@@ -253,6 +279,444 @@ When auto-refresh is configured:
|
|
|
253
279
|
- Setting `autoRefresh: true` uses the default 30-second interval
|
|
254
280
|
- The component silently refreshes data without showing notifications
|
|
255
281
|
|
|
282
|
+
#### Group By Functionality
|
|
283
|
+
|
|
284
|
+
The DataGrid component supports grouping data by one or more columns, allowing you to organize and visualize data in hierarchical structures.
|
|
285
|
+
|
|
286
|
+
##### Using defaultGroupBy
|
|
287
|
+
|
|
288
|
+
The `defaultGroupBy` property allows you to specify initial grouping when the DataGrid loads:
|
|
289
|
+
|
|
290
|
+
```jsx
|
|
291
|
+
<DataGrid
|
|
292
|
+
// Other props...
|
|
293
|
+
defaultGroupBy={[
|
|
294
|
+
{ name: 'category' }, // Group by a single column
|
|
295
|
+
]}
|
|
296
|
+
/>
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
For more complex grouping with multiple levels:
|
|
300
|
+
|
|
301
|
+
```jsx
|
|
302
|
+
<DataGrid
|
|
303
|
+
// Other props...
|
|
304
|
+
defaultGroupBy={[
|
|
305
|
+
{ name: 'department' }, // First level grouping
|
|
306
|
+
{ name: 'role' }, // Second level grouping
|
|
307
|
+
]}
|
|
308
|
+
/>
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
##### Dynamic Grouping
|
|
312
|
+
|
|
313
|
+
You can also control grouping programmatically:
|
|
314
|
+
|
|
315
|
+
```jsx
|
|
316
|
+
const [groupBy, setGroupBy] = useState([{ name: 'category' }]);
|
|
317
|
+
|
|
318
|
+
// Later in your component
|
|
319
|
+
<DataGrid
|
|
320
|
+
// Other props...
|
|
321
|
+
groupBy={groupBy}
|
|
322
|
+
onGroupByChange={setGroupBy}
|
|
323
|
+
/>;
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
##### Server-Side Grouping
|
|
327
|
+
|
|
328
|
+
For server-side grouping, the DataGrid sends the groupBy information to your API endpoint:
|
|
329
|
+
|
|
330
|
+
```jsx
|
|
331
|
+
<DataGrid
|
|
332
|
+
ajaxSetting={{
|
|
333
|
+
url: '/api/data',
|
|
334
|
+
method: 'POST',
|
|
335
|
+
// Other settings...
|
|
336
|
+
}}
|
|
337
|
+
defaultGroupBy={[{ name: 'category' }]}
|
|
338
|
+
// Other props...
|
|
339
|
+
/>
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
Your server will receive a request with groupBy information:
|
|
343
|
+
|
|
344
|
+
```json
|
|
345
|
+
{
|
|
346
|
+
"page": 1,
|
|
347
|
+
"take": 25,
|
|
348
|
+
"groupBy": [{ "name": "category" }]
|
|
349
|
+
// Other parameters...
|
|
350
|
+
}
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
##### Group Summaries
|
|
354
|
+
|
|
355
|
+
You can add summary calculations to grouped data:
|
|
356
|
+
|
|
357
|
+
```jsx
|
|
358
|
+
<DataGrid
|
|
359
|
+
// Other props...
|
|
360
|
+
columns={[
|
|
361
|
+
{
|
|
362
|
+
name: 'amount',
|
|
363
|
+
headerName: 'Amount',
|
|
364
|
+
type: 'number',
|
|
365
|
+
groupSummaryReducer: 'sum', // Options: 'sum', 'avg', 'min', 'max', 'count'
|
|
366
|
+
groupSummaryRenderer: (summary) => `Total: $${summary.toFixed(2)}`,
|
|
367
|
+
},
|
|
368
|
+
// Other columns...
|
|
369
|
+
]}
|
|
370
|
+
defaultGroupBy={[{ name: 'category' }]}
|
|
371
|
+
/>
|
|
372
|
+
```
|
|
373
|
+
|
|
374
|
+
##### Customizing Group Rendering
|
|
375
|
+
|
|
376
|
+
You can customize how groups are displayed:
|
|
377
|
+
|
|
378
|
+
```jsx
|
|
379
|
+
<DataGrid
|
|
380
|
+
// Other props...
|
|
381
|
+
renderGroupTitle={(groupData) => (
|
|
382
|
+
<div style={{ fontWeight: 'bold', color: 'var(--primary-color)' }}>
|
|
383
|
+
{groupData.name}: {groupData.value} ({groupData.count} items)
|
|
384
|
+
</div>
|
|
385
|
+
)}
|
|
386
|
+
defaultGroupBy={[{ name: 'category' }]}
|
|
387
|
+
/>
|
|
388
|
+
```
|
|
389
|
+
|
|
390
|
+
##### Complete Example with Dynamic Grouping
|
|
391
|
+
|
|
392
|
+
Here's a complete example of a component that uses DataGrid with dynamic grouping controls:
|
|
393
|
+
|
|
394
|
+
```jsx
|
|
395
|
+
import React, { useState, useEffect } from 'react';
|
|
396
|
+
import { DataGrid } from 'visns-components';
|
|
397
|
+
|
|
398
|
+
const EmployeeDataGrid = () => {
|
|
399
|
+
// State for grouping
|
|
400
|
+
const [groupBy, setGroupBy] = useState([]);
|
|
401
|
+
|
|
402
|
+
// Sample data for the grid
|
|
403
|
+
const [employees, setEmployees] = useState([]);
|
|
404
|
+
|
|
405
|
+
// Fetch data on component mount
|
|
406
|
+
useEffect(() => {
|
|
407
|
+
const fetchEmployees = async () => {
|
|
408
|
+
try {
|
|
409
|
+
const response = await fetch('/api/employees');
|
|
410
|
+
const data = await response.json();
|
|
411
|
+
setEmployees(data);
|
|
412
|
+
} catch (error) {
|
|
413
|
+
console.error('Error fetching employees:', error);
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
|
|
417
|
+
fetchEmployees();
|
|
418
|
+
}, []);
|
|
419
|
+
|
|
420
|
+
// Column definitions
|
|
421
|
+
const columns = [
|
|
422
|
+
{
|
|
423
|
+
name: 'id',
|
|
424
|
+
headerName: 'ID',
|
|
425
|
+
width: 80,
|
|
426
|
+
},
|
|
427
|
+
{
|
|
428
|
+
name: 'name',
|
|
429
|
+
headerName: 'Name',
|
|
430
|
+
width: 180,
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
name: 'department',
|
|
434
|
+
headerName: 'Department',
|
|
435
|
+
width: 150,
|
|
436
|
+
groupSummaryReducer: 'count',
|
|
437
|
+
groupSummaryRenderer: (count) => `${count} employees`,
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
name: 'role',
|
|
441
|
+
headerName: 'Role',
|
|
442
|
+
width: 150,
|
|
443
|
+
},
|
|
444
|
+
{
|
|
445
|
+
name: 'salary',
|
|
446
|
+
headerName: 'Salary',
|
|
447
|
+
width: 120,
|
|
448
|
+
type: 'number',
|
|
449
|
+
groupSummaryReducer: 'sum',
|
|
450
|
+
groupSummaryRenderer: (sum) => `$${sum.toLocaleString()}`,
|
|
451
|
+
},
|
|
452
|
+
{
|
|
453
|
+
name: 'hireDate',
|
|
454
|
+
headerName: 'Hire Date',
|
|
455
|
+
width: 120,
|
|
456
|
+
type: 'date',
|
|
457
|
+
},
|
|
458
|
+
];
|
|
459
|
+
|
|
460
|
+
// Group by options for the dropdown
|
|
461
|
+
const groupByOptions = [
|
|
462
|
+
{ label: 'No Grouping', value: 'none' },
|
|
463
|
+
{ label: 'Department', value: 'department' },
|
|
464
|
+
{ label: 'Role', value: 'role' },
|
|
465
|
+
{ label: 'Department & Role', value: 'department-role' },
|
|
466
|
+
];
|
|
467
|
+
|
|
468
|
+
// Handle group by change
|
|
469
|
+
const handleGroupByChange = (e) => {
|
|
470
|
+
const value = e.target.value;
|
|
471
|
+
|
|
472
|
+
switch (value) {
|
|
473
|
+
case 'department':
|
|
474
|
+
setGroupBy([{ name: 'department' }]);
|
|
475
|
+
break;
|
|
476
|
+
case 'role':
|
|
477
|
+
setGroupBy([{ name: 'role' }]);
|
|
478
|
+
break;
|
|
479
|
+
case 'department-role':
|
|
480
|
+
setGroupBy([{ name: 'department' }, { name: 'role' }]);
|
|
481
|
+
break;
|
|
482
|
+
default:
|
|
483
|
+
setGroupBy([]);
|
|
484
|
+
break;
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
return (
|
|
489
|
+
<div>
|
|
490
|
+
<div style={{ marginBottom: '1rem' }}>
|
|
491
|
+
<label htmlFor="groupBy">Group By: </label>
|
|
492
|
+
<select
|
|
493
|
+
id="groupBy"
|
|
494
|
+
onChange={handleGroupByChange}
|
|
495
|
+
style={{ padding: '0.5rem', borderRadius: '4px' }}
|
|
496
|
+
>
|
|
497
|
+
{groupByOptions.map((option) => (
|
|
498
|
+
<option key={option.value} value={option.value}>
|
|
499
|
+
{option.label}
|
|
500
|
+
</option>
|
|
501
|
+
))}
|
|
502
|
+
</select>
|
|
503
|
+
</div>
|
|
504
|
+
|
|
505
|
+
<DataGrid
|
|
506
|
+
columns={columns}
|
|
507
|
+
dataSource={employees}
|
|
508
|
+
groupBy={groupBy}
|
|
509
|
+
onGroupByChange={setGroupBy}
|
|
510
|
+
gridHeight={600}
|
|
511
|
+
settings={{
|
|
512
|
+
pagination: true,
|
|
513
|
+
pageSize: 25,
|
|
514
|
+
}}
|
|
515
|
+
renderGroupTitle={(groupData) => (
|
|
516
|
+
<div
|
|
517
|
+
style={{
|
|
518
|
+
fontWeight: 'bold',
|
|
519
|
+
color: 'var(--primary-color)',
|
|
520
|
+
padding: '0.25rem 0',
|
|
521
|
+
}}
|
|
522
|
+
>
|
|
523
|
+
{groupData.name === 'department'
|
|
524
|
+
? 'Department'
|
|
525
|
+
: 'Role'}
|
|
526
|
+
:<span style={{ marginLeft: '0.5rem' }}>
|
|
527
|
+
{groupData.value} ({groupData.count} employees)
|
|
528
|
+
</span>
|
|
529
|
+
</div>
|
|
530
|
+
)}
|
|
531
|
+
/>
|
|
532
|
+
</div>
|
|
533
|
+
);
|
|
534
|
+
};
|
|
535
|
+
|
|
536
|
+
export default EmployeeDataGrid;
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
#### TreeGrid Functionality
|
|
540
|
+
|
|
541
|
+
The DataGrid component also supports hierarchical data display through its TreeGrid functionality. This allows you to represent parent-child relationships in your data with expandable/collapsible nodes.
|
|
542
|
+
|
|
543
|
+
##### Basic TreeGrid Configuration
|
|
544
|
+
|
|
545
|
+
To use the TreeGrid functionality:
|
|
546
|
+
|
|
547
|
+
```jsx
|
|
548
|
+
<DataGrid
|
|
549
|
+
// Other props...
|
|
550
|
+
treeColumn="name" // Specify which column will display the expand/collapse icons
|
|
551
|
+
dataSource={hierarchicalData} // Data with parent-child relationships
|
|
552
|
+
/>
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
Your data should have a structure where parent nodes contain child nodes:
|
|
556
|
+
|
|
557
|
+
```jsx
|
|
558
|
+
const hierarchicalData = [
|
|
559
|
+
{
|
|
560
|
+
id: 1,
|
|
561
|
+
name: 'Parent 1',
|
|
562
|
+
nodes: [
|
|
563
|
+
// Child nodes
|
|
564
|
+
{ id: 11, name: 'Child 1.1' },
|
|
565
|
+
{
|
|
566
|
+
id: 12,
|
|
567
|
+
name: 'Child 1.2',
|
|
568
|
+
nodes: [
|
|
569
|
+
// Nested children
|
|
570
|
+
{ id: 121, name: 'Grandchild 1.2.1' },
|
|
571
|
+
],
|
|
572
|
+
},
|
|
573
|
+
],
|
|
574
|
+
},
|
|
575
|
+
{
|
|
576
|
+
id: 2,
|
|
577
|
+
name: 'Parent 2',
|
|
578
|
+
nodes: [{ id: 21, name: 'Child 2.1' }],
|
|
579
|
+
},
|
|
580
|
+
];
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
##### Controlling Expanded Nodes
|
|
584
|
+
|
|
585
|
+
You can control which nodes are expanded:
|
|
586
|
+
|
|
587
|
+
```jsx
|
|
588
|
+
const [expandedNodes, setExpandedNodes] = useState({
|
|
589
|
+
1: true, // Node with ID 1 is expanded
|
|
590
|
+
'1/12': true, // Node with path 1/12 is expanded
|
|
591
|
+
});
|
|
592
|
+
|
|
593
|
+
<DataGrid
|
|
594
|
+
// Other props...
|
|
595
|
+
treeColumn="name"
|
|
596
|
+
dataSource={hierarchicalData}
|
|
597
|
+
expandedNodes={expandedNodes}
|
|
598
|
+
onExpandedNodesChange={setExpandedNodes}
|
|
599
|
+
/>;
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
##### Asynchronous Node Loading
|
|
603
|
+
|
|
604
|
+
For large datasets, you can load child nodes on demand:
|
|
605
|
+
|
|
606
|
+
```jsx
|
|
607
|
+
<DataGrid
|
|
608
|
+
// Other props...
|
|
609
|
+
treeColumn="name"
|
|
610
|
+
dataSource={asyncHierarchicalData}
|
|
611
|
+
loadNode={({ node }) => {
|
|
612
|
+
// Return a Promise that resolves to the child nodes
|
|
613
|
+
return fetchChildNodes(node.id);
|
|
614
|
+
}}
|
|
615
|
+
/>
|
|
616
|
+
```
|
|
617
|
+
|
|
618
|
+
For asynchronous loading, parent nodes should have `nodes: null` to indicate they have children that need to be loaded.
|
|
619
|
+
|
|
620
|
+
##### Complete TreeGrid Example
|
|
621
|
+
|
|
622
|
+
Here's a complete example of a component that uses DataGrid with TreeGrid functionality:
|
|
623
|
+
|
|
624
|
+
```jsx
|
|
625
|
+
import React, { useState } from 'react';
|
|
626
|
+
import { DataGrid } from 'visns-components';
|
|
627
|
+
|
|
628
|
+
const OrganizationTreeGrid = () => {
|
|
629
|
+
// Sample hierarchical data
|
|
630
|
+
const departments = [
|
|
631
|
+
{
|
|
632
|
+
id: 1,
|
|
633
|
+
name: 'Executive',
|
|
634
|
+
budget: 1500000,
|
|
635
|
+
nodes: [
|
|
636
|
+
{
|
|
637
|
+
id: 11,
|
|
638
|
+
name: 'Finance',
|
|
639
|
+
budget: 850000,
|
|
640
|
+
nodes: [
|
|
641
|
+
{ id: 111, name: 'Accounting', budget: 350000 },
|
|
642
|
+
{ id: 112, name: 'Investment', budget: 500000 },
|
|
643
|
+
],
|
|
644
|
+
},
|
|
645
|
+
{
|
|
646
|
+
id: 12,
|
|
647
|
+
name: 'Operations',
|
|
648
|
+
budget: 650000,
|
|
649
|
+
nodes: [
|
|
650
|
+
{ id: 121, name: 'HR', budget: 200000 },
|
|
651
|
+
{ id: 122, name: 'Facilities', budget: 450000 },
|
|
652
|
+
],
|
|
653
|
+
},
|
|
654
|
+
],
|
|
655
|
+
},
|
|
656
|
+
{
|
|
657
|
+
id: 2,
|
|
658
|
+
name: 'Sales & Marketing',
|
|
659
|
+
budget: 1200000,
|
|
660
|
+
nodes: [
|
|
661
|
+
{ id: 21, name: 'Sales', budget: 700000 },
|
|
662
|
+
{ id: 22, name: 'Marketing', budget: 500000 },
|
|
663
|
+
],
|
|
664
|
+
},
|
|
665
|
+
{
|
|
666
|
+
id: 3,
|
|
667
|
+
name: 'Technology',
|
|
668
|
+
budget: 2000000,
|
|
669
|
+
nodes: [
|
|
670
|
+
{ id: 31, name: 'Development', budget: 1200000 },
|
|
671
|
+
{ id: 32, name: 'IT Support', budget: 800000 },
|
|
672
|
+
],
|
|
673
|
+
},
|
|
674
|
+
];
|
|
675
|
+
|
|
676
|
+
// State for expanded nodes
|
|
677
|
+
const [expandedNodes, setExpandedNodes] = useState({
|
|
678
|
+
1: true, // Executive department is expanded by default
|
|
679
|
+
3: true, // Technology department is expanded by default
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
// Column definitions
|
|
683
|
+
const columns = [
|
|
684
|
+
{
|
|
685
|
+
name: 'name',
|
|
686
|
+
headerName: 'Department',
|
|
687
|
+
width: 250,
|
|
688
|
+
// This column will display the tree expand/collapse icons
|
|
689
|
+
},
|
|
690
|
+
{
|
|
691
|
+
name: 'budget',
|
|
692
|
+
headerName: 'Budget',
|
|
693
|
+
width: 150,
|
|
694
|
+
type: 'number',
|
|
695
|
+
render: ({ value }) => `$${value.toLocaleString()}`,
|
|
696
|
+
},
|
|
697
|
+
];
|
|
698
|
+
|
|
699
|
+
return (
|
|
700
|
+
<div>
|
|
701
|
+
<h2>Organization Structure</h2>
|
|
702
|
+
|
|
703
|
+
<DataGrid
|
|
704
|
+
columns={columns}
|
|
705
|
+
dataSource={departments}
|
|
706
|
+
treeColumn="name"
|
|
707
|
+
expandedNodes={expandedNodes}
|
|
708
|
+
onExpandedNodesChange={setExpandedNodes}
|
|
709
|
+
treeNestingSize={30} // Indentation size for nested levels
|
|
710
|
+
gridHeight={500}
|
|
711
|
+
idProperty="id"
|
|
712
|
+
/>
|
|
713
|
+
</div>
|
|
714
|
+
);
|
|
715
|
+
};
|
|
716
|
+
|
|
717
|
+
export default OrganizationTreeGrid;
|
|
718
|
+
```
|
|
719
|
+
|
|
256
720
|
### Form Components
|
|
257
721
|
|
|
258
722
|
#### Form
|
package/package.json
CHANGED
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"react-dom": "^17.0.0 || ^18.0.0"
|
|
85
85
|
},
|
|
86
86
|
"name": "@visns-studio/visns-components",
|
|
87
|
-
"version": "5.6.
|
|
87
|
+
"version": "5.6.12",
|
|
88
88
|
"description": "Various packages to assist in the development of our Custom Applications.",
|
|
89
89
|
"main": "src/index.js",
|
|
90
90
|
"files": [
|
|
@@ -935,6 +935,12 @@ const DataGrid = forwardRef(
|
|
|
935
935
|
const cellElement = event.target.closest(
|
|
936
936
|
'.InovuaReactDataGrid__cell'
|
|
937
937
|
);
|
|
938
|
+
|
|
939
|
+
// Add null check to prevent errors when cellElement is null
|
|
940
|
+
if (!cellElement) {
|
|
941
|
+
return; // Exit early if no cell element was found
|
|
942
|
+
}
|
|
943
|
+
|
|
938
944
|
const columnIndex = Array.from(
|
|
939
945
|
cellElement.parentNode.children
|
|
940
946
|
).indexOf(cellElement);
|
|
@@ -1042,6 +1048,12 @@ const DataGrid = forwardRef(
|
|
|
1042
1048
|
const cellElement = event.target.closest(
|
|
1043
1049
|
'.InovuaReactDataGrid__cell'
|
|
1044
1050
|
);
|
|
1051
|
+
|
|
1052
|
+
// Add null check to prevent errors when cellElement is null
|
|
1053
|
+
if (!cellElement) {
|
|
1054
|
+
return; // Exit early if no cell element was found
|
|
1055
|
+
}
|
|
1056
|
+
|
|
1045
1057
|
const columnIndex = Array.from(
|
|
1046
1058
|
cellElement.parentNode.children
|
|
1047
1059
|
).indexOf(cellElement);
|
|
@@ -2773,13 +2785,68 @@ const DataGrid = forwardRef(
|
|
|
2773
2785
|
e.preventDefault();
|
|
2774
2786
|
|
|
2775
2787
|
// Check if every required field in column.validate exists and is truthy in data
|
|
2776
|
-
|
|
2777
|
-
column.validate
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2788
|
+
if (
|
|
2789
|
+
column.validate &&
|
|
2790
|
+
column.validate
|
|
2791
|
+
.length > 0
|
|
2792
|
+
) {
|
|
2793
|
+
const missingFields =
|
|
2794
|
+
column.validate.filter(
|
|
2795
|
+
(v) =>
|
|
2796
|
+
!data[
|
|
2797
|
+
v
|
|
2798
|
+
]
|
|
2799
|
+
);
|
|
2781
2800
|
|
|
2782
|
-
|
|
2801
|
+
if (
|
|
2802
|
+
missingFields.length ===
|
|
2803
|
+
0
|
|
2804
|
+
) {
|
|
2805
|
+
handleTimer(
|
|
2806
|
+
data[
|
|
2807
|
+
form
|
|
2808
|
+
.primaryKey
|
|
2809
|
+
],
|
|
2810
|
+
column.id
|
|
2811
|
+
);
|
|
2812
|
+
} else {
|
|
2813
|
+
// Format field names to be more readable
|
|
2814
|
+
const formattedFields =
|
|
2815
|
+
missingFields.map(
|
|
2816
|
+
(
|
|
2817
|
+
field
|
|
2818
|
+
) => {
|
|
2819
|
+
// Try to find the column with this field ID to get its label
|
|
2820
|
+
const fieldColumn =
|
|
2821
|
+
columns.find(
|
|
2822
|
+
(
|
|
2823
|
+
col
|
|
2824
|
+
) =>
|
|
2825
|
+
col.id ===
|
|
2826
|
+
field
|
|
2827
|
+
);
|
|
2828
|
+
// Use the label if found, otherwise use the field ID
|
|
2829
|
+
return (
|
|
2830
|
+
fieldColumn?.label ||
|
|
2831
|
+
field
|
|
2832
|
+
);
|
|
2833
|
+
}
|
|
2834
|
+
);
|
|
2835
|
+
|
|
2836
|
+
const fieldList =
|
|
2837
|
+
formattedFields.join(
|
|
2838
|
+
', '
|
|
2839
|
+
);
|
|
2840
|
+
toast.error(
|
|
2841
|
+
`Please complete the following required field${
|
|
2842
|
+
missingFields.length >
|
|
2843
|
+
1
|
|
2844
|
+
? 's'
|
|
2845
|
+
: ''
|
|
2846
|
+
} before starting the timer: ${fieldList}`
|
|
2847
|
+
);
|
|
2848
|
+
}
|
|
2849
|
+
} else {
|
|
2783
2850
|
handleTimer(
|
|
2784
2851
|
data[
|
|
2785
2852
|
form
|
|
@@ -2787,10 +2854,6 @@ const DataGrid = forwardRef(
|
|
|
2787
2854
|
],
|
|
2788
2855
|
column.id
|
|
2789
2856
|
);
|
|
2790
|
-
} else {
|
|
2791
|
-
toast.error(
|
|
2792
|
-
'Please complete the required fields before starting the timer.'
|
|
2793
|
-
);
|
|
2794
2857
|
}
|
|
2795
2858
|
}}
|
|
2796
2859
|
>
|
|
@@ -3206,6 +3269,22 @@ const DataGrid = forwardRef(
|
|
|
3206
3269
|
};
|
|
3207
3270
|
}, []);
|
|
3208
3271
|
|
|
3272
|
+
// Reset state when ajaxSetting changes (e.g., when navigating to a new page)
|
|
3273
|
+
useEffect(() => {
|
|
3274
|
+
// Reset selected state
|
|
3275
|
+
setSelected({});
|
|
3276
|
+
|
|
3277
|
+
// Reset filter value if needed
|
|
3278
|
+
if (setFilterData) {
|
|
3279
|
+
setFilterData([]);
|
|
3280
|
+
}
|
|
3281
|
+
|
|
3282
|
+
// Force reload of data
|
|
3283
|
+
if (gridRef.current && gridRef.current.paginationProps) {
|
|
3284
|
+
gridRef.current.paginationProps.reload();
|
|
3285
|
+
}
|
|
3286
|
+
}, [ajaxSetting?.url]);
|
|
3287
|
+
|
|
3209
3288
|
// Auto-refresh effect
|
|
3210
3289
|
useEffect(() => {
|
|
3211
3290
|
// Clear any existing timer
|
|
@@ -3258,9 +3337,15 @@ const DataGrid = forwardRef(
|
|
|
3258
3337
|
<div className={styles.dataGridContainer}>
|
|
3259
3338
|
{limit && limit > 0 ? (
|
|
3260
3339
|
<ReactDataGrid
|
|
3340
|
+
key={`datagrid-${ajaxSetting?.url || ''}-${
|
|
3341
|
+
ajaxSetting?.groupBy ? 'grouped' : 'ungrouped'
|
|
3342
|
+
}`}
|
|
3261
3343
|
{...tableSetting}
|
|
3262
3344
|
columns={gridColumns}
|
|
3263
3345
|
dataSource={dataSource}
|
|
3346
|
+
{...(ajaxSetting && ajaxSetting.groupBy
|
|
3347
|
+
? { defaultGroupBy: ajaxSetting.groupBy }
|
|
3348
|
+
: {})}
|
|
3264
3349
|
filterValue={filterValue}
|
|
3265
3350
|
limit={limit}
|
|
3266
3351
|
enableColumnAutosize={false}
|