@topconsultnpm/sdkui-react 6.22.0-dev3.16 → 6.22.0-dev3.17
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/lib/components/base/TMDataGrid.js +219 -219
- package/lib/components/features/archive/TMArchive.js +7 -2
- package/lib/components/features/documents/TMCopyToFolderForm.js +21 -49
- package/lib/components/features/documents/TMMergeToPdfForm.js +1 -7
- package/lib/components/features/search/TMSearchResult.js +78 -78
- package/lib/components/forms/TMResultDialog.js +1 -1
- package/lib/hooks/useDcmtOperations.d.ts +0 -7
- package/lib/hooks/useDcmtOperations.js +39 -71
- package/package.json +1 -1
|
@@ -434,229 +434,229 @@ export default TMDataGrid;
|
|
|
434
434
|
// (padding, altezza minima, font e dimensione dei pulsanti di pagina).
|
|
435
435
|
// Con compactPager = false non emette nessuna regola: width/height restano inline come in precedenza,
|
|
436
436
|
// così il rendering è identico a quello del div originale.
|
|
437
|
-
const StyledDataGridContainer = styled.div `
|
|
438
|
-
${({ $compactPager }) => $compactPager && css `
|
|
439
|
-
& .dx-datagrid-pager {
|
|
440
|
-
padding: 2px 4px !important;
|
|
441
|
-
min-height: 0 !important;
|
|
442
|
-
font-size: 11px;
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
& .dx-datagrid-pager .dx-page,
|
|
446
|
-
& .dx-datagrid-pager .dx-page-size,
|
|
447
|
-
& .dx-datagrid-pager .dx-selection,
|
|
448
|
-
& .dx-datagrid-pager .dx-navigate-button {
|
|
449
|
-
min-width: 18px !important;
|
|
450
|
-
min-height: 18px !important;
|
|
451
|
-
height: auto !important;
|
|
452
|
-
padding: 1px 5px !important;
|
|
453
|
-
line-height: 16px !important;
|
|
454
|
-
font-size: 11px;
|
|
455
|
-
}
|
|
456
|
-
|
|
457
|
-
& .dx-datagrid-pager .dx-pages,
|
|
458
|
-
& .dx-datagrid-pager .dx-page-sizes,
|
|
459
|
-
& .dx-datagrid-pager .dx-info,
|
|
460
|
-
& .dx-datagrid-pager .dx-page-index,
|
|
461
|
-
& .dx-datagrid-pager .dx-pages .dx-info {
|
|
462
|
-
font-size: 11px;
|
|
463
|
-
margin-top: 0 !important;
|
|
464
|
-
margin-bottom: 0 !important;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
& .dx-datagrid-pager .dx-info {
|
|
468
|
-
margin-left: 4px !important;
|
|
469
|
-
margin-right: 4px !important;
|
|
470
|
-
}
|
|
471
|
-
|
|
472
|
-
/* Editor del numero di pagina (displayMode compact) */
|
|
473
|
-
& .dx-datagrid-pager .dx-page-index .dx-texteditor-input,
|
|
474
|
-
& .dx-datagrid-pager .dx-page-sizes .dx-texteditor-input {
|
|
475
|
-
min-height: 18px !important;
|
|
476
|
-
height: 18px !important;
|
|
477
|
-
padding: 0 4px !important;
|
|
478
|
-
font-size: 11px;
|
|
479
|
-
}
|
|
480
|
-
`}
|
|
437
|
+
const StyledDataGridContainer = styled.div `
|
|
438
|
+
${({ $compactPager }) => $compactPager && css `
|
|
439
|
+
& .dx-datagrid-pager {
|
|
440
|
+
padding: 2px 4px !important;
|
|
441
|
+
min-height: 0 !important;
|
|
442
|
+
font-size: 11px;
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
& .dx-datagrid-pager .dx-page,
|
|
446
|
+
& .dx-datagrid-pager .dx-page-size,
|
|
447
|
+
& .dx-datagrid-pager .dx-selection,
|
|
448
|
+
& .dx-datagrid-pager .dx-navigate-button {
|
|
449
|
+
min-width: 18px !important;
|
|
450
|
+
min-height: 18px !important;
|
|
451
|
+
height: auto !important;
|
|
452
|
+
padding: 1px 5px !important;
|
|
453
|
+
line-height: 16px !important;
|
|
454
|
+
font-size: 11px;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
& .dx-datagrid-pager .dx-pages,
|
|
458
|
+
& .dx-datagrid-pager .dx-page-sizes,
|
|
459
|
+
& .dx-datagrid-pager .dx-info,
|
|
460
|
+
& .dx-datagrid-pager .dx-page-index,
|
|
461
|
+
& .dx-datagrid-pager .dx-pages .dx-info {
|
|
462
|
+
font-size: 11px;
|
|
463
|
+
margin-top: 0 !important;
|
|
464
|
+
margin-bottom: 0 !important;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
& .dx-datagrid-pager .dx-info {
|
|
468
|
+
margin-left: 4px !important;
|
|
469
|
+
margin-right: 4px !important;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
/* Editor del numero di pagina (displayMode compact) */
|
|
473
|
+
& .dx-datagrid-pager .dx-page-index .dx-texteditor-input,
|
|
474
|
+
& .dx-datagrid-pager .dx-page-sizes .dx-texteditor-input {
|
|
475
|
+
min-height: 18px !important;
|
|
476
|
+
height: 18px !important;
|
|
477
|
+
padding: 0 4px !important;
|
|
478
|
+
font-size: 11px;
|
|
479
|
+
}
|
|
480
|
+
`}
|
|
481
481
|
`;
|
|
482
482
|
// Stile del pannello del filtro d'intestazione (ricerca, elenco valori, OK/Annulla).
|
|
483
483
|
// Il popup è in portale sul body, fuori da StyledDataGridContainer: le regole sono globali
|
|
484
484
|
// ma tutte sotto .dx-header-filter-menu, classe che DevExtreme applica solo a questo popup.
|
|
485
|
-
const HeaderFilterMenuGlobalStyle = createGlobalStyle `
|
|
486
|
-
.dx-header-filter-menu {
|
|
487
|
-
|
|
488
|
-
&.dx-popup-wrapper > .dx-overlay-content.dx-popup-normal {
|
|
489
|
-
border: 1px solid #E4E8EE;
|
|
490
|
-
border-radius: 8px;
|
|
491
|
-
box-shadow: 0 2px 6px rgba(18, 44, 76, .08), 0 12px 28px rgba(18, 44, 76, .14);
|
|
492
|
-
overflow: hidden;
|
|
493
|
-
}
|
|
494
|
-
|
|
495
|
-
&.dx-popup-wrapper .dx-popup-content {
|
|
496
|
-
padding: 10px 10px 4px;
|
|
497
|
-
}
|
|
498
|
-
|
|
499
|
-
/* Campo di ricerca: TextBox mode "search" resa da List o da TreeView (filtri gerarchici) */
|
|
500
|
-
.dx-searchbox.dx-list-search,
|
|
501
|
-
.dx-searchbox.dx-treeview-search {
|
|
502
|
-
margin-bottom: 8px;
|
|
503
|
-
border-radius: 6px;
|
|
504
|
-
border: 1px solid #CDD2D8;
|
|
505
|
-
background-color: #F4F6F9;
|
|
506
|
-
box-shadow: none;
|
|
507
|
-
transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
|
|
508
|
-
|
|
509
|
-
/* fluent disegna qui la barretta di focus, sostituita da bordo + alone */
|
|
510
|
-
&::before,
|
|
511
|
-
&::after {
|
|
512
|
-
display: none;
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
&.dx-state-hover:not(.dx-state-focused) {
|
|
516
|
-
border-color: #AEB6C1;
|
|
517
|
-
background-color: #EEF2F7;
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
&.dx-state-focused {
|
|
521
|
-
border-color: #2559A5;
|
|
522
|
-
background-color: #FFFFFF;
|
|
523
|
-
box-shadow: 0 0 0 2px rgba(37, 89, 165, .16);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
.dx-icon-search {
|
|
527
|
-
width: 28px;
|
|
528
|
-
color: #8A929B;
|
|
529
|
-
transition: color .15s ease;
|
|
530
|
-
}
|
|
531
|
-
|
|
532
|
-
&.dx-state-focused .dx-icon-search {
|
|
533
|
-
color: #2559A5;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
.dx-texteditor-input {
|
|
537
|
-
min-height: 28px;
|
|
538
|
-
padding-inline-start: 28px;
|
|
539
|
-
padding-inline-end: 8px;
|
|
540
|
-
font-size: 12px;
|
|
541
|
-
background-color: transparent;
|
|
542
|
-
}
|
|
543
|
-
|
|
544
|
-
.dx-placeholder {
|
|
545
|
-
color: #9AA3AD;
|
|
546
|
-
font-size: 12px;
|
|
547
|
-
|
|
548
|
-
&::before {
|
|
549
|
-
padding-inline-start: 28px;
|
|
550
|
-
padding-inline-end: 8px;
|
|
551
|
-
}
|
|
552
|
-
}
|
|
553
|
-
|
|
554
|
-
.dx-clear-button-area .dx-icon-clear {
|
|
555
|
-
color: #8A929B;
|
|
556
|
-
transition: color .15s ease;
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
.dx-clear-button-area:hover .dx-icon-clear {
|
|
560
|
-
color: #2559A5;
|
|
561
|
-
}
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
.dx-list-item {
|
|
565
|
-
border-radius: 4px;
|
|
566
|
-
transition: background-color .12s ease;
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
.dx-list-item-content {
|
|
570
|
-
padding: 4px 6px;
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
.dx-list-item.dx-state-hover,
|
|
574
|
-
.dx-list-item.dx-state-focused {
|
|
575
|
-
background-color: #F1F6FD;
|
|
576
|
-
color: #122C4C;
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
.dx-list-select-all {
|
|
580
|
-
padding: 2px 0 6px;
|
|
581
|
-
margin-bottom: 4px;
|
|
582
|
-
border-bottom: 1px solid #ECEFF3;
|
|
583
|
-
}
|
|
584
|
-
|
|
585
|
-
.dx-list-select-all-label {
|
|
586
|
-
font-weight: 600;
|
|
587
|
-
color: #122C4C;
|
|
588
|
-
}
|
|
589
|
-
|
|
590
|
-
.dx-checkbox-checked .dx-checkbox-icon,
|
|
591
|
-
.dx-checkbox-indeterminate .dx-checkbox-icon {
|
|
592
|
-
border-color: #2559A5;
|
|
593
|
-
background-color: #2559A5;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
.dx-checkbox.dx-state-hover:not(.dx-checkbox-checked) .dx-checkbox-icon {
|
|
597
|
-
border-color: #2559A5;
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
/* il :not() lascia intatto il fade-out nativo della scrollbar */
|
|
601
|
-
.dx-scrollable-scroll:not(.dx-state-invisible) .dx-scrollable-scroll-content {
|
|
602
|
-
background-color: rgba(18, 44, 76, .28);
|
|
603
|
-
border-radius: 3px;
|
|
604
|
-
}
|
|
605
|
-
|
|
606
|
-
&.dx-popup-wrapper .dx-popup-bottom.dx-toolbar {
|
|
607
|
-
padding: 8px 10px 10px;
|
|
608
|
-
border-top: 1px solid #ECEFF3;
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
.dx-popup-bottom .dx-button {
|
|
612
|
-
border-radius: 5px;
|
|
613
|
-
min-width: 76px;
|
|
614
|
-
margin: 0;
|
|
615
|
-
}
|
|
616
|
-
|
|
617
|
-
.dx-popup-bottom .dx-toolbar-button + .dx-toolbar-button {
|
|
618
|
-
margin-inline-start: 6px;
|
|
619
|
-
|
|
620
|
-
.dx-button {
|
|
621
|
-
margin: 0;
|
|
622
|
-
}
|
|
623
|
-
}
|
|
624
|
-
|
|
625
|
-
/* DevExtreme aggiunge prima OK e poi Annulla: individuarli per posizione li rende indipendenti dalla lingua */
|
|
626
|
-
.dx-popup-bottom .dx-toolbar-after > .dx-toolbar-button:first-child .dx-button {
|
|
627
|
-
border-color: transparent;
|
|
628
|
-
background-color: #2559A5;
|
|
629
|
-
|
|
630
|
-
.dx-button-text {
|
|
631
|
-
color: #FFFFFF;
|
|
632
|
-
}
|
|
633
|
-
|
|
634
|
-
&.dx-state-hover {
|
|
635
|
-
background-color: #1E4A8C;
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
&.dx-state-active {
|
|
639
|
-
background-color: #1A3F78;
|
|
640
|
-
}
|
|
641
|
-
}
|
|
642
|
-
|
|
643
|
-
.dx-popup-bottom .dx-toolbar-after > .dx-toolbar-button + .dx-toolbar-button .dx-button {
|
|
644
|
-
border-color: transparent;
|
|
645
|
-
background-color: transparent;
|
|
646
|
-
|
|
647
|
-
.dx-button-text {
|
|
648
|
-
color: #44506B;
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
&.dx-state-hover {
|
|
652
|
-
background-color: #F0F3F7;
|
|
653
|
-
}
|
|
654
|
-
|
|
655
|
-
&.dx-state-active {
|
|
656
|
-
background-color: #E6EBF2;
|
|
657
|
-
}
|
|
658
|
-
}
|
|
659
|
-
}
|
|
485
|
+
const HeaderFilterMenuGlobalStyle = createGlobalStyle `
|
|
486
|
+
.dx-header-filter-menu {
|
|
487
|
+
|
|
488
|
+
&.dx-popup-wrapper > .dx-overlay-content.dx-popup-normal {
|
|
489
|
+
border: 1px solid #E4E8EE;
|
|
490
|
+
border-radius: 8px;
|
|
491
|
+
box-shadow: 0 2px 6px rgba(18, 44, 76, .08), 0 12px 28px rgba(18, 44, 76, .14);
|
|
492
|
+
overflow: hidden;
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
&.dx-popup-wrapper .dx-popup-content {
|
|
496
|
+
padding: 10px 10px 4px;
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
/* Campo di ricerca: TextBox mode "search" resa da List o da TreeView (filtri gerarchici) */
|
|
500
|
+
.dx-searchbox.dx-list-search,
|
|
501
|
+
.dx-searchbox.dx-treeview-search {
|
|
502
|
+
margin-bottom: 8px;
|
|
503
|
+
border-radius: 6px;
|
|
504
|
+
border: 1px solid #CDD2D8;
|
|
505
|
+
background-color: #F4F6F9;
|
|
506
|
+
box-shadow: none;
|
|
507
|
+
transition: border-color .15s ease, background-color .15s ease, box-shadow .15s ease;
|
|
508
|
+
|
|
509
|
+
/* fluent disegna qui la barretta di focus, sostituita da bordo + alone */
|
|
510
|
+
&::before,
|
|
511
|
+
&::after {
|
|
512
|
+
display: none;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
&.dx-state-hover:not(.dx-state-focused) {
|
|
516
|
+
border-color: #AEB6C1;
|
|
517
|
+
background-color: #EEF2F7;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
&.dx-state-focused {
|
|
521
|
+
border-color: #2559A5;
|
|
522
|
+
background-color: #FFFFFF;
|
|
523
|
+
box-shadow: 0 0 0 2px rgba(37, 89, 165, .16);
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.dx-icon-search {
|
|
527
|
+
width: 28px;
|
|
528
|
+
color: #8A929B;
|
|
529
|
+
transition: color .15s ease;
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
&.dx-state-focused .dx-icon-search {
|
|
533
|
+
color: #2559A5;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.dx-texteditor-input {
|
|
537
|
+
min-height: 28px;
|
|
538
|
+
padding-inline-start: 28px;
|
|
539
|
+
padding-inline-end: 8px;
|
|
540
|
+
font-size: 12px;
|
|
541
|
+
background-color: transparent;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.dx-placeholder {
|
|
545
|
+
color: #9AA3AD;
|
|
546
|
+
font-size: 12px;
|
|
547
|
+
|
|
548
|
+
&::before {
|
|
549
|
+
padding-inline-start: 28px;
|
|
550
|
+
padding-inline-end: 8px;
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
|
|
554
|
+
.dx-clear-button-area .dx-icon-clear {
|
|
555
|
+
color: #8A929B;
|
|
556
|
+
transition: color .15s ease;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
.dx-clear-button-area:hover .dx-icon-clear {
|
|
560
|
+
color: #2559A5;
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
.dx-list-item {
|
|
565
|
+
border-radius: 4px;
|
|
566
|
+
transition: background-color .12s ease;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
.dx-list-item-content {
|
|
570
|
+
padding: 4px 6px;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.dx-list-item.dx-state-hover,
|
|
574
|
+
.dx-list-item.dx-state-focused {
|
|
575
|
+
background-color: #F1F6FD;
|
|
576
|
+
color: #122C4C;
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
.dx-list-select-all {
|
|
580
|
+
padding: 2px 0 6px;
|
|
581
|
+
margin-bottom: 4px;
|
|
582
|
+
border-bottom: 1px solid #ECEFF3;
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
.dx-list-select-all-label {
|
|
586
|
+
font-weight: 600;
|
|
587
|
+
color: #122C4C;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
.dx-checkbox-checked .dx-checkbox-icon,
|
|
591
|
+
.dx-checkbox-indeterminate .dx-checkbox-icon {
|
|
592
|
+
border-color: #2559A5;
|
|
593
|
+
background-color: #2559A5;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
.dx-checkbox.dx-state-hover:not(.dx-checkbox-checked) .dx-checkbox-icon {
|
|
597
|
+
border-color: #2559A5;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
/* il :not() lascia intatto il fade-out nativo della scrollbar */
|
|
601
|
+
.dx-scrollable-scroll:not(.dx-state-invisible) .dx-scrollable-scroll-content {
|
|
602
|
+
background-color: rgba(18, 44, 76, .28);
|
|
603
|
+
border-radius: 3px;
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
&.dx-popup-wrapper .dx-popup-bottom.dx-toolbar {
|
|
607
|
+
padding: 8px 10px 10px;
|
|
608
|
+
border-top: 1px solid #ECEFF3;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.dx-popup-bottom .dx-button {
|
|
612
|
+
border-radius: 5px;
|
|
613
|
+
min-width: 76px;
|
|
614
|
+
margin: 0;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
.dx-popup-bottom .dx-toolbar-button + .dx-toolbar-button {
|
|
618
|
+
margin-inline-start: 6px;
|
|
619
|
+
|
|
620
|
+
.dx-button {
|
|
621
|
+
margin: 0;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
/* DevExtreme aggiunge prima OK e poi Annulla: individuarli per posizione li rende indipendenti dalla lingua */
|
|
626
|
+
.dx-popup-bottom .dx-toolbar-after > .dx-toolbar-button:first-child .dx-button {
|
|
627
|
+
border-color: transparent;
|
|
628
|
+
background-color: #2559A5;
|
|
629
|
+
|
|
630
|
+
.dx-button-text {
|
|
631
|
+
color: #FFFFFF;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
&.dx-state-hover {
|
|
635
|
+
background-color: #1E4A8C;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
&.dx-state-active {
|
|
639
|
+
background-color: #1A3F78;
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
.dx-popup-bottom .dx-toolbar-after > .dx-toolbar-button + .dx-toolbar-button .dx-button {
|
|
644
|
+
border-color: transparent;
|
|
645
|
+
background-color: transparent;
|
|
646
|
+
|
|
647
|
+
.dx-button-text {
|
|
648
|
+
color: #44506B;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
&.dx-state-hover {
|
|
652
|
+
background-color: #F0F3F7;
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
&.dx-state-active {
|
|
656
|
+
background-color: #E6EBF2;
|
|
657
|
+
}
|
|
658
|
+
}
|
|
659
|
+
}
|
|
660
660
|
`;
|
|
661
661
|
const getRecordCount = (dataSource) => {
|
|
662
662
|
if (Array.isArray(dataSource)) {
|
|
@@ -22,12 +22,17 @@ const TMArchive = ({ onDcmtTypeSelect = undefined, inputTID, inputFile = null, c
|
|
|
22
22
|
const pendingMidsRef = React.useRef(null);
|
|
23
23
|
const deviceType = useDeviceType();
|
|
24
24
|
useEffect(() => { setMruTIDs(SDKUI_Globals.userSettings.archivingSettings.mruTIDs); }, []);
|
|
25
|
+
// Applica inputTID solo quando cambia davvero: non deve sovrascrivere il tipo documento
|
|
26
|
+
// selezionato dall'utente.
|
|
25
27
|
useEffect(() => {
|
|
26
28
|
if (!inputTID)
|
|
27
29
|
return;
|
|
28
30
|
setCurrentTID(inputTID);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
+
}, [inputTID]);
|
|
32
|
+
// Evidenzia la scorciatoia del tipo documento corrente, anche quando mruTIDs cambia.
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
setCurrentMruTID(currentTID && mruTIDs.includes(currentTID) ? currentTID : 0);
|
|
35
|
+
}, [currentTID, mruTIDs]);
|
|
31
36
|
useEffect(() => {
|
|
32
37
|
pendingMidsRef.current = inputMids;
|
|
33
38
|
if (currentTID && currentTID > 0 && inputMids && inputMids.length > 0) {
|
|
@@ -20,12 +20,6 @@ import ShowAlert from '../../base/TMAlert';
|
|
|
20
20
|
import { TMColors } from '../../../utils/theme';
|
|
21
21
|
import TMDownloadRelationViewerSection from './TMDownloadRelationViewerSection';
|
|
22
22
|
import { buildInitialDownloadSettings, fileExists, generateTargetFileName, generateUniqueFileName, getDcmtInfosToDownload, getFloatingLabelStyle, isDirectoryPickerSupported, } from './copyAndMergeDcmtsShared';
|
|
23
|
-
/**
|
|
24
|
-
* Numero di documenti scaricati in parallelo.
|
|
25
|
-
* I file vengono consumati via callback (scrittura su cartella o raccolta per lo ZIP),
|
|
26
|
-
* quindi il parallelismo è sicuro; l'ordine finale è garantito a valle del download.
|
|
27
|
-
*/
|
|
28
|
-
const COPY_DOWNLOAD_CONCURRENCY = 5;
|
|
29
23
|
/**
|
|
30
24
|
* Form per la copia/zip dei documenti in una cartella locale.
|
|
31
25
|
* Logica condivisa con TMMergeToPdfForm è esternalizzata in copyAndMergeDcmtsShared
|
|
@@ -117,23 +111,9 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
117
111
|
const handleSeparatorChange = (e) => {
|
|
118
112
|
updateSettings('separatorChar', e.target.value.slice(0, 1));
|
|
119
113
|
};
|
|
120
|
-
// ---- Serializzazione delle scritture su filesystem ----
|
|
121
|
-
// I download sono paralleli, le scritture no: fileExists/generateUniqueFileName/
|
|
122
|
-
// getFileHandle sono check-then-act sulla stessa cartella e due scritture contemporanee
|
|
123
|
-
// potrebbero sovrascriversi o calcolare lo stesso nome "univoco".
|
|
124
|
-
const writeChainRef = useRef(Promise.resolve());
|
|
125
|
-
const runExclusive = (action) => {
|
|
126
|
-
const result = writeChainRef.current.then(action);
|
|
127
|
-
// La catena non deve interrompersi se una scrittura fallisce: l'errore viene
|
|
128
|
-
// comunque propagato al chiamante tramite il promise restituito.
|
|
129
|
-
writeChainRef.current = result.catch(() => undefined);
|
|
130
|
-
return result;
|
|
131
|
-
};
|
|
132
114
|
// ---- Scrittura file nel filesystem locale ----
|
|
133
115
|
const writeFileToFolder = async (file, dcmtInfo) => {
|
|
134
116
|
try {
|
|
135
|
-
// Fuori dal lock: con la denominazione da metadati questa fa una chiamata al
|
|
136
|
-
// server per documento e deve poter andare in parallelo agli altri download.
|
|
137
117
|
const baseFileName = await generateTargetFileName(file, dcmtInfo, {
|
|
138
118
|
fileNamingMode: settings.fileNamingMode,
|
|
139
119
|
separatorChar: settings.separatorChar,
|
|
@@ -152,29 +132,25 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
152
132
|
URL.revokeObjectURL(url);
|
|
153
133
|
return;
|
|
154
134
|
}
|
|
155
|
-
// Dentro il lock: controllo esistenza, calcolo del nome univoco e scrittura
|
|
156
|
-
// devono restare un'unica operazione atomica sulla cartella.
|
|
157
135
|
const folderHandle = folderHandleRef.current;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
break;
|
|
171
|
-
}
|
|
136
|
+
let targetFileName = baseFileName;
|
|
137
|
+
const exists = await fileExists(folderHandle, targetFileName);
|
|
138
|
+
if (exists) {
|
|
139
|
+
switch (settings.fileExistsMode) {
|
|
140
|
+
case 'skip':
|
|
141
|
+
return;
|
|
142
|
+
case 'rename':
|
|
143
|
+
targetFileName = await generateUniqueFileName(folderHandle, targetFileName);
|
|
144
|
+
break;
|
|
145
|
+
case 'overwrite':
|
|
146
|
+
default:
|
|
147
|
+
break;
|
|
172
148
|
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
149
|
+
}
|
|
150
|
+
const fileHandle = await folderHandle.getFileHandle(targetFileName, { create: true });
|
|
151
|
+
const writable = await fileHandle.createWritable();
|
|
152
|
+
await writable.write(file);
|
|
153
|
+
await writable.close();
|
|
178
154
|
}
|
|
179
155
|
catch (err) {
|
|
180
156
|
console.error('Errore nella scrittura del file:', err);
|
|
@@ -202,10 +178,7 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
202
178
|
retrieveOptions.invoiceRetrieveFormat = settings.invoiceFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
|
|
203
179
|
retrieveOptions.orderRetrieveFormat = settings.orderFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
|
|
204
180
|
if (settings.exportMode === 'copy') {
|
|
205
|
-
|
|
206
|
-
// in quel caso si resta sequenziali, perché il browser blocca i click ravvicinati.
|
|
207
|
-
const copyConcurrency = folderHandleRef.current ? COPY_DOWNLOAD_CONCURRENCY : 1;
|
|
208
|
-
await downloadDcmtsAsync({ inputDcmts: dcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: writeFileToFolder, skipConfirmation: true, retrieveOptions, useCache: false, downloadConcurrency: copyConcurrency });
|
|
181
|
+
await downloadDcmtsAsync({ inputDcmts: dcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: writeFileToFolder, skipConfirmation: true, retrieveOptions, useCache: false });
|
|
209
182
|
}
|
|
210
183
|
else if (settings.exportMode === 'zip') {
|
|
211
184
|
const fileNameCounts = new Map();
|
|
@@ -220,9 +193,8 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
220
193
|
const ext = dotIndex > 0 ? fileName.substring(dotIndex) : '';
|
|
221
194
|
return `${baseName} (${count})${ext}`;
|
|
222
195
|
};
|
|
223
|
-
//
|
|
224
|
-
//
|
|
225
|
-
// nell'ordine di selezione, così i suffissi di deduplica restano quelli sequenziali.
|
|
196
|
+
// I file scaricati si raccolgono in mappa (nome incluso) e l'archivio viene costruito
|
|
197
|
+
// dopo, nell'ordine di selezione, così i suffissi di deduplica restano coerenti.
|
|
226
198
|
const downloadedEntries = new Map();
|
|
227
199
|
const collectFileForZip = async (file, dcmtInfo) => {
|
|
228
200
|
const targetFileName = await generateTargetFileName(file, dcmtInfo, {
|
|
@@ -231,7 +203,7 @@ const TMCopyToFolderForm = ({ mode, selectedDcmtInfos, onClose, showTMRelationVi
|
|
|
231
203
|
});
|
|
232
204
|
downloadedEntries.set(`${dcmtInfo.TID}_${dcmtInfo.DID}`, { file, targetFileName });
|
|
233
205
|
};
|
|
234
|
-
await downloadDcmtsAsync({ inputDcmts: dcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForZip, skipConfirmation: true, retrieveOptions, useCache: false
|
|
206
|
+
await downloadDcmtsAsync({ inputDcmts: dcmtInfosToDownload, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForZip, skipConfirmation: true, retrieveOptions, useCache: false });
|
|
235
207
|
const zipEntries = [];
|
|
236
208
|
for (const dcmtInfo of dcmtInfosToDownload) {
|
|
237
209
|
const entry = downloadedEntries.get(`${dcmtInfo.TID}_${dcmtInfo.DID}`);
|
|
@@ -18,12 +18,6 @@ import ShowAlert from '../../base/TMAlert';
|
|
|
18
18
|
import TMTooltip from '../../base/TMTooltip';
|
|
19
19
|
import MergePdfManager from '../../../helper/MergePdfManager';
|
|
20
20
|
import { TMResultManager } from '../../forms/TMResultDialog';
|
|
21
|
-
/**
|
|
22
|
-
* Numero di documenti scaricati in parallelo durante l'unione.
|
|
23
|
-
* I file vengono consumati via callback (nessun click su <a download>), quindi il
|
|
24
|
-
* parallelismo è sicuro; il riordino finale è garantito da pdfFilesMap.
|
|
25
|
-
*/
|
|
26
|
-
const MERGE_DOWNLOAD_CONCURRENCY = 5;
|
|
27
21
|
/**
|
|
28
22
|
* Converte il formato di visualizzazione fatture impostato dall'utente nel corrispondente formato PDF:
|
|
29
23
|
* il foglio di stile SDI resta SDI, in tutti gli altri casi si usa ASW.
|
|
@@ -206,7 +200,7 @@ const TMMergeToPdfForm = (props) => {
|
|
|
206
200
|
const collectFileForMerge = async (file, dcmtInfo) => {
|
|
207
201
|
pdfFilesMap.set(`${dcmtInfo.TID}_${dcmtInfo.DID}`, file);
|
|
208
202
|
};
|
|
209
|
-
await downloadDcmtsAsync({ inputDcmts: dcmtsWithFile, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForMerge, skipConfirmation: true, retrieveOptions: rfo, useCache: false
|
|
203
|
+
await downloadDcmtsAsync({ inputDcmts: dcmtsWithFile, downloadType: DownloadTypes.Dcmt, downloadMode: 'download', onFileDownloaded: collectFileForMerge, skipConfirmation: true, retrieveOptions: rfo, useCache: false });
|
|
210
204
|
}
|
|
211
205
|
// Ricostruisci l'array pdfFiles nell'ordine originale di selezione
|
|
212
206
|
const pdfFiles = [];
|
|
@@ -1518,9 +1518,9 @@ const TMSearchResultGrid = ({ openInOffice, fromDTD, operationItems, allUsers, i
|
|
|
1518
1518
|
return _jsxs("div", { style: { width: "100%", height: "100%" }, children: [!isDataGridReady && (_jsxs("div", { style: { display: 'flex', flexDirection: 'column', alignItems: 'center', justifyContent: 'center', height: '100%', width: '100%', gap: '10px' }, children: [_jsx(LoadIndicator, { height: 60, width: 60 }), _jsx("div", { children: SDKUI_Localizator.Loading })] })), isDataGridReady && _jsx(TMDataGrid, { ref: dataGridRef, id: "tm-search-result", keyExpr: "rowIndex", dataColumns: dataColumns, dataSource: dataSource, customizeColumns: customizeColumns, repaintChangesOnly: true, selectedRowKeys: selectedRowKeys, focusedRowKey: disableAutoFocus ? undefined : Number(focusedItem?.rowIndex ?? 0), showSearchPanel: showSearchTMDatagrid, showFilterPanel: true, sorting: gridSorting, selection: gridSelection, scrolling: gridScrolling, pageSize: pageSize, compactPager: compactPager, onSelectionChanged: handleSelectionChange, onFocusedRowChanged: handleFocusedRowChange, onRowDblClick: onRowDblClick, onContentReady: onContentReady, showHeaderColumnChooser: true, onKeyDown: onKeyDown, customContextMenuItems: operationItems, counterConfig: gridCounterConfig })] });
|
|
1519
1519
|
};
|
|
1520
1520
|
//#region TMSearchResultSelector
|
|
1521
|
-
const StyledItemTemplate = styled.div `
|
|
1522
|
-
background: ${(props) => props.$isSelected ? 'oklch(from var(--dx-color-primary) l c h / .2) !important' : 'transparent'};
|
|
1523
|
-
cursor: pointer;
|
|
1521
|
+
const StyledItemTemplate = styled.div `
|
|
1522
|
+
background: ${(props) => props.$isSelected ? 'oklch(from var(--dx-color-primary) l c h / .2) !important' : 'transparent'};
|
|
1523
|
+
cursor: pointer;
|
|
1524
1524
|
`;
|
|
1525
1525
|
const MemoizedStyledItemTemplate = React.memo(StyledItemTemplate);
|
|
1526
1526
|
const TMSearchResultSelector = ({ searchResults = [], disableAccordionIfSingleCategory = false, selectedTID, selectedSearchResult, autoSelectFirst = true, onSelectionChanged }) => {
|
|
@@ -1727,85 +1727,85 @@ const TMDcmtPreviewWrapper = ({ refreshPreviewTrigger, currentDcmt, isVisible, i
|
|
|
1727
1727
|
return (_jsx(TMDcmtPreview, { dcmtData: currentDcmt, onClosePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => setPanelVisibilityById('tmDcmtPreview', false) : undefined, onBack: onBack, allowMaximize: !isMobile && countVisibleLeafPanels() > 1, onMaximizePanel: (!isMobile && countVisibleLeafPanels() > 1) ? () => toggleMaximize("tmDcmtPreview") : undefined, isResizingActive: isResizingActive, isVisible: isVisible && isPageVisible }, refreshPreviewTrigger));
|
|
1728
1728
|
};
|
|
1729
1729
|
// Styled Components
|
|
1730
|
-
const StyledPlaceholder = styled.div `
|
|
1731
|
-
padding: 20px;
|
|
1732
|
-
text-align: center;
|
|
1733
|
-
color: #888;
|
|
1730
|
+
const StyledPlaceholder = styled.div `
|
|
1731
|
+
padding: 20px;
|
|
1732
|
+
text-align: center;
|
|
1733
|
+
color: #888;
|
|
1734
1734
|
`;
|
|
1735
|
-
const StyledIndexingInfoSection = styled.div `
|
|
1736
|
-
padding: 15px;
|
|
1737
|
-
border-top: 1px solid #e0e0e0;
|
|
1738
|
-
background: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
|
|
1739
|
-
display: flex;
|
|
1740
|
-
flex-direction: column;
|
|
1741
|
-
gap: 10px;
|
|
1735
|
+
const StyledIndexingInfoSection = styled.div `
|
|
1736
|
+
padding: 15px;
|
|
1737
|
+
border-top: 1px solid #e0e0e0;
|
|
1738
|
+
background: linear-gradient(to bottom, #f9f9f9, #f5f5f5);
|
|
1739
|
+
display: flex;
|
|
1740
|
+
flex-direction: column;
|
|
1741
|
+
gap: 10px;
|
|
1742
1742
|
`;
|
|
1743
|
-
const StyledIndexingToggle = styled.button `
|
|
1744
|
-
display: flex;
|
|
1745
|
-
align-items: center;
|
|
1746
|
-
justify-content: space-between;
|
|
1747
|
-
width: 100%;
|
|
1748
|
-
padding: 10px 16px;
|
|
1749
|
-
background: white;
|
|
1750
|
-
border: 1px solid #d0d0d0;
|
|
1751
|
-
border-radius: 6px;
|
|
1752
|
-
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
|
1753
|
-
transition: all 0.2s ease;
|
|
1754
|
-
font-size: 14px;
|
|
1755
|
-
font-weight: 500;
|
|
1756
|
-
color: #333;
|
|
1757
|
-
opacity: ${props => props.disabled ? 0.6 : 1};
|
|
1758
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
1759
|
-
|
|
1760
|
-
&:hover:not(:disabled) {
|
|
1761
|
-
background: #f8f8f8;
|
|
1762
|
-
border-color: #2196F3;
|
|
1763
|
-
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
|
|
1764
|
-
}
|
|
1765
|
-
|
|
1766
|
-
&:active:not(:disabled) {
|
|
1767
|
-
transform: translateY(1px);
|
|
1768
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
1769
|
-
}
|
|
1743
|
+
const StyledIndexingToggle = styled.button `
|
|
1744
|
+
display: flex;
|
|
1745
|
+
align-items: center;
|
|
1746
|
+
justify-content: space-between;
|
|
1747
|
+
width: 100%;
|
|
1748
|
+
padding: 10px 16px;
|
|
1749
|
+
background: white;
|
|
1750
|
+
border: 1px solid #d0d0d0;
|
|
1751
|
+
border-radius: 6px;
|
|
1752
|
+
cursor: ${props => props.disabled ? 'not-allowed' : 'pointer'};
|
|
1753
|
+
transition: all 0.2s ease;
|
|
1754
|
+
font-size: 14px;
|
|
1755
|
+
font-weight: 500;
|
|
1756
|
+
color: #333;
|
|
1757
|
+
opacity: ${props => props.disabled ? 0.6 : 1};
|
|
1758
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
1759
|
+
|
|
1760
|
+
&:hover:not(:disabled) {
|
|
1761
|
+
background: #f8f8f8;
|
|
1762
|
+
border-color: #2196F3;
|
|
1763
|
+
box-shadow: 0 2px 4px rgba(33, 150, 243, 0.2);
|
|
1764
|
+
}
|
|
1765
|
+
|
|
1766
|
+
&:active:not(:disabled) {
|
|
1767
|
+
transform: translateY(1px);
|
|
1768
|
+
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
|
|
1769
|
+
}
|
|
1770
1770
|
`;
|
|
1771
|
-
const StyledLeftContent = styled.div `
|
|
1772
|
-
display: flex;
|
|
1773
|
-
align-items: center;
|
|
1774
|
-
gap: 8px;
|
|
1771
|
+
const StyledLeftContent = styled.div `
|
|
1772
|
+
display: flex;
|
|
1773
|
+
align-items: center;
|
|
1774
|
+
gap: 8px;
|
|
1775
1775
|
`;
|
|
1776
|
-
const StyledRightContent = styled.div `
|
|
1777
|
-
display: flex;
|
|
1778
|
-
align-items: center;
|
|
1779
|
-
gap: 8px;
|
|
1776
|
+
const StyledRightContent = styled.div `
|
|
1777
|
+
display: flex;
|
|
1778
|
+
align-items: center;
|
|
1779
|
+
gap: 8px;
|
|
1780
1780
|
`;
|
|
1781
|
-
const StyledChevron = styled.span `
|
|
1782
|
-
transition: transform 0.2s ease;
|
|
1783
|
-
transform: ${props => props.$isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
|
|
1784
|
-
color: #666;
|
|
1785
|
-
font-size: 12px;
|
|
1781
|
+
const StyledChevron = styled.span `
|
|
1782
|
+
transition: transform 0.2s ease;
|
|
1783
|
+
transform: ${props => props.$isOpen ? 'rotate(180deg)' : 'rotate(0deg)'};
|
|
1784
|
+
color: #666;
|
|
1785
|
+
font-size: 12px;
|
|
1786
1786
|
`;
|
|
1787
|
-
const StyledIndexingInfoBox = styled.div `
|
|
1788
|
-
position: relative;
|
|
1789
|
-
background: white;
|
|
1790
|
-
border: 1px solid #e0e0e0;
|
|
1791
|
-
border-radius: 6px;
|
|
1792
|
-
padding: 12px;
|
|
1793
|
-
max-height: 200px;
|
|
1794
|
-
overflow: auto;
|
|
1795
|
-
font-family: 'Courier New', monospace;
|
|
1796
|
-
font-size: 12px;
|
|
1797
|
-
color: #333;
|
|
1798
|
-
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1799
|
-
animation: slideDown 0.2s ease;
|
|
1800
|
-
|
|
1801
|
-
@keyframes slideDown {
|
|
1802
|
-
from {
|
|
1803
|
-
opacity: 0;
|
|
1804
|
-
transform: translateY(-10px);
|
|
1805
|
-
}
|
|
1806
|
-
to {
|
|
1807
|
-
opacity: 1;
|
|
1808
|
-
transform: translateY(0);
|
|
1809
|
-
}
|
|
1810
|
-
}
|
|
1787
|
+
const StyledIndexingInfoBox = styled.div `
|
|
1788
|
+
position: relative;
|
|
1789
|
+
background: white;
|
|
1790
|
+
border: 1px solid #e0e0e0;
|
|
1791
|
+
border-radius: 6px;
|
|
1792
|
+
padding: 12px;
|
|
1793
|
+
max-height: 200px;
|
|
1794
|
+
overflow: auto;
|
|
1795
|
+
font-family: 'Courier New', monospace;
|
|
1796
|
+
font-size: 12px;
|
|
1797
|
+
color: #333;
|
|
1798
|
+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
|
1799
|
+
animation: slideDown 0.2s ease;
|
|
1800
|
+
|
|
1801
|
+
@keyframes slideDown {
|
|
1802
|
+
from {
|
|
1803
|
+
opacity: 0;
|
|
1804
|
+
transform: translateY(-10px);
|
|
1805
|
+
}
|
|
1806
|
+
to {
|
|
1807
|
+
opacity: 1;
|
|
1808
|
+
transform: translateY(0);
|
|
1809
|
+
}
|
|
1810
|
+
}
|
|
1811
1811
|
`;
|
|
@@ -25,7 +25,7 @@ const TMResultDialog = ({ title, result, id1Caption, id2Caption }) => {
|
|
|
25
25
|
TMExceptionBoxManager.show({ exception: e.data.description });
|
|
26
26
|
}
|
|
27
27
|
};
|
|
28
|
-
return (_jsx(TMModal, { height: '65%', width: '75%', title: `${SDKUI_Localizator.OperationResult} - ${title}`, children: _jsxs(TMLayoutContainer, { children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'row', gap: '5px', padding: '5px', justifyContent: 'center', alignItems: 'center' }, children: [_jsx("p", { children: `${SDKUI_Localizator.ProcessedItems}: ${result.length}` }), _jsx(IconSuccessCirlce, { fontSize: 22, color: TMColors.success }), _jsx("p", { children: result.filter(o => o.resultType == ResultTypes.SUCCESS).length ?? 0 }), _jsx(IconWarning, { fontSize: 22, color: TMColors.warning }), _jsx("p", { children: result.filter(o => o.resultType == ResultTypes.WARNING).length ?? 0 }), _jsx(IconCloseCircle, { fontSize: 22, color: TMColors.error }), _jsx("p", { children: result.filter(o => o.resultType == ResultTypes.ERROR).length ?? 0 })] }), _jsxs(DataGrid, { dataSource: result.filter(o => o.resultType != ResultTypes.SUCCESS), showColumnLines: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.showRowLines) === 1, showRowLines: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.showColumnLines) === 1, keyExpr: "rowIndex", width: "100%", height: "100%", columnResizingMode: "widget", allowColumnResizing: true, focusedRowEnabled: true, hoverStateEnabled: true, rowAlternationEnabled: true, onRowDblClick: handleRowDblClick, children: [_jsx(Column, { dataField: 'resultType', caption: '', width: "5%", allowResizing: true, allowSorting: true, cellRender: cellRender }), _jsx(Column, { dataField: 'id1', caption: id1Caption, width: "10%" }), id2Caption && _jsx(Column, { dataField: 'id2', caption: id2Caption, width: "10%" }), _jsx(Column, { dataField: 'description', allowResizing: true, caption: SDKUI_Localizator.Description, allowSorting: true, width: id2Caption ? "75%" : "85%" }), _jsx(HeaderFilter, { visible: true }), _jsx(Paging, { enabled: true, pageSize: 30 }), _jsx(Pager, { visible: true, showInfo: true, showNavigationButtons: true }), _jsx(Scrolling, { mode: 'standard', useNative: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar) === 1 })] })] }) }));
|
|
28
|
+
return (_jsx(TMModal, { height: '65%', width: '75%', title: `${SDKUI_Localizator.OperationResult} - ${title}`, children: _jsxs(TMLayoutContainer, { children: [_jsxs("div", { style: { display: 'flex', flexDirection: 'row', gap: '5px', padding: '5px', justifyContent: 'center', alignItems: 'center', flexShrink: 0 }, children: [_jsx("p", { children: `${SDKUI_Localizator.ProcessedItems}: ${result.length}` }), _jsx(IconSuccessCirlce, { fontSize: 22, color: TMColors.success }), _jsx("p", { children: result.filter(o => o.resultType == ResultTypes.SUCCESS).length ?? 0 }), _jsx(IconWarning, { fontSize: 22, color: TMColors.warning }), _jsx("p", { children: result.filter(o => o.resultType == ResultTypes.WARNING).length ?? 0 }), _jsx(IconCloseCircle, { fontSize: 22, color: TMColors.error }), _jsx("p", { children: result.filter(o => o.resultType == ResultTypes.ERROR).length ?? 0 })] }), _jsx("div", { style: { flex: '1 1 auto', minHeight: 0, width: '100%', overflow: 'hidden' }, children: _jsxs(DataGrid, { dataSource: result.filter(o => o.resultType != ResultTypes.SUCCESS), showColumnLines: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.showRowLines) === 1, showRowLines: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.showColumnLines) === 1, keyExpr: "rowIndex", width: "100%", height: "100%", columnResizingMode: "widget", allowColumnResizing: true, focusedRowEnabled: true, hoverStateEnabled: true, rowAlternationEnabled: true, onRowDblClick: handleRowDblClick, children: [_jsx(Column, { dataField: 'resultType', caption: '', width: "5%", allowResizing: true, allowSorting: true, cellRender: cellRender }), _jsx(Column, { dataField: 'id1', caption: id1Caption, width: "10%" }), id2Caption && _jsx(Column, { dataField: 'id2', caption: id2Caption, width: "10%" }), _jsx(Column, { dataField: 'description', allowResizing: true, caption: SDKUI_Localizator.Description, allowSorting: true, width: id2Caption ? "75%" : "85%" }), _jsx(HeaderFilter, { visible: true }), _jsx(Paging, { enabled: true, pageSize: 30 }), _jsx(Pager, { visible: true, showInfo: true, showNavigationButtons: true }), _jsx(Scrolling, { mode: 'standard', useNative: Number(SDKUI_Globals.userSettings?.themeSettings.gridSettings.useNativeScrollbar) === 1 })] }) })] }) }));
|
|
29
29
|
};
|
|
30
30
|
export default TMResultDialog;
|
|
31
31
|
export class TMResultManager {
|
|
@@ -24,13 +24,6 @@ export interface DownloadDcmtsAsyncParams {
|
|
|
24
24
|
* - false (default): usa il nome file recuperato dal backend (file.name) come priorità
|
|
25
25
|
*/
|
|
26
26
|
useInputFileName?: boolean;
|
|
27
|
-
/**
|
|
28
|
-
* Numero di download eseguiti in parallelo (default 1 = sequenziale, comportamento preesistente).
|
|
29
|
-
* Valori > 1 vanno usati solo quando il chiamante consuma i file via
|
|
30
|
-
* `onFileDownloaded`: nel percorso senza callback ogni file genera un click su un
|
|
31
|
-
* tag <a download>, e più click ravvicinati vengono bloccati dal browser.
|
|
32
|
-
*/
|
|
33
|
-
downloadConcurrency?: number;
|
|
34
27
|
}
|
|
35
28
|
export interface UseDcmtOperationsReturn {
|
|
36
29
|
abortController: AbortController;
|
|
@@ -9,8 +9,6 @@ import { importCertificatesAsync } from '../helper/certificateImportHelper';
|
|
|
9
9
|
import { isXMLFileExt } from '../helper/dcmtsHelper';
|
|
10
10
|
import { ShowConfirm } from '../components/base/TMConfirm';
|
|
11
11
|
import { generateTargetFileName } from '../components/features/documents/copyAndMergeDcmtsShared';
|
|
12
|
-
/** Limite massimo di download paralleli accettato da `downloadDcmtsAsync`. */
|
|
13
|
-
const MAX_DOWNLOAD_CONCURRENCY = 8;
|
|
14
12
|
/** Intervallo minimo (ms) fra due aggiornamenti della progress bar di download. */
|
|
15
13
|
const DOWNLOAD_PROGRESS_THROTTLE_MS = 100;
|
|
16
14
|
const isScannerLicenseConfigured = () => {
|
|
@@ -261,7 +259,7 @@ export const useDcmtOperations = () => {
|
|
|
261
259
|
ShowAlert({ message: SDKUI_Localizator.ZipCreatedSavedInFolder.replaceParams(zipFileName, "Zip creato con successo"), mode: 'success', duration: 5000, title: SDKUI_Localizator.CompressToZipFile });
|
|
262
260
|
}
|
|
263
261
|
};
|
|
264
|
-
const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false
|
|
262
|
+
const _downloadDcmtsAsync = async (inputDcmts, downloadMode = "download", onFileDownloaded, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false) => {
|
|
265
263
|
if (inputDcmts === undefined)
|
|
266
264
|
return;
|
|
267
265
|
if (inputDcmts.length <= 0)
|
|
@@ -290,29 +288,22 @@ export const useDcmtOperations = () => {
|
|
|
290
288
|
setShowSecondary(true);
|
|
291
289
|
setWaitPanelTitle(operationTitle);
|
|
292
290
|
abortController = new AbortController();
|
|
293
|
-
// Riferimento const: il narrowing su inputDcmts non sopravvive dentro le closure
|
|
291
|
+
// Riferimento const: il narrowing su inputDcmts non sopravvive dentro le closure.
|
|
294
292
|
const dcmts = inputDcmts;
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
if (retrieveOptions?.generalRetrieveFormat !== undefined)
|
|
305
|
-
rfo.generalRetrieveFormat = retrieveOptions.generalRetrieveFormat;
|
|
306
|
-
rfo.invoiceRetrieveFormat = retrieveOptions?.invoiceRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
|
|
307
|
-
rfo.orderRetrieveFormat = retrieveOptions?.orderRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
|
|
308
|
-
return rfo;
|
|
309
|
-
};
|
|
310
|
-
const results = new Array(dcmts.length);
|
|
293
|
+
const rfo = new RetrieveFileOptions();
|
|
294
|
+
rfo.retrieveReason = retrieveOptions?.retrieveReason ?? DcmtOpers.ShowFile;
|
|
295
|
+
if (retrieveOptions?.cvtFormat !== undefined)
|
|
296
|
+
rfo.cvtFormat = retrieveOptions.cvtFormat;
|
|
297
|
+
if (retrieveOptions?.generalRetrieveFormat !== undefined)
|
|
298
|
+
rfo.generalRetrieveFormat = retrieveOptions.generalRetrieveFormat;
|
|
299
|
+
rfo.invoiceRetrieveFormat = retrieveOptions?.invoiceRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.invoiceRetrieveFormat;
|
|
300
|
+
rfo.orderRetrieveFormat = retrieveOptions?.orderRetrieveFormat ?? SDKUI_Globals.userSettings.searchSettings.orderRetrieveFormat;
|
|
301
|
+
const results = [];
|
|
311
302
|
setWaitPanelMaxValuePrimary(dcmts.length);
|
|
312
|
-
// ---- Progress secondaria:
|
|
303
|
+
// ---- Progress secondaria: throttlata ----
|
|
313
304
|
// Gli eventi di avanzamento di axios arrivano a ogni chunk: senza throttle ognuno
|
|
314
305
|
// provoca un re-render dell'intera form (relation viewer compreso).
|
|
315
|
-
|
|
306
|
+
let currentBytes;
|
|
316
307
|
let pendingSecondary;
|
|
317
308
|
let secondaryTimer;
|
|
318
309
|
const flushSecondaryProgress = () => {
|
|
@@ -325,41 +316,33 @@ export const useDcmtOperations = () => {
|
|
|
325
316
|
pendingSecondary = undefined;
|
|
326
317
|
};
|
|
327
318
|
const publishSecondaryProgress = () => {
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
for (const bytes of inFlightBytes.values()) {
|
|
331
|
-
loaded += bytes.loaded;
|
|
332
|
-
total += bytes.total;
|
|
333
|
-
}
|
|
319
|
+
const loaded = currentBytes?.loaded ?? 0;
|
|
320
|
+
const total = currentBytes?.total ?? 0;
|
|
334
321
|
pendingSecondary = total > 0
|
|
335
322
|
? { value: loaded, max: total, text: `Downloading... ${Globalization.getNumberDisplayValue(loaded, true)} / ${Globalization.getNumberDisplayValue(total, true)}` }
|
|
336
323
|
: { value: 0, max: 0, text: '' };
|
|
337
324
|
if (secondaryTimer === undefined)
|
|
338
325
|
secondaryTimer = setTimeout(flushSecondaryProgress, DOWNLOAD_PROGRESS_THROTTLE_MS);
|
|
339
326
|
};
|
|
340
|
-
// ----
|
|
341
|
-
let nextIndex = 0;
|
|
342
|
-
let started = 0;
|
|
327
|
+
// ---- Download sequenziale: un documento per volta ----
|
|
343
328
|
let completed = 0;
|
|
344
|
-
let abortReported = false;
|
|
345
329
|
const downloadOne = async (i) => {
|
|
346
|
-
|
|
347
|
-
setWaitPanelTextPrimary(`Download file ${started} di ${dcmts.length}`);
|
|
330
|
+
setWaitPanelTextPrimary(`Download file ${i + 1} di ${dcmts.length}`);
|
|
348
331
|
let file;
|
|
349
332
|
const cacheKey = `${dcmts[i].TID}-${dcmts[i].DID}`;
|
|
350
333
|
if (useCache && dcmtsFileCacheDownload.has(cacheKey)) {
|
|
351
334
|
file = dcmtsFileCacheDownload.get(cacheKey);
|
|
352
335
|
}
|
|
353
336
|
else {
|
|
354
|
-
|
|
337
|
+
currentBytes = { loaded: 0, total: 0 };
|
|
355
338
|
try {
|
|
356
|
-
file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(dcmts[i].TID, dcmts[i].DID,
|
|
357
|
-
|
|
339
|
+
file = await SDK_Globals.tmSession?.NewSearchEngine().RetrieveFileAsync(dcmts[i].TID, dcmts[i].DID, rfo, abortController.signal, (pd) => {
|
|
340
|
+
currentBytes = { loaded: pd.ProgressBarValue, total: pd.ProgressBarMaximum ?? 0 };
|
|
358
341
|
publishSecondaryProgress();
|
|
359
342
|
});
|
|
360
343
|
}
|
|
361
344
|
finally {
|
|
362
|
-
|
|
345
|
+
currentBytes = undefined;
|
|
363
346
|
publishSecondaryProgress();
|
|
364
347
|
}
|
|
365
348
|
}
|
|
@@ -400,40 +383,26 @@ export const useDcmtOperations = () => {
|
|
|
400
383
|
}
|
|
401
384
|
dcmtsFileCacheDownload.set(cacheKey, file);
|
|
402
385
|
}
|
|
403
|
-
results
|
|
386
|
+
results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.SUCCESS });
|
|
404
387
|
};
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
try {
|
|
419
|
-
await downloadOne(i);
|
|
420
|
-
}
|
|
421
|
-
catch (ex) {
|
|
422
|
-
results[i] = { rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(ex) };
|
|
423
|
-
const err = ex;
|
|
424
|
-
if (err.name === 'CanceledError') {
|
|
425
|
-
ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
|
|
426
|
-
}
|
|
388
|
+
for (let i = 0; i < dcmts.length; i++) {
|
|
389
|
+
if (abortController.signal.aborted) {
|
|
390
|
+
results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.WARNING, description: `Operazione interrotta. Elaborati ${completed} documenti` });
|
|
391
|
+
break;
|
|
392
|
+
}
|
|
393
|
+
try {
|
|
394
|
+
await downloadOne(i);
|
|
395
|
+
}
|
|
396
|
+
catch (ex) {
|
|
397
|
+
results.push({ rowIndex: i, id1: dcmts[i].TID, id2: dcmts[i].DID, resultType: ResultTypes.ERROR, description: getExceptionMessage(ex) });
|
|
398
|
+
const err = ex;
|
|
399
|
+
if (err.name === 'CanceledError') {
|
|
400
|
+
ShowAlert({ message: err.message, mode: 'warning', duration: 3000, title: 'Abort' });
|
|
427
401
|
}
|
|
428
402
|
}
|
|
429
|
-
}
|
|
430
|
-
// `|| 1` intercetta NaN/0/negativi: senza, Array.from({length: NaN}) non creerebbe
|
|
431
|
-
// alcun worker e il download terminerebbe in silenzio senza scaricare nulla.
|
|
432
|
-
const workers = Math.max(1, Math.min(Math.trunc(downloadConcurrency) || 1, MAX_DOWNLOAD_CONCURRENCY, dcmts.length));
|
|
433
|
-
await Promise.all(Array.from({ length: workers }, () => runWorker()));
|
|
403
|
+
}
|
|
434
404
|
if (secondaryTimer !== undefined)
|
|
435
405
|
clearTimeout(secondaryTimer);
|
|
436
|
-
const result = results.filter((r) => r !== undefined);
|
|
437
406
|
setWaitPanelTextPrimary('');
|
|
438
407
|
setWaitPanelMaxValuePrimary(0);
|
|
439
408
|
setWaitPanelValuePrimary(0);
|
|
@@ -441,7 +410,7 @@ export const useDcmtOperations = () => {
|
|
|
441
410
|
setWaitPanelMaxValueSecondary(0);
|
|
442
411
|
setWaitPanelValueSecondary(0);
|
|
443
412
|
setShowWaitPanel(false);
|
|
444
|
-
TMResultManager.show(
|
|
413
|
+
TMResultManager.show(results, operationTitle, "TID", "DID", undefined, undefined, showSuccessAlert);
|
|
445
414
|
};
|
|
446
415
|
const _downloadAttachmentsAsync = async (inputDcmts, confirmAttachments, showSuccessAlert = true) => {
|
|
447
416
|
if (inputDcmts === undefined)
|
|
@@ -505,13 +474,12 @@ export const useDcmtOperations = () => {
|
|
|
505
474
|
};
|
|
506
475
|
const downloadDcmtsAsync = async (params) => {
|
|
507
476
|
const { inputDcmts, downloadType = DownloadTypes.Attachment, downloadMode = "download", onFileDownloaded, confirmAttachments, skipConfirmation = false, retrieveOptions, useCache = true, showSuccessAlert = true, useInputFileName = false, // Default: usa il nome dal backend
|
|
508
|
-
downloadConcurrency = 1, // Default: sequenziale
|
|
509
477
|
} = params;
|
|
510
478
|
switch (downloadType) {
|
|
511
479
|
// Per il download di documenti, passa useInputFileName per controllare la priorità del nome
|
|
512
|
-
case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName
|
|
480
|
+
case DownloadTypes.Dcmt: return await _downloadDcmtsAsync(inputDcmts, downloadMode, onFileDownloaded, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName);
|
|
513
481
|
case DownloadTypes.Attachment: return await _downloadAttachmentsAsync(inputDcmts, confirmAttachments, showSuccessAlert);
|
|
514
|
-
default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName
|
|
482
|
+
default: return await _downloadDcmtsAsync(inputDcmts, undefined, undefined, skipConfirmation, retrieveOptions, useCache, showSuccessAlert, useInputFileName);
|
|
515
483
|
}
|
|
516
484
|
};
|
|
517
485
|
const uploadDcmtsAsync = async (inputDcmts, operationTitle, operType, actionAfterOperationAsync) => {
|