@praxisui/files-upload 1.0.0-beta.60 → 1.0.0-beta.63
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 +139 -40
- package/fesm2022/praxisui-files-upload.mjs +2044 -658
- package/fesm2022/praxisui-files-upload.mjs.map +1 -1
- package/index.d.ts +529 -153
- package/package.json +8 -5
|
@@ -1,44 +1,70 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { Injectable, inject, signal, Inject, Component, InjectionToken, Optional, EventEmitter, ViewChild, Output, Input, ChangeDetectionStrategy, ENVIRONMENT_INITIALIZER } from '@angular/core';
|
|
3
3
|
import { ActivatedRoute } from '@angular/router';
|
|
4
|
-
import * as
|
|
4
|
+
import * as i9 from '@angular/common';
|
|
5
5
|
import { CommonModule } from '@angular/common';
|
|
6
6
|
import * as i1$1 from '@angular/forms';
|
|
7
7
|
import { ReactiveFormsModule, FormsModule, FormControl } from '@angular/forms';
|
|
8
|
-
import * as
|
|
8
|
+
import * as i11 from '@angular/material/button';
|
|
9
9
|
import { MatButtonModule } from '@angular/material/button';
|
|
10
10
|
import * as i10 from '@angular/material/icon';
|
|
11
11
|
import { MatIconModule } from '@angular/material/icon';
|
|
12
|
-
import * as
|
|
12
|
+
import * as i13 from '@angular/material/progress-bar';
|
|
13
13
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
14
|
-
import * as
|
|
14
|
+
import * as i14 from '@angular/material/menu';
|
|
15
15
|
import { MatMenuModule } from '@angular/material/menu';
|
|
16
16
|
import * as i1 from '@angular/common/http';
|
|
17
|
-
import { HttpHeaders, HttpEventType, HttpErrorResponse } from '@angular/common/http';
|
|
18
|
-
import
|
|
17
|
+
import { HttpHeaders, HttpEventType, HttpResponse, HttpErrorResponse } from '@angular/common/http';
|
|
18
|
+
import * as i1$2 from '@praxisui/core';
|
|
19
|
+
import { providePraxisI18n, ComponentKeyService, ASYNC_CONFIG_STORAGE, PraxisIconDirective, PraxisI18nService, ComponentMetadataRegistry } from '@praxisui/core';
|
|
19
20
|
import * as i4 from '@angular/material/tabs';
|
|
20
21
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
21
22
|
import * as i5 from '@angular/material/form-field';
|
|
22
23
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
23
24
|
import * as i6 from '@angular/material/input';
|
|
24
25
|
import { MatInputModule } from '@angular/material/input';
|
|
25
|
-
import * as i7
|
|
26
|
+
import * as i7 from '@angular/material/checkbox';
|
|
26
27
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
27
28
|
import * as i8 from '@angular/material/select';
|
|
28
29
|
import { MatSelectModule } from '@angular/material/select';
|
|
29
|
-
import * as i11 from '@angular/material/tooltip';
|
|
30
|
+
import * as i11$1 from '@angular/material/tooltip';
|
|
30
31
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
31
32
|
import * as i2 from '@angular/material/snack-bar';
|
|
32
33
|
import { MatSnackBarModule } from '@angular/material/snack-bar';
|
|
33
34
|
import { toObservable, takeUntilDestroyed } from '@angular/core/rxjs-interop';
|
|
34
|
-
import { of, throwError, BehaviorSubject, map as map$1, startWith, retry, debounceTime } from 'rxjs';
|
|
35
|
-
import * as i1$
|
|
35
|
+
import { of, throwError, BehaviorSubject, map as map$1, startWith, retry, debounceTime, from } from 'rxjs';
|
|
36
|
+
import * as i1$3 from '@praxisui/settings-panel';
|
|
36
37
|
import { SETTINGS_PANEL_DATA } from '@praxisui/settings-panel';
|
|
37
|
-
import { map, catchError, take } from 'rxjs/operators';
|
|
38
|
+
import { map, catchError, take, concatMap, toArray, tap, filter, defaultIfEmpty } from 'rxjs/operators';
|
|
38
39
|
import { TemplatePortal } from '@angular/cdk/portal';
|
|
39
40
|
import * as i3 from '@angular/cdk/overlay';
|
|
41
|
+
import { CdkConnectedOverlay, CdkOverlayOrigin } from '@angular/cdk/overlay';
|
|
40
42
|
import { SimpleBaseInputComponent } from '@praxisui/dynamic-fields';
|
|
41
43
|
|
|
44
|
+
function isStructuredErrorItem(value) {
|
|
45
|
+
return !!value && typeof value === 'object' && 'code' in value && 'message' in value;
|
|
46
|
+
}
|
|
47
|
+
function isErrorEnvelope(value) {
|
|
48
|
+
return !!value && typeof value === 'object' && 'status' in value;
|
|
49
|
+
}
|
|
50
|
+
/** Resolves the primary structured error item from the backend error payload. */
|
|
51
|
+
function getPrimaryErrorItem(error) {
|
|
52
|
+
if (!error) {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
if (isStructuredErrorItem(error) && !isErrorEnvelope(error)) {
|
|
56
|
+
return error;
|
|
57
|
+
}
|
|
58
|
+
if (!isErrorEnvelope(error)) {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
const structured = error.errors?.[0];
|
|
62
|
+
if (structured) {
|
|
63
|
+
return structured;
|
|
64
|
+
}
|
|
65
|
+
return undefined;
|
|
66
|
+
}
|
|
67
|
+
|
|
42
68
|
var ErrorCode;
|
|
43
69
|
(function (ErrorCode) {
|
|
44
70
|
ErrorCode["INVALID_FILE_TYPE"] = "INVALID_FILE_TYPE";
|
|
@@ -433,7 +459,7 @@ class PraxisFilesUploadConfigEditor {
|
|
|
433
459
|
dense: [false],
|
|
434
460
|
accept: [''],
|
|
435
461
|
showMetadataForm: [false],
|
|
436
|
-
//
|
|
462
|
+
// Grupos específicos de UI
|
|
437
463
|
dropzone: this.fb.group({
|
|
438
464
|
expandOnDragProximity: [true],
|
|
439
465
|
proximityPx: [64],
|
|
@@ -454,18 +480,18 @@ class PraxisFilesUploadConfigEditor {
|
|
|
454
480
|
maxFileSizeBytes: [null],
|
|
455
481
|
maxFilesPerBulk: [null],
|
|
456
482
|
maxBulkSizeBytes: [null],
|
|
483
|
+
}),
|
|
484
|
+
options: this.fb.group({
|
|
457
485
|
defaultConflictPolicy: ['RENAME'],
|
|
458
|
-
failFast: [false],
|
|
459
486
|
strictValidation: [true],
|
|
460
487
|
maxUploadSizeMb: [50],
|
|
461
|
-
}),
|
|
462
|
-
options: this.fb.group({
|
|
463
488
|
allowedExtensions: [''],
|
|
464
489
|
acceptMimeTypes: [''],
|
|
465
490
|
targetDirectory: [''],
|
|
466
491
|
enableVirusScanning: [false],
|
|
467
492
|
}),
|
|
468
493
|
bulk: this.fb.group({
|
|
494
|
+
failFast: [false],
|
|
469
495
|
parallelUploads: [1],
|
|
470
496
|
retryCount: [0],
|
|
471
497
|
retryBackoffMs: [0],
|
|
@@ -483,7 +509,7 @@ class PraxisFilesUploadConfigEditor {
|
|
|
483
509
|
headers: this.fb.group({
|
|
484
510
|
tenantHeader: ['X-Tenant-Id'],
|
|
485
511
|
userHeader: ['X-User-Id'],
|
|
486
|
-
//
|
|
512
|
+
// Valores usados nas consultas de configuração do servidor
|
|
487
513
|
tenantValue: [''],
|
|
488
514
|
userValue: [''],
|
|
489
515
|
}),
|
|
@@ -537,7 +563,7 @@ class PraxisFilesUploadConfigEditor {
|
|
|
537
563
|
if (patch.ui?.accept) {
|
|
538
564
|
patch.ui = { ...patch.ui, accept: patch.ui.accept.join(',') };
|
|
539
565
|
}
|
|
540
|
-
//
|
|
566
|
+
// Normalizar lista de detalhes para CSV
|
|
541
567
|
if (patch.ui?.list?.detailsFields) {
|
|
542
568
|
patch.ui = {
|
|
543
569
|
...patch.ui,
|
|
@@ -570,22 +596,18 @@ class PraxisFilesUploadConfigEditor {
|
|
|
570
596
|
applyServerConfig(cfg) {
|
|
571
597
|
if (!cfg)
|
|
572
598
|
return;
|
|
573
|
-
//
|
|
599
|
+
// Configuração efetiva do backend separada em validações locais, opções
|
|
600
|
+
// de backend e execução de lote.
|
|
574
601
|
const options = cfg.options ?? {};
|
|
575
602
|
const bulk = cfg.bulk ?? {};
|
|
576
|
-
//
|
|
603
|
+
// Validações locais alimentadas pelo backend
|
|
577
604
|
this.limitsGroup.patchValue({
|
|
578
|
-
|
|
579
|
-
strictValidation: options.strictValidation ?? null,
|
|
580
|
-
maxUploadSizeMb: options.maxUploadSizeMb ?? null,
|
|
581
|
-
// failFast padrão do servidor pode influenciar a UI de validação em lote
|
|
582
|
-
failFast: bulk.failFastModeDefault ?? false,
|
|
583
|
-
// preencher máx. arquivos por lote a partir do backend
|
|
605
|
+
// Preencher máx. arquivos por lote a partir do backend
|
|
584
606
|
maxFilesPerBulk: typeof bulk.maxFilesPerBatch === 'number'
|
|
585
607
|
? bulk.maxFilesPerBatch
|
|
586
608
|
: null,
|
|
587
609
|
}, { emitEvent: false });
|
|
588
|
-
//
|
|
610
|
+
// Opções de backend (normalizar arrays em string CSV)
|
|
589
611
|
const allowed = Array.isArray(options.allowedExtensions)
|
|
590
612
|
? options.allowedExtensions.join(',')
|
|
591
613
|
: '';
|
|
@@ -593,13 +615,17 @@ class PraxisFilesUploadConfigEditor {
|
|
|
593
615
|
? options.acceptMimeTypes.join(',')
|
|
594
616
|
: '';
|
|
595
617
|
this.optionsGroup.patchValue({
|
|
618
|
+
defaultConflictPolicy: options.nameConflictPolicy ?? null,
|
|
619
|
+
strictValidation: options.strictValidation ?? null,
|
|
620
|
+
maxUploadSizeMb: options.maxUploadSizeMb ?? null,
|
|
596
621
|
allowedExtensions: allowed,
|
|
597
622
|
acceptMimeTypes: mimes,
|
|
598
623
|
targetDirectory: options.targetDirectory ?? '',
|
|
599
624
|
enableVirusScanning: !!options.enableVirusScanning,
|
|
600
625
|
}, { emitEvent: false });
|
|
601
|
-
//
|
|
626
|
+
// Execução de lote
|
|
602
627
|
this.bulkGroup.patchValue({
|
|
628
|
+
failFast: bulk.failFastModeDefault ?? false,
|
|
603
629
|
parallelUploads: typeof bulk.maxConcurrentUploads === 'number'
|
|
604
630
|
? bulk.maxConcurrentUploads
|
|
605
631
|
: 1,
|
|
@@ -725,10 +751,165 @@ class PraxisFilesUploadConfigEditor {
|
|
|
725
751
|
refetchServerConfig() {
|
|
726
752
|
this.state.refetch();
|
|
727
753
|
}
|
|
754
|
+
get summaryStrategyTitle() {
|
|
755
|
+
const strategy = this.form.get('strategy')?.value;
|
|
756
|
+
if (strategy === 'presign')
|
|
757
|
+
return 'Upload com URL pré-assinada';
|
|
758
|
+
if (strategy === 'auto')
|
|
759
|
+
return 'Seleção automática da estratégia';
|
|
760
|
+
return 'Upload direto ao backend';
|
|
761
|
+
}
|
|
762
|
+
get summaryStrategyDetail() {
|
|
763
|
+
const strategy = this.form.get('strategy')?.value;
|
|
764
|
+
if (strategy === 'presign') {
|
|
765
|
+
return 'Depende de endpoint de presign e costuma reduzir carga direta no servidor principal.';
|
|
766
|
+
}
|
|
767
|
+
if (strategy === 'auto') {
|
|
768
|
+
return 'Tenta presign primeiro e volta ao fluxo direto quando necessário.';
|
|
769
|
+
}
|
|
770
|
+
return 'Fluxo mais simples de integrar e diagnosticar em ambientes controlados.';
|
|
771
|
+
}
|
|
772
|
+
get summaryExperienceTitle() {
|
|
773
|
+
const ui = this.uiGroup.value;
|
|
774
|
+
if (ui?.manualUpload)
|
|
775
|
+
return 'Revisão manual antes do envio';
|
|
776
|
+
if (ui?.showDropzone === false)
|
|
777
|
+
return 'Seleção orientada por campo compacto';
|
|
778
|
+
return 'Envio imediato com dropzone visível';
|
|
779
|
+
}
|
|
780
|
+
get summaryExperienceDetail() {
|
|
781
|
+
const ui = this.uiGroup.value;
|
|
782
|
+
const tokens = [
|
|
783
|
+
ui?.showProgress ? 'progresso visível' : 'sem barra de progresso',
|
|
784
|
+
ui?.showConflictPolicySelector
|
|
785
|
+
? 'escolha de conflito disponível'
|
|
786
|
+
: 'política de conflito escondida',
|
|
787
|
+
ui?.showMetadataForm ? 'metadados editáveis na UI' : 'sem formulário extra',
|
|
788
|
+
];
|
|
789
|
+
return tokens.join(' • ');
|
|
790
|
+
}
|
|
791
|
+
get summaryLimitsTitle() {
|
|
792
|
+
const maxFile = this.optionsGroup.get('maxUploadSizeMb')?.value;
|
|
793
|
+
const maxBulk = this.limitsGroup.get('maxFilesPerBulk')?.value;
|
|
794
|
+
return `${maxFile || '—'} MB por arquivo • ${maxBulk || '—'} itens por lote`;
|
|
795
|
+
}
|
|
796
|
+
get summaryLimitsDetail() {
|
|
797
|
+
const parallel = this.bulkGroup.get('parallelUploads')?.value;
|
|
798
|
+
const failFast = this.bulkGroup.get('failFast')?.value;
|
|
799
|
+
return `${parallel || 1} upload(s) paralelos • ${failFast ? 'fail-fast ativo' : 'falhas parciais permitidas'}`;
|
|
800
|
+
}
|
|
801
|
+
get summaryServerTitle() {
|
|
802
|
+
const conflict = this.optionsGroup.get('defaultConflictPolicy')?.value || 'RENAME';
|
|
803
|
+
return `Conflito padrão: ${conflict}`;
|
|
804
|
+
}
|
|
805
|
+
get summaryServerDetail() {
|
|
806
|
+
const strict = this.optionsGroup.get('strictValidation')?.value;
|
|
807
|
+
const virus = this.optionsGroup.get('enableVirusScanning')?.value;
|
|
808
|
+
return `${strict ? 'validação rigorosa ligada' : 'validação rígida desativada'} • ${virus ? 'antivírus forçado' : 'antivírus opcional/inativo'}`;
|
|
809
|
+
}
|
|
810
|
+
get activeRisks() {
|
|
811
|
+
const risks = [];
|
|
812
|
+
if (this.optionsGroup.get('defaultConflictPolicy')?.value === 'OVERWRITE') {
|
|
813
|
+
risks.push({
|
|
814
|
+
title: 'Sobrescrita habilitada',
|
|
815
|
+
detail: 'Arquivos existentes podem ser substituídos silenciosamente se o backend aceitar a operação.',
|
|
816
|
+
});
|
|
817
|
+
}
|
|
818
|
+
if (this.optionsGroup.get('enableVirusScanning')?.value === true) {
|
|
819
|
+
risks.push({
|
|
820
|
+
title: 'Antivírus pode elevar latência',
|
|
821
|
+
detail: 'Ativar varredura forçada melhora segurança, mas pode impactar throughput e tempo de resposta.',
|
|
822
|
+
});
|
|
823
|
+
}
|
|
824
|
+
if (this.bulkGroup.get('failFast')?.value === true) {
|
|
825
|
+
risks.push({
|
|
826
|
+
title: 'Lote para no primeiro erro',
|
|
827
|
+
detail: 'Usuários podem precisar reenviar itens válidos quando o primeiro erro interromper o restante do lote.',
|
|
828
|
+
});
|
|
829
|
+
}
|
|
830
|
+
if ((this.rateLimitGroup.get('maxAutoRetry')?.value ?? 0) >= 5) {
|
|
831
|
+
risks.push({
|
|
832
|
+
title: 'Retry automático agressivo',
|
|
833
|
+
detail: 'Muitas tentativas automáticas podem aumentar tempo de espera percebido e ruído operacional.',
|
|
834
|
+
});
|
|
835
|
+
}
|
|
836
|
+
if (this.uiGroup.get('manualUpload')?.value === true) {
|
|
837
|
+
risks.push({
|
|
838
|
+
title: 'Fluxo exige ação manual',
|
|
839
|
+
detail: 'Adequado para revisão, mas aumenta passos no envio e pode reduzir taxa de conclusão em cenários simples.',
|
|
840
|
+
});
|
|
841
|
+
}
|
|
842
|
+
return risks;
|
|
843
|
+
}
|
|
728
844
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilesUploadConfigEditor, deps: [{ token: i1$1.FormBuilder }, { token: SETTINGS_PANEL_DATA }, { token: i2.MatSnackBar }, { token: i0.DestroyRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
729
845
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisFilesUploadConfigEditor, isStandalone: true, selector: "praxis-files-upload-config-editor", ngImport: i0, template: `
|
|
846
|
+
<div class="editor-layout">
|
|
847
|
+
<aside class="summary-panel" aria-label="Resumo executivo da configuração">
|
|
848
|
+
<h3>Resumo executivo</h3>
|
|
849
|
+
<p class="summary-intro">
|
|
850
|
+
Revise aqui o impacto operacional antes de percorrer todos os grupos
|
|
851
|
+
de configuração.
|
|
852
|
+
</p>
|
|
853
|
+
|
|
854
|
+
<div class="summary-grid">
|
|
855
|
+
<section class="summary-card">
|
|
856
|
+
<span class="eyebrow">Estratégia</span>
|
|
857
|
+
<strong>{{ summaryStrategyTitle }}</strong>
|
|
858
|
+
<p>{{ summaryStrategyDetail }}</p>
|
|
859
|
+
</section>
|
|
860
|
+
|
|
861
|
+
<section class="summary-card">
|
|
862
|
+
<span class="eyebrow">Experiência</span>
|
|
863
|
+
<strong>{{ summaryExperienceTitle }}</strong>
|
|
864
|
+
<p>{{ summaryExperienceDetail }}</p>
|
|
865
|
+
</section>
|
|
866
|
+
|
|
867
|
+
<section class="summary-card">
|
|
868
|
+
<span class="eyebrow">Limites</span>
|
|
869
|
+
<strong>{{ summaryLimitsTitle }}</strong>
|
|
870
|
+
<p>{{ summaryLimitsDetail }}</p>
|
|
871
|
+
</section>
|
|
872
|
+
|
|
873
|
+
<section class="summary-card">
|
|
874
|
+
<span class="eyebrow">Servidor</span>
|
|
875
|
+
<strong>{{ summaryServerTitle }}</strong>
|
|
876
|
+
<p>{{ summaryServerDetail }}</p>
|
|
877
|
+
</section>
|
|
878
|
+
</div>
|
|
879
|
+
|
|
880
|
+
<section class="risk-panel" [class.safe]="activeRisks.length === 0">
|
|
881
|
+
<h4>
|
|
882
|
+
<mat-icon aria-hidden="true">{{
|
|
883
|
+
activeRisks.length === 0 ? 'verified' : 'warning'
|
|
884
|
+
}}</mat-icon>
|
|
885
|
+
Riscos e atenção
|
|
886
|
+
</h4>
|
|
887
|
+
<div *ngIf="activeRisks.length > 0; else noRiskState" class="risk-list">
|
|
888
|
+
<article class="risk-item" *ngFor="let risk of activeRisks">
|
|
889
|
+
<strong>{{ risk.title }}</strong>
|
|
890
|
+
<p>{{ risk.detail }}</p>
|
|
891
|
+
</article>
|
|
892
|
+
</div>
|
|
893
|
+
<ng-template #noRiskState>
|
|
894
|
+
<p class="safe-copy">
|
|
895
|
+
Nenhum risco operacional evidente na configuração atual.
|
|
896
|
+
</p>
|
|
897
|
+
</ng-template>
|
|
898
|
+
</section>
|
|
899
|
+
</aside>
|
|
900
|
+
|
|
901
|
+
<div class="editor-main">
|
|
730
902
|
<mat-tab-group>
|
|
731
903
|
<mat-tab label="Comportamento">
|
|
904
|
+
<div class="tab-panel">
|
|
905
|
+
<section class="tab-intro">
|
|
906
|
+
<h3>Estratégia e cadência do envio</h3>
|
|
907
|
+
<p>
|
|
908
|
+
Defina aqui como o usuário inicia o upload e como o lote se
|
|
909
|
+
comporta em termos de paralelismo e retentativa.
|
|
910
|
+
</p>
|
|
911
|
+
</section>
|
|
912
|
+
<section class="config-section">
|
|
732
913
|
<form [formGroup]="form">
|
|
733
914
|
<mat-form-field appearance="fill">
|
|
734
915
|
<mat-label>Estratégia de envio <span class="opt-tag">opcional</span></mat-label>
|
|
@@ -751,11 +932,18 @@ class PraxisFilesUploadConfigEditor {
|
|
|
751
932
|
</button>
|
|
752
933
|
</mat-form-field>
|
|
753
934
|
</form>
|
|
935
|
+
</section>
|
|
936
|
+
<section class="config-section">
|
|
754
937
|
<form [formGroup]="bulkGroup">
|
|
755
938
|
<h4 class="section-subtitle">
|
|
756
939
|
<mat-icon aria-hidden="true">build</mat-icon>
|
|
757
940
|
Opções configuráveis — Lote
|
|
758
941
|
</h4>
|
|
942
|
+
<p class="section-note">
|
|
943
|
+
Use paralelismo e retries com moderação. Em ambientes corporativos,
|
|
944
|
+
esses controles afetam throughput, experiência percebida e carga no
|
|
945
|
+
backend.
|
|
946
|
+
</p>
|
|
759
947
|
<mat-form-field appearance="fill">
|
|
760
948
|
<mat-label>Uploads paralelos <span class="opt-tag">opcional</span></mat-label>
|
|
761
949
|
<input matInput type="number" formControlName="parallelUploads" />
|
|
@@ -799,13 +987,28 @@ class PraxisFilesUploadConfigEditor {
|
|
|
799
987
|
</button>
|
|
800
988
|
</mat-form-field>
|
|
801
989
|
</form>
|
|
990
|
+
</section>
|
|
991
|
+
</div>
|
|
802
992
|
</mat-tab>
|
|
803
993
|
<mat-tab label="Interface">
|
|
994
|
+
<div class="tab-panel">
|
|
995
|
+
<section class="tab-intro">
|
|
996
|
+
<h3>Experiência visível para o usuário</h3>
|
|
997
|
+
<p>
|
|
998
|
+
Configure densidade, dropzone, metadados e detalhes da lista.
|
|
999
|
+
Priorize clareza operacional antes de habilitar recursos avançados.
|
|
1000
|
+
</p>
|
|
1001
|
+
</section>
|
|
1002
|
+
<section class="config-section">
|
|
804
1003
|
<form [formGroup]="uiGroup">
|
|
805
1004
|
<h4 class="section-subtitle">
|
|
806
1005
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
807
1006
|
Opções configuráveis — Interface
|
|
808
1007
|
</h4>
|
|
1008
|
+
<p class="section-note">
|
|
1009
|
+
Esta seção controla a ergonomia do componente. Mudanças aqui afetam
|
|
1010
|
+
descoberta, esforço de uso e taxa de conclusão do envio.
|
|
1011
|
+
</p>
|
|
809
1012
|
<mat-checkbox formControlName="showDropzone"
|
|
810
1013
|
>Exibir área de soltar</mat-checkbox
|
|
811
1014
|
>
|
|
@@ -919,13 +1122,28 @@ class PraxisFilesUploadConfigEditor {
|
|
|
919
1122
|
</mat-form-field>
|
|
920
1123
|
</fieldset>
|
|
921
1124
|
</form>
|
|
1125
|
+
</section>
|
|
1126
|
+
</div>
|
|
922
1127
|
</mat-tab>
|
|
923
1128
|
<mat-tab label="Validações">
|
|
1129
|
+
<div class="tab-panel">
|
|
1130
|
+
<section class="tab-intro">
|
|
1131
|
+
<h3>Regras de aceite e comportamento operacional</h3>
|
|
1132
|
+
<p>
|
|
1133
|
+
Separe mentalmente o que é validação de UX local do que é regra
|
|
1134
|
+
efetiva de backend. Revise com atenção as opções destrutivas.
|
|
1135
|
+
</p>
|
|
1136
|
+
</section>
|
|
1137
|
+
<section class="config-section">
|
|
924
1138
|
<form [formGroup]="limitsGroup">
|
|
925
1139
|
<h4 class="section-subtitle">
|
|
926
1140
|
<mat-icon aria-hidden="true">build</mat-icon>
|
|
927
1141
|
Opções configuráveis — Validações
|
|
928
1142
|
</h4>
|
|
1143
|
+
<p class="section-note">
|
|
1144
|
+
Limites locais melhoram feedback imediato, mas não substituem a
|
|
1145
|
+
política efetiva do servidor.
|
|
1146
|
+
</p>
|
|
929
1147
|
<mat-form-field appearance="fill">
|
|
930
1148
|
<mat-label>Tamanho máximo do arquivo (bytes) <span class="opt-tag">opcional</span></mat-label>
|
|
931
1149
|
<input matInput type="number" formControlName="maxFileSizeBytes" />
|
|
@@ -948,6 +1166,19 @@ class PraxisFilesUploadConfigEditor {
|
|
|
948
1166
|
<mat-label>Tamanho máximo do lote (bytes) <span class="opt-tag">opcional</span></mat-label>
|
|
949
1167
|
<input matInput type="number" formControlName="maxBulkSizeBytes" />
|
|
950
1168
|
</mat-form-field>
|
|
1169
|
+
</form>
|
|
1170
|
+
</section>
|
|
1171
|
+
<section class="config-section">
|
|
1172
|
+
<form [formGroup]="optionsGroup">
|
|
1173
|
+
<h4 class="section-subtitle">
|
|
1174
|
+
<mat-icon aria-hidden="true">tune</mat-icon>
|
|
1175
|
+
Opções configuráveis — Backend
|
|
1176
|
+
</h4>
|
|
1177
|
+
<p class="section-note">
|
|
1178
|
+
Estas opções têm impacto direto no contrato com o servidor e em
|
|
1179
|
+
governança operacional. Trate alterações aqui como decisão de
|
|
1180
|
+
política, não apenas de interface.
|
|
1181
|
+
</p>
|
|
951
1182
|
<mat-form-field appearance="fill">
|
|
952
1183
|
<mat-label>Política de conflito (padrão) <span class="opt-tag">opcional</span></mat-label>
|
|
953
1184
|
<mat-select formControlName="defaultConflictPolicy">
|
|
@@ -967,14 +1198,11 @@ class PraxisFilesUploadConfigEditor {
|
|
|
967
1198
|
>
|
|
968
1199
|
<mat-icon>help_outline</mat-icon>
|
|
969
1200
|
</button>
|
|
970
|
-
<div class="warn" *ngIf="
|
|
1201
|
+
<div class="warn" *ngIf="optionsGroup.get('defaultConflictPolicy')?.value === 'OVERWRITE'">
|
|
971
1202
|
<mat-icon color="warn" aria-hidden="true">warning</mat-icon>
|
|
972
1203
|
Atenção: OVERWRITE pode sobrescrever arquivos existentes.
|
|
973
1204
|
</div>
|
|
974
1205
|
</mat-form-field>
|
|
975
|
-
<mat-checkbox formControlName="failFast"
|
|
976
|
-
>Parar no primeiro erro (fail-fast)</mat-checkbox
|
|
977
|
-
>
|
|
978
1206
|
<mat-checkbox formControlName="strictValidation"
|
|
979
1207
|
>Validação rigorosa (backend)</mat-checkbox
|
|
980
1208
|
>
|
|
@@ -992,12 +1220,6 @@ class PraxisFilesUploadConfigEditor {
|
|
|
992
1220
|
<mat-icon>help_outline</mat-icon>
|
|
993
1221
|
</button>
|
|
994
1222
|
</mat-form-field>
|
|
995
|
-
</form>
|
|
996
|
-
<form [formGroup]="optionsGroup">
|
|
997
|
-
<h4 class="section-subtitle">
|
|
998
|
-
<mat-icon aria-hidden="true">tune</mat-icon>
|
|
999
|
-
Opções configuráveis — Avançado
|
|
1000
|
-
</h4>
|
|
1001
1223
|
<mat-form-field appearance="fill">
|
|
1002
1224
|
<mat-label>Extensões permitidas <span class="opt-tag">opcional</span></mat-label>
|
|
1003
1225
|
<input
|
|
@@ -1050,6 +1272,8 @@ class PraxisFilesUploadConfigEditor {
|
|
|
1050
1272
|
Pode impactar desempenho e latência de upload.
|
|
1051
1273
|
</div>
|
|
1052
1274
|
</form>
|
|
1275
|
+
</section>
|
|
1276
|
+
<section class="config-section">
|
|
1053
1277
|
<form [formGroup]="quotasGroup">
|
|
1054
1278
|
<h4 class="section-subtitle">
|
|
1055
1279
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
@@ -1062,11 +1286,17 @@ class PraxisFilesUploadConfigEditor {
|
|
|
1062
1286
|
>Bloquear ao exceder cota</mat-checkbox
|
|
1063
1287
|
>
|
|
1064
1288
|
</form>
|
|
1289
|
+
</section>
|
|
1290
|
+
<section class="config-section">
|
|
1065
1291
|
<form [formGroup]="rateLimitGroup">
|
|
1066
1292
|
<h4 class="section-subtitle">
|
|
1067
1293
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
1068
1294
|
Opções configuráveis — Rate Limit (UI)
|
|
1069
1295
|
</h4>
|
|
1296
|
+
<p class="section-note">
|
|
1297
|
+
Prefira poucos retries automáticos. Mais tentativas aliviam atrito
|
|
1298
|
+
imediato, mas podem mascarar gargalos reais do ambiente.
|
|
1299
|
+
</p>
|
|
1070
1300
|
<mat-checkbox formControlName="showBannerOn429"
|
|
1071
1301
|
>Exibir banner quando atingir o limite</mat-checkbox
|
|
1072
1302
|
>
|
|
@@ -1082,13 +1312,40 @@ class PraxisFilesUploadConfigEditor {
|
|
|
1082
1312
|
<input matInput type="number" formControlName="baseBackoffMs" />
|
|
1083
1313
|
</mat-form-field>
|
|
1084
1314
|
</form>
|
|
1315
|
+
</section>
|
|
1316
|
+
<section class="config-section">
|
|
1317
|
+
<form [formGroup]="bulkGroup">
|
|
1318
|
+
<h4 class="section-subtitle">
|
|
1319
|
+
<mat-icon aria-hidden="true">bolt</mat-icon>
|
|
1320
|
+
Opções configuráveis — Execução do lote
|
|
1321
|
+
</h4>
|
|
1322
|
+
<mat-checkbox formControlName="failFast"
|
|
1323
|
+
>Parar no primeiro erro (fail-fast)</mat-checkbox
|
|
1324
|
+
>
|
|
1325
|
+
</form>
|
|
1326
|
+
</section>
|
|
1327
|
+
</div>
|
|
1085
1328
|
</mat-tab>
|
|
1086
1329
|
<mat-tab label="Mensagens">
|
|
1330
|
+
<div class="tab-panel">
|
|
1331
|
+
<section class="tab-intro">
|
|
1332
|
+
<h3>Mensagens orientadas à operação</h3>
|
|
1333
|
+
<p>
|
|
1334
|
+
Ajuste o texto exibido ao usuário final. Priorize clareza,
|
|
1335
|
+
consistência e linguagem acionável.
|
|
1336
|
+
</p>
|
|
1337
|
+
</section>
|
|
1338
|
+
<section class="config-section">
|
|
1087
1339
|
<form [formGroup]="messagesGroup">
|
|
1088
1340
|
<h4 class="section-subtitle">
|
|
1089
1341
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
1090
1342
|
Opções configuráveis — Mensagens (UI)
|
|
1091
1343
|
</h4>
|
|
1344
|
+
<p class="section-note">
|
|
1345
|
+
Mensagens curtas e objetivas funcionam melhor em contextos de alta
|
|
1346
|
+
frequência. Use este grupo para adequar a voz da interface ao seu
|
|
1347
|
+
ambiente.
|
|
1348
|
+
</p>
|
|
1092
1349
|
<mat-form-field appearance="fill">
|
|
1093
1350
|
<mat-label>Sucesso (individual) <span class="opt-tag">opcional</span></mat-label>
|
|
1094
1351
|
<input
|
|
@@ -1115,8 +1372,19 @@ class PraxisFilesUploadConfigEditor {
|
|
|
1115
1372
|
</ng-container>
|
|
1116
1373
|
</div>
|
|
1117
1374
|
</form>
|
|
1375
|
+
</section>
|
|
1376
|
+
</div>
|
|
1118
1377
|
</mat-tab>
|
|
1119
1378
|
<mat-tab label="Cabeçalhos">
|
|
1379
|
+
<div class="tab-panel">
|
|
1380
|
+
<section class="tab-intro">
|
|
1381
|
+
<h3>Contexto de tenant e usuário</h3>
|
|
1382
|
+
<p>
|
|
1383
|
+
Use cabeçalhos para consultar configuração efetiva do servidor no
|
|
1384
|
+
mesmo contexto que o componente usará em produção.
|
|
1385
|
+
</p>
|
|
1386
|
+
</section>
|
|
1387
|
+
<section class="config-section">
|
|
1120
1388
|
<form [formGroup]="headersGroup">
|
|
1121
1389
|
<h4 class="section-subtitle">
|
|
1122
1390
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
@@ -1151,8 +1419,19 @@ class PraxisFilesUploadConfigEditor {
|
|
|
1151
1419
|
<input matInput formControlName="userValue" placeholder="ex.: 42" />
|
|
1152
1420
|
</mat-form-field>
|
|
1153
1421
|
</form>
|
|
1422
|
+
</section>
|
|
1423
|
+
</div>
|
|
1154
1424
|
</mat-tab>
|
|
1155
1425
|
<mat-tab label="Servidor">
|
|
1426
|
+
<div class="tab-panel">
|
|
1427
|
+
<section class="tab-intro">
|
|
1428
|
+
<h3>Contrato efetivo retornado pelo backend</h3>
|
|
1429
|
+
<p>
|
|
1430
|
+
Esta aba é a fonte de verdade do ambiente ativo. Compare o resumo
|
|
1431
|
+
do servidor com o que foi configurado nos formulários anteriores.
|
|
1432
|
+
</p>
|
|
1433
|
+
</section>
|
|
1434
|
+
<section class="config-section">
|
|
1156
1435
|
<div class="server-tab">
|
|
1157
1436
|
<div class="toolbar">
|
|
1158
1437
|
<h4 class="section-subtitle ro">
|
|
@@ -1244,17 +1523,36 @@ class PraxisFilesUploadConfigEditor {
|
|
|
1244
1523
|
</ng-template>
|
|
1245
1524
|
</ng-template>
|
|
1246
1525
|
</div>
|
|
1526
|
+
</section>
|
|
1527
|
+
</div>
|
|
1247
1528
|
</mat-tab>
|
|
1248
1529
|
<mat-tab label="JSON">
|
|
1530
|
+
<div class="tab-panel">
|
|
1531
|
+
<section class="tab-intro">
|
|
1532
|
+
<h3>Modo avançado</h3>
|
|
1533
|
+
<p>
|
|
1534
|
+
Edite o payload bruto apenas quando precisar de controle fino.
|
|
1535
|
+
Alterações aqui exigem mais rigor de revisão e validação.
|
|
1536
|
+
</p>
|
|
1537
|
+
</section>
|
|
1538
|
+
<div class="advanced-note">
|
|
1539
|
+
O JSON é uma visão de baixo nível da configuração. Prefira as abas
|
|
1540
|
+
guiadas sempre que possível para reduzir erro humano e manter a
|
|
1541
|
+
configuração auditável.
|
|
1542
|
+
</div>
|
|
1249
1543
|
<textarea
|
|
1544
|
+
class="json-textarea"
|
|
1250
1545
|
rows="10"
|
|
1251
1546
|
[ngModel]="form.value | json"
|
|
1252
1547
|
(ngModelChange)="onJsonChange($event)"
|
|
1253
1548
|
></textarea>
|
|
1254
1549
|
<div class="error" *ngIf="jsonError">{{ jsonError }}</div>
|
|
1550
|
+
</div>
|
|
1255
1551
|
</mat-tab>
|
|
1256
1552
|
</mat-tab-group>
|
|
1257
|
-
|
|
1553
|
+
</div>
|
|
1554
|
+
</div>
|
|
1555
|
+
`, isInline: true, styles: [".editor-layout{display:grid;grid-template-columns:minmax(260px,320px) minmax(0,1fr);gap:16px;align-items:start}.summary-panel{position:sticky;top:0;display:flex;flex-direction:column;gap:12px;padding:16px;border:1px solid var(--pfx-surface-border, #d8d8d8);border-radius:16px;background:linear-gradient(180deg,#fafbfc,#fff)}.summary-panel h3{margin:0;font-size:1rem;font-weight:600}.summary-panel .summary-intro{margin:0;color:#000000a6;line-height:1.4;font-size:.88rem}.summary-grid{display:grid;gap:10px}.summary-card{padding:12px;border-radius:12px;background:#fff;border:1px solid #e7e9ee}.summary-card .eyebrow{display:block;margin-bottom:4px;font-size:.72rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#5f6b7a}.summary-card strong{display:block;margin-bottom:4px;font-size:.95rem}.summary-card p{margin:0;font-size:.85rem;line-height:1.45;color:#000000b8}.risk-panel{padding:12px;border-radius:12px;background:#fff8e8;border:1px solid #f0d29a}.risk-panel.safe{background:#f4fbf6;border-color:#b8ddc1}.risk-panel h4{display:flex;align-items:center;gap:6px;margin:0 0 8px;font-size:.92rem}.risk-list{display:grid;gap:8px}.risk-item{padding:10px;border-radius:10px;background:#ffffffb8}.risk-item strong{display:block;margin-bottom:2px;font-size:.84rem}.risk-item p,.risk-panel .safe-copy{margin:0;font-size:.82rem;line-height:1.4;color:#000000b8}.editor-main{min-width:0}.tab-panel{display:grid;gap:16px;padding-top:8px}.tab-intro{padding:16px 18px;border:1px solid #e5e8ef;border-radius:16px;background:linear-gradient(180deg,#fff,#f7f9fc)}.tab-intro h3{margin:0 0 6px;font-size:1rem;font-weight:600}.tab-intro p{margin:0;font-size:.9rem;line-height:1.5;color:#000000b8}.config-section{padding:18px;border:1px solid #e5e8ef;border-radius:16px;background:#fff}.config-section+.config-section{margin-top:0}.section-note{margin:0 0 16px;color:#000000b3;font-size:.88rem;line-height:1.45}.advanced-note{padding:14px 16px;border-radius:14px;border:1px solid #f0d29a;background:#fff8e8;color:#000000c7;font-size:.88rem;line-height:1.45}.json-textarea{width:100%;min-height:280px;padding:14px 16px;border-radius:14px;border:1px solid #d7dce5;background:#0f172a;color:#e5eefc;font:500 .84rem/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;resize:vertical}.server-tab{display:grid;gap:16px}.toolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.summary{display:grid;gap:8px;padding-left:18px}.summary li{line-height:1.45}details{margin-top:12px;padding:14px 16px;border:1px solid #e5e8ef;border-radius:14px;background:#fbfcfe}details pre{white-space:pre-wrap;word-break:break-word}@media(max-width:1100px){.editor-layout{grid-template-columns:1fr}.summary-panel{position:static}}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i9.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i9.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$1.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "ngmodule", type: MatTabsModule }, { kind: "component", type: i4.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i4.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "ngmodule", type: MatFormFieldModule }, { kind: "component", type: i5.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i5.MatLabel, selector: "mat-label" }, { kind: "directive", type: i5.MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "ngmodule", type: MatInputModule }, { kind: "directive", type: i6.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "ngmodule", type: MatCheckboxModule }, { kind: "component", type: i7.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "ngmodule", type: MatSelectModule }, { kind: "component", type: i8.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i8.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i11.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i10.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "ngmodule", type: MatSnackBarModule }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1$1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "pipe", type: i9.JsonPipe, name: "json" }] });
|
|
1258
1556
|
}
|
|
1259
1557
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilesUploadConfigEditor, decorators: [{
|
|
1260
1558
|
type: Component,
|
|
@@ -1272,8 +1570,73 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1272
1570
|
MatSnackBarModule,
|
|
1273
1571
|
FormsModule,
|
|
1274
1572
|
], template: `
|
|
1573
|
+
<div class="editor-layout">
|
|
1574
|
+
<aside class="summary-panel" aria-label="Resumo executivo da configuração">
|
|
1575
|
+
<h3>Resumo executivo</h3>
|
|
1576
|
+
<p class="summary-intro">
|
|
1577
|
+
Revise aqui o impacto operacional antes de percorrer todos os grupos
|
|
1578
|
+
de configuração.
|
|
1579
|
+
</p>
|
|
1580
|
+
|
|
1581
|
+
<div class="summary-grid">
|
|
1582
|
+
<section class="summary-card">
|
|
1583
|
+
<span class="eyebrow">Estratégia</span>
|
|
1584
|
+
<strong>{{ summaryStrategyTitle }}</strong>
|
|
1585
|
+
<p>{{ summaryStrategyDetail }}</p>
|
|
1586
|
+
</section>
|
|
1587
|
+
|
|
1588
|
+
<section class="summary-card">
|
|
1589
|
+
<span class="eyebrow">Experiência</span>
|
|
1590
|
+
<strong>{{ summaryExperienceTitle }}</strong>
|
|
1591
|
+
<p>{{ summaryExperienceDetail }}</p>
|
|
1592
|
+
</section>
|
|
1593
|
+
|
|
1594
|
+
<section class="summary-card">
|
|
1595
|
+
<span class="eyebrow">Limites</span>
|
|
1596
|
+
<strong>{{ summaryLimitsTitle }}</strong>
|
|
1597
|
+
<p>{{ summaryLimitsDetail }}</p>
|
|
1598
|
+
</section>
|
|
1599
|
+
|
|
1600
|
+
<section class="summary-card">
|
|
1601
|
+
<span class="eyebrow">Servidor</span>
|
|
1602
|
+
<strong>{{ summaryServerTitle }}</strong>
|
|
1603
|
+
<p>{{ summaryServerDetail }}</p>
|
|
1604
|
+
</section>
|
|
1605
|
+
</div>
|
|
1606
|
+
|
|
1607
|
+
<section class="risk-panel" [class.safe]="activeRisks.length === 0">
|
|
1608
|
+
<h4>
|
|
1609
|
+
<mat-icon aria-hidden="true">{{
|
|
1610
|
+
activeRisks.length === 0 ? 'verified' : 'warning'
|
|
1611
|
+
}}</mat-icon>
|
|
1612
|
+
Riscos e atenção
|
|
1613
|
+
</h4>
|
|
1614
|
+
<div *ngIf="activeRisks.length > 0; else noRiskState" class="risk-list">
|
|
1615
|
+
<article class="risk-item" *ngFor="let risk of activeRisks">
|
|
1616
|
+
<strong>{{ risk.title }}</strong>
|
|
1617
|
+
<p>{{ risk.detail }}</p>
|
|
1618
|
+
</article>
|
|
1619
|
+
</div>
|
|
1620
|
+
<ng-template #noRiskState>
|
|
1621
|
+
<p class="safe-copy">
|
|
1622
|
+
Nenhum risco operacional evidente na configuração atual.
|
|
1623
|
+
</p>
|
|
1624
|
+
</ng-template>
|
|
1625
|
+
</section>
|
|
1626
|
+
</aside>
|
|
1627
|
+
|
|
1628
|
+
<div class="editor-main">
|
|
1275
1629
|
<mat-tab-group>
|
|
1276
1630
|
<mat-tab label="Comportamento">
|
|
1631
|
+
<div class="tab-panel">
|
|
1632
|
+
<section class="tab-intro">
|
|
1633
|
+
<h3>Estratégia e cadência do envio</h3>
|
|
1634
|
+
<p>
|
|
1635
|
+
Defina aqui como o usuário inicia o upload e como o lote se
|
|
1636
|
+
comporta em termos de paralelismo e retentativa.
|
|
1637
|
+
</p>
|
|
1638
|
+
</section>
|
|
1639
|
+
<section class="config-section">
|
|
1277
1640
|
<form [formGroup]="form">
|
|
1278
1641
|
<mat-form-field appearance="fill">
|
|
1279
1642
|
<mat-label>Estratégia de envio <span class="opt-tag">opcional</span></mat-label>
|
|
@@ -1296,11 +1659,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1296
1659
|
</button>
|
|
1297
1660
|
</mat-form-field>
|
|
1298
1661
|
</form>
|
|
1662
|
+
</section>
|
|
1663
|
+
<section class="config-section">
|
|
1299
1664
|
<form [formGroup]="bulkGroup">
|
|
1300
1665
|
<h4 class="section-subtitle">
|
|
1301
1666
|
<mat-icon aria-hidden="true">build</mat-icon>
|
|
1302
1667
|
Opções configuráveis — Lote
|
|
1303
1668
|
</h4>
|
|
1669
|
+
<p class="section-note">
|
|
1670
|
+
Use paralelismo e retries com moderação. Em ambientes corporativos,
|
|
1671
|
+
esses controles afetam throughput, experiência percebida e carga no
|
|
1672
|
+
backend.
|
|
1673
|
+
</p>
|
|
1304
1674
|
<mat-form-field appearance="fill">
|
|
1305
1675
|
<mat-label>Uploads paralelos <span class="opt-tag">opcional</span></mat-label>
|
|
1306
1676
|
<input matInput type="number" formControlName="parallelUploads" />
|
|
@@ -1344,13 +1714,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1344
1714
|
</button>
|
|
1345
1715
|
</mat-form-field>
|
|
1346
1716
|
</form>
|
|
1717
|
+
</section>
|
|
1718
|
+
</div>
|
|
1347
1719
|
</mat-tab>
|
|
1348
1720
|
<mat-tab label="Interface">
|
|
1721
|
+
<div class="tab-panel">
|
|
1722
|
+
<section class="tab-intro">
|
|
1723
|
+
<h3>Experiência visível para o usuário</h3>
|
|
1724
|
+
<p>
|
|
1725
|
+
Configure densidade, dropzone, metadados e detalhes da lista.
|
|
1726
|
+
Priorize clareza operacional antes de habilitar recursos avançados.
|
|
1727
|
+
</p>
|
|
1728
|
+
</section>
|
|
1729
|
+
<section class="config-section">
|
|
1349
1730
|
<form [formGroup]="uiGroup">
|
|
1350
1731
|
<h4 class="section-subtitle">
|
|
1351
1732
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
1352
1733
|
Opções configuráveis — Interface
|
|
1353
1734
|
</h4>
|
|
1735
|
+
<p class="section-note">
|
|
1736
|
+
Esta seção controla a ergonomia do componente. Mudanças aqui afetam
|
|
1737
|
+
descoberta, esforço de uso e taxa de conclusão do envio.
|
|
1738
|
+
</p>
|
|
1354
1739
|
<mat-checkbox formControlName="showDropzone"
|
|
1355
1740
|
>Exibir área de soltar</mat-checkbox
|
|
1356
1741
|
>
|
|
@@ -1464,13 +1849,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1464
1849
|
</mat-form-field>
|
|
1465
1850
|
</fieldset>
|
|
1466
1851
|
</form>
|
|
1852
|
+
</section>
|
|
1853
|
+
</div>
|
|
1467
1854
|
</mat-tab>
|
|
1468
1855
|
<mat-tab label="Validações">
|
|
1856
|
+
<div class="tab-panel">
|
|
1857
|
+
<section class="tab-intro">
|
|
1858
|
+
<h3>Regras de aceite e comportamento operacional</h3>
|
|
1859
|
+
<p>
|
|
1860
|
+
Separe mentalmente o que é validação de UX local do que é regra
|
|
1861
|
+
efetiva de backend. Revise com atenção as opções destrutivas.
|
|
1862
|
+
</p>
|
|
1863
|
+
</section>
|
|
1864
|
+
<section class="config-section">
|
|
1469
1865
|
<form [formGroup]="limitsGroup">
|
|
1470
1866
|
<h4 class="section-subtitle">
|
|
1471
1867
|
<mat-icon aria-hidden="true">build</mat-icon>
|
|
1472
1868
|
Opções configuráveis — Validações
|
|
1473
1869
|
</h4>
|
|
1870
|
+
<p class="section-note">
|
|
1871
|
+
Limites locais melhoram feedback imediato, mas não substituem a
|
|
1872
|
+
política efetiva do servidor.
|
|
1873
|
+
</p>
|
|
1474
1874
|
<mat-form-field appearance="fill">
|
|
1475
1875
|
<mat-label>Tamanho máximo do arquivo (bytes) <span class="opt-tag">opcional</span></mat-label>
|
|
1476
1876
|
<input matInput type="number" formControlName="maxFileSizeBytes" />
|
|
@@ -1493,6 +1893,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1493
1893
|
<mat-label>Tamanho máximo do lote (bytes) <span class="opt-tag">opcional</span></mat-label>
|
|
1494
1894
|
<input matInput type="number" formControlName="maxBulkSizeBytes" />
|
|
1495
1895
|
</mat-form-field>
|
|
1896
|
+
</form>
|
|
1897
|
+
</section>
|
|
1898
|
+
<section class="config-section">
|
|
1899
|
+
<form [formGroup]="optionsGroup">
|
|
1900
|
+
<h4 class="section-subtitle">
|
|
1901
|
+
<mat-icon aria-hidden="true">tune</mat-icon>
|
|
1902
|
+
Opções configuráveis — Backend
|
|
1903
|
+
</h4>
|
|
1904
|
+
<p class="section-note">
|
|
1905
|
+
Estas opções têm impacto direto no contrato com o servidor e em
|
|
1906
|
+
governança operacional. Trate alterações aqui como decisão de
|
|
1907
|
+
política, não apenas de interface.
|
|
1908
|
+
</p>
|
|
1496
1909
|
<mat-form-field appearance="fill">
|
|
1497
1910
|
<mat-label>Política de conflito (padrão) <span class="opt-tag">opcional</span></mat-label>
|
|
1498
1911
|
<mat-select formControlName="defaultConflictPolicy">
|
|
@@ -1512,14 +1925,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1512
1925
|
>
|
|
1513
1926
|
<mat-icon>help_outline</mat-icon>
|
|
1514
1927
|
</button>
|
|
1515
|
-
<div class="warn" *ngIf="
|
|
1928
|
+
<div class="warn" *ngIf="optionsGroup.get('defaultConflictPolicy')?.value === 'OVERWRITE'">
|
|
1516
1929
|
<mat-icon color="warn" aria-hidden="true">warning</mat-icon>
|
|
1517
1930
|
Atenção: OVERWRITE pode sobrescrever arquivos existentes.
|
|
1518
1931
|
</div>
|
|
1519
1932
|
</mat-form-field>
|
|
1520
|
-
<mat-checkbox formControlName="failFast"
|
|
1521
|
-
>Parar no primeiro erro (fail-fast)</mat-checkbox
|
|
1522
|
-
>
|
|
1523
1933
|
<mat-checkbox formControlName="strictValidation"
|
|
1524
1934
|
>Validação rigorosa (backend)</mat-checkbox
|
|
1525
1935
|
>
|
|
@@ -1537,12 +1947,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1537
1947
|
<mat-icon>help_outline</mat-icon>
|
|
1538
1948
|
</button>
|
|
1539
1949
|
</mat-form-field>
|
|
1540
|
-
</form>
|
|
1541
|
-
<form [formGroup]="optionsGroup">
|
|
1542
|
-
<h4 class="section-subtitle">
|
|
1543
|
-
<mat-icon aria-hidden="true">tune</mat-icon>
|
|
1544
|
-
Opções configuráveis — Avançado
|
|
1545
|
-
</h4>
|
|
1546
1950
|
<mat-form-field appearance="fill">
|
|
1547
1951
|
<mat-label>Extensões permitidas <span class="opt-tag">opcional</span></mat-label>
|
|
1548
1952
|
<input
|
|
@@ -1595,6 +1999,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1595
1999
|
Pode impactar desempenho e latência de upload.
|
|
1596
2000
|
</div>
|
|
1597
2001
|
</form>
|
|
2002
|
+
</section>
|
|
2003
|
+
<section class="config-section">
|
|
1598
2004
|
<form [formGroup]="quotasGroup">
|
|
1599
2005
|
<h4 class="section-subtitle">
|
|
1600
2006
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
@@ -1607,11 +2013,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1607
2013
|
>Bloquear ao exceder cota</mat-checkbox
|
|
1608
2014
|
>
|
|
1609
2015
|
</form>
|
|
2016
|
+
</section>
|
|
2017
|
+
<section class="config-section">
|
|
1610
2018
|
<form [formGroup]="rateLimitGroup">
|
|
1611
2019
|
<h4 class="section-subtitle">
|
|
1612
2020
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
1613
2021
|
Opções configuráveis — Rate Limit (UI)
|
|
1614
2022
|
</h4>
|
|
2023
|
+
<p class="section-note">
|
|
2024
|
+
Prefira poucos retries automáticos. Mais tentativas aliviam atrito
|
|
2025
|
+
imediato, mas podem mascarar gargalos reais do ambiente.
|
|
2026
|
+
</p>
|
|
1615
2027
|
<mat-checkbox formControlName="showBannerOn429"
|
|
1616
2028
|
>Exibir banner quando atingir o limite</mat-checkbox
|
|
1617
2029
|
>
|
|
@@ -1627,13 +2039,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1627
2039
|
<input matInput type="number" formControlName="baseBackoffMs" />
|
|
1628
2040
|
</mat-form-field>
|
|
1629
2041
|
</form>
|
|
2042
|
+
</section>
|
|
2043
|
+
<section class="config-section">
|
|
2044
|
+
<form [formGroup]="bulkGroup">
|
|
2045
|
+
<h4 class="section-subtitle">
|
|
2046
|
+
<mat-icon aria-hidden="true">bolt</mat-icon>
|
|
2047
|
+
Opções configuráveis — Execução do lote
|
|
2048
|
+
</h4>
|
|
2049
|
+
<mat-checkbox formControlName="failFast"
|
|
2050
|
+
>Parar no primeiro erro (fail-fast)</mat-checkbox
|
|
2051
|
+
>
|
|
2052
|
+
</form>
|
|
2053
|
+
</section>
|
|
2054
|
+
</div>
|
|
1630
2055
|
</mat-tab>
|
|
1631
2056
|
<mat-tab label="Mensagens">
|
|
2057
|
+
<div class="tab-panel">
|
|
2058
|
+
<section class="tab-intro">
|
|
2059
|
+
<h3>Mensagens orientadas à operação</h3>
|
|
2060
|
+
<p>
|
|
2061
|
+
Ajuste o texto exibido ao usuário final. Priorize clareza,
|
|
2062
|
+
consistência e linguagem acionável.
|
|
2063
|
+
</p>
|
|
2064
|
+
</section>
|
|
2065
|
+
<section class="config-section">
|
|
1632
2066
|
<form [formGroup]="messagesGroup">
|
|
1633
2067
|
<h4 class="section-subtitle">
|
|
1634
2068
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
1635
2069
|
Opções configuráveis — Mensagens (UI)
|
|
1636
2070
|
</h4>
|
|
2071
|
+
<p class="section-note">
|
|
2072
|
+
Mensagens curtas e objetivas funcionam melhor em contextos de alta
|
|
2073
|
+
frequência. Use este grupo para adequar a voz da interface ao seu
|
|
2074
|
+
ambiente.
|
|
2075
|
+
</p>
|
|
1637
2076
|
<mat-form-field appearance="fill">
|
|
1638
2077
|
<mat-label>Sucesso (individual) <span class="opt-tag">opcional</span></mat-label>
|
|
1639
2078
|
<input
|
|
@@ -1660,8 +2099,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1660
2099
|
</ng-container>
|
|
1661
2100
|
</div>
|
|
1662
2101
|
</form>
|
|
2102
|
+
</section>
|
|
2103
|
+
</div>
|
|
1663
2104
|
</mat-tab>
|
|
1664
2105
|
<mat-tab label="Cabeçalhos">
|
|
2106
|
+
<div class="tab-panel">
|
|
2107
|
+
<section class="tab-intro">
|
|
2108
|
+
<h3>Contexto de tenant e usuário</h3>
|
|
2109
|
+
<p>
|
|
2110
|
+
Use cabeçalhos para consultar configuração efetiva do servidor no
|
|
2111
|
+
mesmo contexto que o componente usará em produção.
|
|
2112
|
+
</p>
|
|
2113
|
+
</section>
|
|
2114
|
+
<section class="config-section">
|
|
1665
2115
|
<form [formGroup]="headersGroup">
|
|
1666
2116
|
<h4 class="section-subtitle">
|
|
1667
2117
|
<mat-icon aria-hidden="true">edit</mat-icon>
|
|
@@ -1696,8 +2146,19 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1696
2146
|
<input matInput formControlName="userValue" placeholder="ex.: 42" />
|
|
1697
2147
|
</mat-form-field>
|
|
1698
2148
|
</form>
|
|
2149
|
+
</section>
|
|
2150
|
+
</div>
|
|
1699
2151
|
</mat-tab>
|
|
1700
2152
|
<mat-tab label="Servidor">
|
|
2153
|
+
<div class="tab-panel">
|
|
2154
|
+
<section class="tab-intro">
|
|
2155
|
+
<h3>Contrato efetivo retornado pelo backend</h3>
|
|
2156
|
+
<p>
|
|
2157
|
+
Esta aba é a fonte de verdade do ambiente ativo. Compare o resumo
|
|
2158
|
+
do servidor com o que foi configurado nos formulários anteriores.
|
|
2159
|
+
</p>
|
|
2160
|
+
</section>
|
|
2161
|
+
<section class="config-section">
|
|
1701
2162
|
<div class="server-tab">
|
|
1702
2163
|
<div class="toolbar">
|
|
1703
2164
|
<h4 class="section-subtitle ro">
|
|
@@ -1789,22 +2250,272 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
1789
2250
|
</ng-template>
|
|
1790
2251
|
</ng-template>
|
|
1791
2252
|
</div>
|
|
2253
|
+
</section>
|
|
2254
|
+
</div>
|
|
1792
2255
|
</mat-tab>
|
|
1793
2256
|
<mat-tab label="JSON">
|
|
2257
|
+
<div class="tab-panel">
|
|
2258
|
+
<section class="tab-intro">
|
|
2259
|
+
<h3>Modo avançado</h3>
|
|
2260
|
+
<p>
|
|
2261
|
+
Edite o payload bruto apenas quando precisar de controle fino.
|
|
2262
|
+
Alterações aqui exigem mais rigor de revisão e validação.
|
|
2263
|
+
</p>
|
|
2264
|
+
</section>
|
|
2265
|
+
<div class="advanced-note">
|
|
2266
|
+
O JSON é uma visão de baixo nível da configuração. Prefira as abas
|
|
2267
|
+
guiadas sempre que possível para reduzir erro humano e manter a
|
|
2268
|
+
configuração auditável.
|
|
2269
|
+
</div>
|
|
1794
2270
|
<textarea
|
|
2271
|
+
class="json-textarea"
|
|
1795
2272
|
rows="10"
|
|
1796
2273
|
[ngModel]="form.value | json"
|
|
1797
2274
|
(ngModelChange)="onJsonChange($event)"
|
|
1798
2275
|
></textarea>
|
|
1799
2276
|
<div class="error" *ngIf="jsonError">{{ jsonError }}</div>
|
|
2277
|
+
</div>
|
|
1800
2278
|
</mat-tab>
|
|
1801
2279
|
</mat-tab-group>
|
|
1802
|
-
|
|
2280
|
+
</div>
|
|
2281
|
+
</div>
|
|
2282
|
+
`, styles: [".editor-layout{display:grid;grid-template-columns:minmax(260px,320px) minmax(0,1fr);gap:16px;align-items:start}.summary-panel{position:sticky;top:0;display:flex;flex-direction:column;gap:12px;padding:16px;border:1px solid var(--pfx-surface-border, #d8d8d8);border-radius:16px;background:linear-gradient(180deg,#fafbfc,#fff)}.summary-panel h3{margin:0;font-size:1rem;font-weight:600}.summary-panel .summary-intro{margin:0;color:#000000a6;line-height:1.4;font-size:.88rem}.summary-grid{display:grid;gap:10px}.summary-card{padding:12px;border-radius:12px;background:#fff;border:1px solid #e7e9ee}.summary-card .eyebrow{display:block;margin-bottom:4px;font-size:.72rem;font-weight:700;letter-spacing:.04em;text-transform:uppercase;color:#5f6b7a}.summary-card strong{display:block;margin-bottom:4px;font-size:.95rem}.summary-card p{margin:0;font-size:.85rem;line-height:1.45;color:#000000b8}.risk-panel{padding:12px;border-radius:12px;background:#fff8e8;border:1px solid #f0d29a}.risk-panel.safe{background:#f4fbf6;border-color:#b8ddc1}.risk-panel h4{display:flex;align-items:center;gap:6px;margin:0 0 8px;font-size:.92rem}.risk-list{display:grid;gap:8px}.risk-item{padding:10px;border-radius:10px;background:#ffffffb8}.risk-item strong{display:block;margin-bottom:2px;font-size:.84rem}.risk-item p,.risk-panel .safe-copy{margin:0;font-size:.82rem;line-height:1.4;color:#000000b8}.editor-main{min-width:0}.tab-panel{display:grid;gap:16px;padding-top:8px}.tab-intro{padding:16px 18px;border:1px solid #e5e8ef;border-radius:16px;background:linear-gradient(180deg,#fff,#f7f9fc)}.tab-intro h3{margin:0 0 6px;font-size:1rem;font-weight:600}.tab-intro p{margin:0;font-size:.9rem;line-height:1.5;color:#000000b8}.config-section{padding:18px;border:1px solid #e5e8ef;border-radius:16px;background:#fff}.config-section+.config-section{margin-top:0}.section-note{margin:0 0 16px;color:#000000b3;font-size:.88rem;line-height:1.45}.advanced-note{padding:14px 16px;border-radius:14px;border:1px solid #f0d29a;background:#fff8e8;color:#000000c7;font-size:.88rem;line-height:1.45}.json-textarea{width:100%;min-height:280px;padding:14px 16px;border-radius:14px;border:1px solid #d7dce5;background:#0f172a;color:#e5eefc;font:500 .84rem/1.55 ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;resize:vertical}.server-tab{display:grid;gap:16px}.toolbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.summary{display:grid;gap:8px;padding-left:18px}.summary li{line-height:1.45}details{margin-top:12px;padding:14px 16px;border:1px solid #e5e8ef;border-radius:14px;background:#fbfcfe}details pre{white-space:pre-wrap;word-break:break-word}@media(max-width:1100px){.editor-layout{grid-template-columns:1fr}.summary-panel{position:static}}.help-icon-button{--mdc-icon-button-state-layer-size: 28px;--mdc-icon-button-icon-size: 18px;width:28px;height:28px;padding:0;display:inline-flex;align-items:center;justify-content:center;margin-right:-4px}.help-icon-button mat-icon{font-size:18px;width:18px;height:18px}.mat-mdc-form-field-icon-suffix{align-self:center}\n"] }]
|
|
1803
2283
|
}], ctorParameters: () => [{ type: i1$1.FormBuilder }, { type: undefined, decorators: [{
|
|
1804
2284
|
type: Inject,
|
|
1805
2285
|
args: [SETTINGS_PANEL_DATA]
|
|
1806
2286
|
}] }, { type: i2.MatSnackBar }, { type: i0.DestroyRef }] });
|
|
1807
2287
|
|
|
2288
|
+
const FILES_UPLOAD_EN_US = {
|
|
2289
|
+
'praxis.filesUpload.settingsAriaLabel': 'Open settings',
|
|
2290
|
+
'praxis.filesUpload.dropzoneLabel': 'Drag files or',
|
|
2291
|
+
'praxis.filesUpload.dropzoneButton': 'select',
|
|
2292
|
+
'praxis.filesUpload.conflictPolicyLabel': 'Conflict policy',
|
|
2293
|
+
'praxis.filesUpload.metadataLabel': 'Metadata (JSON)',
|
|
2294
|
+
'praxis.filesUpload.progressAriaLabel': 'Upload progress',
|
|
2295
|
+
'praxis.filesUpload.rateLimitBanner': 'Rate limit exceeded. Try again at',
|
|
2296
|
+
'praxis.filesUpload.settingsTitle': 'Files Upload Configuration',
|
|
2297
|
+
'praxis.filesUpload.statusSuccess': 'Uploaded',
|
|
2298
|
+
'praxis.filesUpload.statusError': 'Error',
|
|
2299
|
+
'praxis.filesUpload.invalidMetadata': 'Invalid metadata.',
|
|
2300
|
+
'praxis.filesUpload.genericUploadError': 'File upload error.',
|
|
2301
|
+
'praxis.filesUpload.acceptError': 'File type not allowed.',
|
|
2302
|
+
'praxis.filesUpload.maxFileSizeError': 'File exceeds the maximum size.',
|
|
2303
|
+
'praxis.filesUpload.maxFilesPerBulkError': 'Too many files selected.',
|
|
2304
|
+
'praxis.filesUpload.maxBulkSizeError': 'Total files size exceeded.',
|
|
2305
|
+
'praxis.filesUpload.serviceUnavailable': 'Upload service unavailable.',
|
|
2306
|
+
'praxis.filesUpload.baseUrlMissing': 'Base URL not configured. Provide [baseUrl] to enable uploads.',
|
|
2307
|
+
'praxis.filesUpload.selectFiles': 'Select file(s)',
|
|
2308
|
+
'praxis.filesUpload.remove': 'Remove',
|
|
2309
|
+
'praxis.filesUpload.moreActions': 'More actions',
|
|
2310
|
+
'praxis.filesUpload.policySummaryAction': 'Information about upload policies',
|
|
2311
|
+
'praxis.filesUpload.retry': 'Retry',
|
|
2312
|
+
'praxis.filesUpload.download': 'Download',
|
|
2313
|
+
'praxis.filesUpload.copyLink': 'Copy link',
|
|
2314
|
+
'praxis.filesUpload.details': 'Details',
|
|
2315
|
+
'praxis.filesUpload.detailsMetadata': 'Metadata',
|
|
2316
|
+
'praxis.filesUpload.dropzoneHint': 'Drag and drop files here or click to select',
|
|
2317
|
+
'praxis.filesUpload.dropzoneProximityHint': 'Drop here to upload',
|
|
2318
|
+
'praxis.filesUpload.placeholder': 'Select or drop files…',
|
|
2319
|
+
'praxis.filesUpload.partialUploadError': 'Partial upload completed with failures in part of the batch.',
|
|
2320
|
+
'praxis.filesUpload.presignMetadataMissing': 'Presigned upload completed without file metadata.',
|
|
2321
|
+
'praxis.filesUpload.fieldChipAria': 'Additional files selected',
|
|
2322
|
+
'praxis.filesUpload.fieldPendingCountSuffix': 'file(s) selected',
|
|
2323
|
+
'praxis.filesUpload.removePendingFile': 'Remove selected file',
|
|
2324
|
+
'praxis.filesUpload.fieldPolicyTypes': 'Types',
|
|
2325
|
+
'praxis.filesUpload.fieldPolicyMaxPerFile': 'Max/file',
|
|
2326
|
+
'praxis.filesUpload.fieldPolicyMaxItems': 'Max/items',
|
|
2327
|
+
'praxis.filesUpload.fieldPolicyNotConfigured': 'Not configured',
|
|
2328
|
+
'praxis.filesUpload.sizeUnitBytes': 'Bytes',
|
|
2329
|
+
'praxis.filesUpload.sizeUnitKB': 'KB',
|
|
2330
|
+
'praxis.filesUpload.sizeUnitMB': 'MB',
|
|
2331
|
+
'praxis.filesUpload.sizeUnitGB': 'GB',
|
|
2332
|
+
'praxis.filesUpload.sizeUnitTB': 'TB',
|
|
2333
|
+
'praxis.filesUpload.errors.INVALID_FILE_TYPE': 'Invalid file type.',
|
|
2334
|
+
'praxis.filesUpload.errors.FILE_TOO_LARGE': 'File is too large.',
|
|
2335
|
+
'praxis.filesUpload.errors.NOT_FOUND': 'File not found.',
|
|
2336
|
+
'praxis.filesUpload.errors.UNAUTHORIZED': 'Unauthorized request.',
|
|
2337
|
+
'praxis.filesUpload.errors.RATE_LIMIT_EXCEEDED': 'Request limit exceeded.',
|
|
2338
|
+
'praxis.filesUpload.errors.INTERNAL_ERROR': 'Internal server error.',
|
|
2339
|
+
'praxis.filesUpload.errors.QUOTA_EXCEEDED': 'Quota exceeded.',
|
|
2340
|
+
'praxis.filesUpload.errors.SEC_VIRUS_DETECTED': 'Virus detected in file.',
|
|
2341
|
+
'praxis.filesUpload.errors.SEC_MALICIOUS_CONTENT': 'Malicious content detected.',
|
|
2342
|
+
'praxis.filesUpload.errors.SEC_DANGEROUS_TYPE': 'Dangerous file type.',
|
|
2343
|
+
'praxis.filesUpload.errors.FMT_MAGIC_MISMATCH': 'File content does not match the declared format.',
|
|
2344
|
+
'praxis.filesUpload.errors.FMT_CORRUPTED': 'Corrupted file.',
|
|
2345
|
+
'praxis.filesUpload.errors.FMT_UNSUPPORTED': 'Unsupported file format.',
|
|
2346
|
+
'praxis.filesUpload.errors.SYS_STORAGE_ERROR': 'Storage error.',
|
|
2347
|
+
'praxis.filesUpload.errors.SYS_SERVICE_DOWN': 'Service unavailable.',
|
|
2348
|
+
'praxis.filesUpload.errors.SYS_RATE_LIMIT': 'Request limit exceeded.',
|
|
2349
|
+
'praxis.filesUpload.errors.ARQUIVO_MUITO_GRANDE': 'File is too large.',
|
|
2350
|
+
'praxis.filesUpload.errors.TIPO_ARQUIVO_INVALIDO': 'Invalid file type.',
|
|
2351
|
+
'praxis.filesUpload.errors.TIPO_MIDIA_NAO_SUPORTADO': 'Unsupported media type.',
|
|
2352
|
+
'praxis.filesUpload.errors.CAMPO_OBRIGATORIO_AUSENTE': 'Required field is missing.',
|
|
2353
|
+
'praxis.filesUpload.errors.OPCOES_JSON_INVALIDAS': 'Invalid JSON options.',
|
|
2354
|
+
'praxis.filesUpload.errors.ARGUMENTO_INVALIDO': 'Invalid argument.',
|
|
2355
|
+
'praxis.filesUpload.errors.NAO_AUTORIZADO': 'Authentication required.',
|
|
2356
|
+
'praxis.filesUpload.errors.ERRO_INTERNO': 'Internal server error.',
|
|
2357
|
+
'praxis.filesUpload.errors.LIMITE_TAXA_EXCEDIDO': 'Rate limit exceeded.',
|
|
2358
|
+
'praxis.filesUpload.errors.COTA_EXCEDIDA': 'Quota exceeded.',
|
|
2359
|
+
'praxis.filesUpload.errors.ARQUIVO_JA_EXISTE': 'File already exists.',
|
|
2360
|
+
'praxis.filesUpload.errors.INVALID_JSON_OPTIONS': 'Invalid JSON options.',
|
|
2361
|
+
'praxis.filesUpload.errors.EMPTY_FILENAME': 'File name is required.',
|
|
2362
|
+
'praxis.filesUpload.errors.FILE_EXISTS': 'File already exists.',
|
|
2363
|
+
'praxis.filesUpload.errors.PATH_TRAVERSAL': 'Path traversal detected.',
|
|
2364
|
+
'praxis.filesUpload.errors.INSUFFICIENT_STORAGE': 'Insufficient storage.',
|
|
2365
|
+
'praxis.filesUpload.errors.UPLOAD_TIMEOUT': 'Upload timed out.',
|
|
2366
|
+
'praxis.filesUpload.errors.BULK_UPLOAD_TIMEOUT': 'Bulk upload timed out.',
|
|
2367
|
+
'praxis.filesUpload.errors.BULK_UPLOAD_CANCELLED': 'Bulk upload cancelled.',
|
|
2368
|
+
'praxis.filesUpload.errors.USER_CANCELLED': 'Upload cancelled by user.',
|
|
2369
|
+
'praxis.filesUpload.errors.UNKNOWN_ERROR': 'Unknown error.',
|
|
2370
|
+
'praxis.filesUpload.fieldPendingSummary': 'Files ready to upload',
|
|
2371
|
+
'praxis.filesUpload.fieldSelectedSummary': 'Selected file',
|
|
2372
|
+
'praxis.filesUpload.fieldSelectedPlural': 'files attached',
|
|
2373
|
+
'praxis.filesUpload.fieldEmptySummary': 'No file selected',
|
|
2374
|
+
'praxis.filesUpload.fieldErrorSummary': 'Fix the error to continue',
|
|
2375
|
+
'praxis.filesUpload.fieldActionUpload': 'Upload selected files',
|
|
2376
|
+
'praxis.filesUpload.fieldActionUploadShort': 'Upload',
|
|
2377
|
+
'praxis.filesUpload.fieldActionCancel': 'Cancel current selection',
|
|
2378
|
+
'praxis.filesUpload.fieldActionCancelShort': 'Cancel',
|
|
2379
|
+
'praxis.filesUpload.fieldActionSelect': 'Select file(s)',
|
|
2380
|
+
'praxis.filesUpload.fieldActionClear': 'Clear',
|
|
2381
|
+
'praxis.filesUpload.fieldInfoAction': 'Information',
|
|
2382
|
+
'praxis.filesUpload.fieldMoreActions': 'More actions',
|
|
2383
|
+
'praxis.filesUpload.fieldPendingAriaSuffix': 'files ready to upload',
|
|
2384
|
+
'praxis.filesUpload.selectedOverlayAriaLabel': 'Selected files',
|
|
2385
|
+
'praxis.filesUpload.selectedOverlayTitle': 'Selected',
|
|
2386
|
+
'praxis.filesUpload.bulkSummaryTotal': 'Total',
|
|
2387
|
+
'praxis.filesUpload.bulkSummarySuccess': 'Success',
|
|
2388
|
+
'praxis.filesUpload.bulkSummaryFailed': 'Failed',
|
|
2389
|
+
'praxis.filesUpload.resultMetaIdLabel': 'ID',
|
|
2390
|
+
'praxis.filesUpload.resultMetaTypeLabel': 'Type',
|
|
2391
|
+
'praxis.filesUpload.resultMetaSizeLabel': 'Size',
|
|
2392
|
+
'praxis.filesUpload.resultMetaUploadedAtLabel': 'Uploaded at',
|
|
2393
|
+
'praxis.filesUpload.policySummaryAny': 'any',
|
|
2394
|
+
'praxis.filesUpload.policySummaryNotConfigured': '—',
|
|
2395
|
+
'praxis.filesUpload.policySummaryTypesLabel': 'Types',
|
|
2396
|
+
'praxis.filesUpload.policySummaryMaxPerFileLabel': 'Max. per file',
|
|
2397
|
+
'praxis.filesUpload.policySummaryQuantityLabel': 'Qty',
|
|
2398
|
+
'praxis.filesUpload.showAll': 'Show all',
|
|
2399
|
+
'praxis.filesUpload.showLess': 'Show less',
|
|
2400
|
+
};
|
|
2401
|
+
|
|
2402
|
+
const FILES_UPLOAD_PT_BR = {
|
|
2403
|
+
'praxis.filesUpload.settingsAriaLabel': 'Abrir configurações',
|
|
2404
|
+
'praxis.filesUpload.dropzoneLabel': 'Arraste arquivos ou',
|
|
2405
|
+
'praxis.filesUpload.dropzoneButton': 'selecionar',
|
|
2406
|
+
'praxis.filesUpload.conflictPolicyLabel': 'Política de conflito',
|
|
2407
|
+
'praxis.filesUpload.metadataLabel': 'Metadados (JSON)',
|
|
2408
|
+
'praxis.filesUpload.progressAriaLabel': 'Progresso do upload',
|
|
2409
|
+
'praxis.filesUpload.rateLimitBanner': 'Limite de requisições excedido. Tente novamente às',
|
|
2410
|
+
'praxis.filesUpload.settingsTitle': 'Configuração de Upload de Arquivos',
|
|
2411
|
+
'praxis.filesUpload.statusSuccess': 'Enviado',
|
|
2412
|
+
'praxis.filesUpload.statusError': 'Erro',
|
|
2413
|
+
'praxis.filesUpload.invalidMetadata': 'Metadados inválidos.',
|
|
2414
|
+
'praxis.filesUpload.genericUploadError': 'Erro no envio de arquivo.',
|
|
2415
|
+
'praxis.filesUpload.acceptError': 'Tipo de arquivo não permitido.',
|
|
2416
|
+
'praxis.filesUpload.maxFileSizeError': 'Arquivo excede o tamanho máximo.',
|
|
2417
|
+
'praxis.filesUpload.maxFilesPerBulkError': 'Quantidade de arquivos excedida.',
|
|
2418
|
+
'praxis.filesUpload.maxBulkSizeError': 'Tamanho total dos arquivos excedido.',
|
|
2419
|
+
'praxis.filesUpload.serviceUnavailable': 'Serviço de upload indisponível.',
|
|
2420
|
+
'praxis.filesUpload.baseUrlMissing': 'Base URL não configurada. Informe [baseUrl] para habilitar o envio.',
|
|
2421
|
+
'praxis.filesUpload.selectFiles': 'Selecionar arquivo(s)',
|
|
2422
|
+
'praxis.filesUpload.remove': 'Remover',
|
|
2423
|
+
'praxis.filesUpload.moreActions': 'Mais ações',
|
|
2424
|
+
'praxis.filesUpload.policySummaryAction': 'Informações sobre políticas de upload',
|
|
2425
|
+
'praxis.filesUpload.retry': 'Reenviar',
|
|
2426
|
+
'praxis.filesUpload.download': 'Baixar',
|
|
2427
|
+
'praxis.filesUpload.copyLink': 'Copiar link',
|
|
2428
|
+
'praxis.filesUpload.details': 'Detalhes',
|
|
2429
|
+
'praxis.filesUpload.detailsMetadata': 'Metadados',
|
|
2430
|
+
'praxis.filesUpload.dropzoneHint': 'Arraste e solte arquivos aqui ou clique para selecionar',
|
|
2431
|
+
'praxis.filesUpload.dropzoneProximityHint': 'Solte aqui para enviar',
|
|
2432
|
+
'praxis.filesUpload.placeholder': 'Selecione ou solte arquivos…',
|
|
2433
|
+
'praxis.filesUpload.partialUploadError': 'Upload parcial concluído com falhas em parte do lote.',
|
|
2434
|
+
'praxis.filesUpload.presignMetadataMissing': 'Upload assinado concluído sem metadados do arquivo.',
|
|
2435
|
+
'praxis.filesUpload.fieldChipAria': 'Arquivos adicionais selecionados',
|
|
2436
|
+
'praxis.filesUpload.fieldPendingCountSuffix': 'arquivo(s) selecionado(s)',
|
|
2437
|
+
'praxis.filesUpload.removePendingFile': 'Remover arquivo selecionado',
|
|
2438
|
+
'praxis.filesUpload.fieldPolicyTypes': 'Tipos',
|
|
2439
|
+
'praxis.filesUpload.fieldPolicyMaxPerFile': 'Máx/arquivo',
|
|
2440
|
+
'praxis.filesUpload.fieldPolicyMaxItems': 'Máx/itens',
|
|
2441
|
+
'praxis.filesUpload.fieldPolicyNotConfigured': 'Não configurado',
|
|
2442
|
+
'praxis.filesUpload.sizeUnitBytes': 'Bytes',
|
|
2443
|
+
'praxis.filesUpload.sizeUnitKB': 'KB',
|
|
2444
|
+
'praxis.filesUpload.sizeUnitMB': 'MB',
|
|
2445
|
+
'praxis.filesUpload.sizeUnitGB': 'GB',
|
|
2446
|
+
'praxis.filesUpload.sizeUnitTB': 'TB',
|
|
2447
|
+
'praxis.filesUpload.errors.INVALID_FILE_TYPE': 'Tipo de arquivo inválido.',
|
|
2448
|
+
'praxis.filesUpload.errors.FILE_TOO_LARGE': 'Arquivo muito grande.',
|
|
2449
|
+
'praxis.filesUpload.errors.NOT_FOUND': 'Arquivo não encontrado.',
|
|
2450
|
+
'praxis.filesUpload.errors.UNAUTHORIZED': 'Requisição não autorizada.',
|
|
2451
|
+
'praxis.filesUpload.errors.RATE_LIMIT_EXCEEDED': 'Limite de requisições excedido.',
|
|
2452
|
+
'praxis.filesUpload.errors.INTERNAL_ERROR': 'Erro interno do servidor.',
|
|
2453
|
+
'praxis.filesUpload.errors.QUOTA_EXCEEDED': 'Cota excedida.',
|
|
2454
|
+
'praxis.filesUpload.errors.SEC_VIRUS_DETECTED': 'Vírus detectado no arquivo.',
|
|
2455
|
+
'praxis.filesUpload.errors.SEC_MALICIOUS_CONTENT': 'Conteúdo malicioso detectado.',
|
|
2456
|
+
'praxis.filesUpload.errors.SEC_DANGEROUS_TYPE': 'Tipo de arquivo perigoso.',
|
|
2457
|
+
'praxis.filesUpload.errors.FMT_MAGIC_MISMATCH': 'Conteúdo do arquivo incompatível.',
|
|
2458
|
+
'praxis.filesUpload.errors.FMT_CORRUPTED': 'Arquivo corrompido.',
|
|
2459
|
+
'praxis.filesUpload.errors.FMT_UNSUPPORTED': 'Formato de arquivo não suportado.',
|
|
2460
|
+
'praxis.filesUpload.errors.SYS_STORAGE_ERROR': 'Erro de armazenamento.',
|
|
2461
|
+
'praxis.filesUpload.errors.SYS_SERVICE_DOWN': 'Serviço indisponível.',
|
|
2462
|
+
'praxis.filesUpload.errors.SYS_RATE_LIMIT': 'Limite de requisições excedido.',
|
|
2463
|
+
// Aliases PT-BR do backend e códigos adicionais
|
|
2464
|
+
'praxis.filesUpload.errors.ARQUIVO_MUITO_GRANDE': 'Arquivo muito grande.',
|
|
2465
|
+
'praxis.filesUpload.errors.TIPO_ARQUIVO_INVALIDO': 'Tipo de arquivo inválido.',
|
|
2466
|
+
'praxis.filesUpload.errors.TIPO_MIDIA_NAO_SUPORTADO': 'Tipo de mídia não suportado.',
|
|
2467
|
+
'praxis.filesUpload.errors.CAMPO_OBRIGATORIO_AUSENTE': 'Campo obrigatório ausente.',
|
|
2468
|
+
'praxis.filesUpload.errors.OPCOES_JSON_INVALIDAS': 'JSON de opções inválido.',
|
|
2469
|
+
'praxis.filesUpload.errors.ARGUMENTO_INVALIDO': 'Argumento inválido.',
|
|
2470
|
+
'praxis.filesUpload.errors.NAO_AUTORIZADO': 'Autenticação necessária.',
|
|
2471
|
+
'praxis.filesUpload.errors.ERRO_INTERNO': 'Erro interno do servidor.',
|
|
2472
|
+
'praxis.filesUpload.errors.LIMITE_TAXA_EXCEDIDO': 'Limite de taxa excedido.',
|
|
2473
|
+
'praxis.filesUpload.errors.COTA_EXCEDIDA': 'Cota excedida.',
|
|
2474
|
+
'praxis.filesUpload.errors.ARQUIVO_JA_EXISTE': 'Arquivo já existe.',
|
|
2475
|
+
// Outros possíveis códigos mapeados no catálogo
|
|
2476
|
+
'praxis.filesUpload.errors.INVALID_JSON_OPTIONS': 'JSON de opções inválido.',
|
|
2477
|
+
'praxis.filesUpload.errors.EMPTY_FILENAME': 'Nome do arquivo obrigatório.',
|
|
2478
|
+
'praxis.filesUpload.errors.FILE_EXISTS': 'Arquivo já existe.',
|
|
2479
|
+
'praxis.filesUpload.errors.PATH_TRAVERSAL': 'Path traversal detectado.',
|
|
2480
|
+
'praxis.filesUpload.errors.INSUFFICIENT_STORAGE': 'Sem espaço em disco.',
|
|
2481
|
+
'praxis.filesUpload.errors.UPLOAD_TIMEOUT': 'Tempo esgotado no upload.',
|
|
2482
|
+
'praxis.filesUpload.errors.BULK_UPLOAD_TIMEOUT': 'Tempo esgotado no upload em lote.',
|
|
2483
|
+
'praxis.filesUpload.errors.BULK_UPLOAD_CANCELLED': 'Upload em lote cancelado.',
|
|
2484
|
+
'praxis.filesUpload.errors.USER_CANCELLED': 'Upload cancelado pelo usuário.',
|
|
2485
|
+
'praxis.filesUpload.errors.UNKNOWN_ERROR': 'Erro desconhecido.',
|
|
2486
|
+
'praxis.filesUpload.fieldPendingSummary': 'Arquivos prontos para envio',
|
|
2487
|
+
'praxis.filesUpload.fieldSelectedSummary': 'Arquivo selecionado',
|
|
2488
|
+
'praxis.filesUpload.fieldSelectedPlural': 'arquivos vinculados',
|
|
2489
|
+
'praxis.filesUpload.fieldEmptySummary': 'Nenhum arquivo selecionado',
|
|
2490
|
+
'praxis.filesUpload.fieldErrorSummary': 'Corrija o erro para continuar',
|
|
2491
|
+
'praxis.filesUpload.fieldActionUpload': 'Enviar arquivos selecionados',
|
|
2492
|
+
'praxis.filesUpload.fieldActionUploadShort': 'Enviar',
|
|
2493
|
+
'praxis.filesUpload.fieldActionCancel': 'Cancelar seleção atual',
|
|
2494
|
+
'praxis.filesUpload.fieldActionCancelShort': 'Cancelar',
|
|
2495
|
+
'praxis.filesUpload.fieldActionSelect': 'Selecionar arquivo(s)',
|
|
2496
|
+
'praxis.filesUpload.fieldActionClear': 'Limpar',
|
|
2497
|
+
'praxis.filesUpload.fieldInfoAction': 'Informações',
|
|
2498
|
+
'praxis.filesUpload.fieldMoreActions': 'Mais ações',
|
|
2499
|
+
'praxis.filesUpload.fieldPendingAriaSuffix': 'arquivos prontos para envio',
|
|
2500
|
+
'praxis.filesUpload.selectedOverlayAriaLabel': 'Arquivos selecionados',
|
|
2501
|
+
'praxis.filesUpload.selectedOverlayTitle': 'Selecionados',
|
|
2502
|
+
'praxis.filesUpload.bulkSummaryTotal': 'Total',
|
|
2503
|
+
'praxis.filesUpload.bulkSummarySuccess': 'Sucesso',
|
|
2504
|
+
'praxis.filesUpload.bulkSummaryFailed': 'Falhas',
|
|
2505
|
+
'praxis.filesUpload.resultMetaIdLabel': 'ID',
|
|
2506
|
+
'praxis.filesUpload.resultMetaTypeLabel': 'Tipo',
|
|
2507
|
+
'praxis.filesUpload.resultMetaSizeLabel': 'Tamanho',
|
|
2508
|
+
'praxis.filesUpload.resultMetaUploadedAtLabel': 'Enviado em',
|
|
2509
|
+
'praxis.filesUpload.policySummaryAny': 'qualquer',
|
|
2510
|
+
'praxis.filesUpload.policySummaryNotConfigured': '—',
|
|
2511
|
+
'praxis.filesUpload.policySummaryTypesLabel': 'Tipos',
|
|
2512
|
+
'praxis.filesUpload.policySummaryMaxPerFileLabel': 'Máx. por arquivo',
|
|
2513
|
+
'praxis.filesUpload.policySummaryQuantityLabel': 'Qtde',
|
|
2514
|
+
'praxis.filesUpload.showAll': 'Ver todos',
|
|
2515
|
+
'praxis.filesUpload.showLess': 'Ver menos',
|
|
2516
|
+
};
|
|
2517
|
+
|
|
2518
|
+
/** @deprecated Transitional compatibility token. Prefer PraxisI18nService. */
|
|
1808
2519
|
const FILES_UPLOAD_TEXTS = new InjectionToken('FILES_UPLOAD_TEXTS', {
|
|
1809
2520
|
providedIn: 'root',
|
|
1810
2521
|
factory: () => ({
|
|
@@ -1817,97 +2528,62 @@ const FILES_UPLOAD_TEXTS = new InjectionToken('FILES_UPLOAD_TEXTS', {
|
|
|
1817
2528
|
rateLimitBanner: 'Limite de requisições excedido. Tente novamente às',
|
|
1818
2529
|
}),
|
|
1819
2530
|
});
|
|
1820
|
-
|
|
2531
|
+
/** @deprecated Transitional compatibility token. Prefer PraxisI18nService. */
|
|
1821
2532
|
const TRANSLATE_LIKE = new InjectionToken('TRANSLATE_LIKE');
|
|
1822
|
-
|
|
1823
|
-
const
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
FMT_UNSUPPORTED: 'Formato de arquivo não suportado.',
|
|
1837
|
-
SYS_STORAGE_ERROR: 'Erro de armazenamento.',
|
|
1838
|
-
SYS_SERVICE_DOWN: 'Serviço indisponível.',
|
|
1839
|
-
SYS_RATE_LIMIT: 'Limite de requisições excedido.',
|
|
1840
|
-
// aliases em português vindos do backend
|
|
1841
|
-
ARQUIVO_MUITO_GRANDE: 'Arquivo muito grande.',
|
|
1842
|
-
TIPO_ARQUIVO_INVALIDO: 'Tipo de arquivo inválido.',
|
|
1843
|
-
TIPO_MIDIA_NAO_SUPORTADO: 'Tipo de mídia não suportado.',
|
|
1844
|
-
CAMPO_OBRIGATORIO_AUSENTE: 'Campo obrigatório ausente.',
|
|
1845
|
-
OPCOES_JSON_INVALIDAS: 'JSON de opções inválido.',
|
|
1846
|
-
ARGUMENTO_INVALIDO: 'Argumento inválido.',
|
|
1847
|
-
NAO_AUTORIZADO: 'Autenticação necessária.',
|
|
1848
|
-
ERRO_INTERNO: 'Erro interno do servidor.',
|
|
1849
|
-
LIMITE_TAXA_EXCEDIDO: 'Limite de taxa excedido.',
|
|
1850
|
-
COTA_EXCEDIDA: 'Cota excedida.',
|
|
1851
|
-
ARQUIVO_JA_EXISTE: 'Arquivo já existe.',
|
|
1852
|
-
};
|
|
1853
|
-
class ErrorMapperService {
|
|
1854
|
-
customMessages;
|
|
1855
|
-
translate;
|
|
1856
|
-
constructor(customMessages, translate) {
|
|
1857
|
-
this.customMessages = customMessages;
|
|
1858
|
-
this.translate = translate;
|
|
1859
|
-
}
|
|
1860
|
-
map(error, headers) {
|
|
1861
|
-
const code = String(error.code || 'UNKNOWN_ERROR');
|
|
1862
|
-
const key = `praxis.filesUpload.errors.${code}`;
|
|
1863
|
-
const translated = this.translate?.instant(key);
|
|
1864
|
-
const message = (translated && translated !== key ? translated : undefined) ??
|
|
1865
|
-
this.customMessages?.[code] ??
|
|
1866
|
-
DEFAULT_MESSAGES[code] ??
|
|
1867
|
-
error.message ??
|
|
1868
|
-
'Erro desconhecido.';
|
|
1869
|
-
// metadados do catálogo
|
|
1870
|
-
const meta = getErrorMeta(code);
|
|
1871
|
-
const mapped = {
|
|
1872
|
-
message,
|
|
1873
|
-
title: meta.title,
|
|
1874
|
-
severity: meta.severity,
|
|
1875
|
-
category: meta.category,
|
|
1876
|
-
phase: meta.phase,
|
|
1877
|
-
userAction: meta.userAction,
|
|
1878
|
-
};
|
|
1879
|
-
const isRateLimit = code === 'LIMITE_TAXA_EXCEDIDO' ||
|
|
1880
|
-
code === 'RATE_LIMIT_EXCEEDED' ||
|
|
1881
|
-
code === 'SYS_RATE_LIMIT';
|
|
1882
|
-
if (isRateLimit) {
|
|
1883
|
-
const limit = Number(headers?.get('X-RateLimit-Limit'));
|
|
1884
|
-
const remaining = Number(headers?.get('X-RateLimit-Remaining'));
|
|
1885
|
-
const resetEpochSeconds = Number(headers?.get('X-RateLimit-Reset'));
|
|
1886
|
-
if (!Number.isNaN(limit) &&
|
|
1887
|
-
!Number.isNaN(remaining) &&
|
|
1888
|
-
!Number.isNaN(resetEpochSeconds)) {
|
|
1889
|
-
mapped.rateLimit = { limit, remaining, resetEpochSeconds };
|
|
1890
|
-
}
|
|
2533
|
+
function createPraxisFilesUploadI18nConfig(options = {}) {
|
|
2534
|
+
const dictionaries = {
|
|
2535
|
+
'pt-BR': {
|
|
2536
|
+
...FILES_UPLOAD_PT_BR,
|
|
2537
|
+
...(options.dictionaries?.['pt-BR'] ?? {}),
|
|
2538
|
+
},
|
|
2539
|
+
'en-US': {
|
|
2540
|
+
...FILES_UPLOAD_EN_US,
|
|
2541
|
+
...(options.dictionaries?.['en-US'] ?? {}),
|
|
2542
|
+
},
|
|
2543
|
+
};
|
|
2544
|
+
for (const [locale, dictionary] of Object.entries(options.dictionaries ?? {})) {
|
|
2545
|
+
if (locale === 'pt-BR' || locale === 'en-US') {
|
|
2546
|
+
continue;
|
|
1891
2547
|
}
|
|
1892
|
-
|
|
2548
|
+
dictionaries[locale] = {
|
|
2549
|
+
...(dictionaries[locale] ?? {}),
|
|
2550
|
+
...dictionary,
|
|
2551
|
+
};
|
|
1893
2552
|
}
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
2553
|
+
return {
|
|
2554
|
+
locale: options.locale,
|
|
2555
|
+
fallbackLocale: options.fallbackLocale ?? 'pt-BR',
|
|
2556
|
+
dictionaries,
|
|
2557
|
+
};
|
|
2558
|
+
}
|
|
2559
|
+
function providePraxisFilesUploadI18n(options = {}) {
|
|
2560
|
+
return providePraxisI18n(createPraxisFilesUploadI18nConfig(options));
|
|
2561
|
+
}
|
|
2562
|
+
function resolvePraxisFilesUploadText(i18n, key, fallback, legacyTexts, legacyTranslate) {
|
|
2563
|
+
const namespacedKey = key.startsWith('praxis.filesUpload.')
|
|
2564
|
+
? key
|
|
2565
|
+
: `praxis.filesUpload.${key}`;
|
|
2566
|
+
const coreTranslation = i18n.t(namespacedKey, undefined, '');
|
|
2567
|
+
if (coreTranslation) {
|
|
2568
|
+
return coreTranslation;
|
|
2569
|
+
}
|
|
2570
|
+
const legacyText = legacyTexts?.[key];
|
|
2571
|
+
if (legacyText) {
|
|
2572
|
+
return legacyText;
|
|
2573
|
+
}
|
|
2574
|
+
const translatedByLegacyService = legacyTranslate?.t?.(namespacedKey, undefined, '') ||
|
|
2575
|
+
legacyTranslate?.t?.(key, undefined, '') ||
|
|
2576
|
+
resolveLegacyInstantTranslation(legacyTranslate, namespacedKey) ||
|
|
2577
|
+
resolveLegacyInstantTranslation(legacyTranslate, key);
|
|
2578
|
+
if (translatedByLegacyService) {
|
|
2579
|
+
return translatedByLegacyService;
|
|
2580
|
+
}
|
|
2581
|
+
return fallback;
|
|
2582
|
+
}
|
|
2583
|
+
function resolveLegacyInstantTranslation(legacyTranslate, key) {
|
|
2584
|
+
const translated = legacyTranslate?.instant?.(key);
|
|
2585
|
+
return translated && translated !== key ? translated : '';
|
|
2586
|
+
}
|
|
1911
2587
|
|
|
1912
2588
|
function toError(key, info) {
|
|
1913
2589
|
return { [key]: info };
|
|
@@ -2044,9 +2720,7 @@ class FilesApiClient {
|
|
|
2044
2720
|
constructor(http) {
|
|
2045
2721
|
this.http = http;
|
|
2046
2722
|
}
|
|
2047
|
-
/**
|
|
2048
|
-
* Upload a single file com metadata/conflictPolicy (compat) e/ou options JSON.
|
|
2049
|
-
*/
|
|
2723
|
+
/** Upload de um único arquivo com metadata/conflictPolicy e options JSON. */
|
|
2050
2724
|
upload(baseUrl, file, options = {}) {
|
|
2051
2725
|
const formData = new FormData();
|
|
2052
2726
|
formData.append('file', file);
|
|
@@ -2063,9 +2737,7 @@ class FilesApiClient {
|
|
|
2063
2737
|
})
|
|
2064
2738
|
.pipe(retry(options.retryCount ?? 0));
|
|
2065
2739
|
}
|
|
2066
|
-
/**
|
|
2067
|
-
* Upload múltiplo com arrays de metadata/conflictPolicy (compat) e/ou options JSON.
|
|
2068
|
-
*/
|
|
2740
|
+
/** Upload múltiplo com arrays de metadata/conflictPolicy e options JSON. */
|
|
2069
2741
|
bulkUpload(baseUrl, files, options = {}) {
|
|
2070
2742
|
const formData = new FormData();
|
|
2071
2743
|
files.forEach((f) => {
|
|
@@ -2073,10 +2745,9 @@ class FilesApiClient {
|
|
|
2073
2745
|
});
|
|
2074
2746
|
if (options.optionsJson)
|
|
2075
2747
|
formData.append('options', options.optionsJson);
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
formData.append('failFast', String(ff));
|
|
2748
|
+
if (options.failFast !== undefined) {
|
|
2749
|
+
formData.append('failFastMode', String(options.failFast));
|
|
2750
|
+
}
|
|
2080
2751
|
// Arrays de metadata/políticas quando presentes
|
|
2081
2752
|
const metas = files.map((f) => f.metadata ?? null);
|
|
2082
2753
|
if (metas.some((m) => m !== null)) {
|
|
@@ -2114,13 +2785,23 @@ class PresignedUploaderService {
|
|
|
2114
2785
|
/**
|
|
2115
2786
|
* Uploads a file to a presigned URL and retries when configured.
|
|
2116
2787
|
*/
|
|
2117
|
-
upload(target, file, retryCount = 0) {
|
|
2788
|
+
upload(target, file, options = {}, retryCount = 0) {
|
|
2118
2789
|
const formData = new FormData();
|
|
2119
2790
|
if (target.fields) {
|
|
2120
2791
|
Object.entries(target.fields).forEach(([key, value]) => {
|
|
2121
2792
|
formData.append(key, value);
|
|
2122
2793
|
});
|
|
2123
2794
|
}
|
|
2795
|
+
if (this.shouldAppendPraxisFields(target.uploadUrl)) {
|
|
2796
|
+
if (options.optionsJson)
|
|
2797
|
+
formData.append('options', options.optionsJson);
|
|
2798
|
+
if (options.metadata) {
|
|
2799
|
+
formData.append('metadata', JSON.stringify(options.metadata));
|
|
2800
|
+
}
|
|
2801
|
+
if (options.conflictPolicy) {
|
|
2802
|
+
formData.append('conflictPolicy', options.conflictPolicy);
|
|
2803
|
+
}
|
|
2804
|
+
}
|
|
2124
2805
|
formData.append('file', file);
|
|
2125
2806
|
return this.http
|
|
2126
2807
|
.post(target.uploadUrl, formData, {
|
|
@@ -2131,6 +2812,18 @@ class PresignedUploaderService {
|
|
|
2131
2812
|
})
|
|
2132
2813
|
.pipe(retry(retryCount));
|
|
2133
2814
|
}
|
|
2815
|
+
shouldAppendPraxisFields(uploadUrl) {
|
|
2816
|
+
if (!uploadUrl)
|
|
2817
|
+
return false;
|
|
2818
|
+
if (uploadUrl.startsWith('/'))
|
|
2819
|
+
return true;
|
|
2820
|
+
try {
|
|
2821
|
+
return new URL(uploadUrl, window.location.origin).origin === window.location.origin;
|
|
2822
|
+
}
|
|
2823
|
+
catch {
|
|
2824
|
+
return false;
|
|
2825
|
+
}
|
|
2826
|
+
}
|
|
2134
2827
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PresignedUploaderService, deps: [{ token: i1.HttpClient }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2135
2828
|
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PresignedUploaderService, providedIn: 'root' });
|
|
2136
2829
|
}
|
|
@@ -2139,38 +2832,123 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
2139
2832
|
args: [{ providedIn: 'root' }]
|
|
2140
2833
|
}], ctorParameters: () => [{ type: i1.HttpClient }] });
|
|
2141
2834
|
|
|
2835
|
+
const FILES_UPLOAD_ERROR_MESSAGES = new InjectionToken('FILES_UPLOAD_ERROR_MESSAGES');
|
|
2836
|
+
const DEFAULT_MESSAGES = {
|
|
2837
|
+
INVALID_FILE_TYPE: 'Tipo de arquivo inválido.',
|
|
2838
|
+
FILE_TOO_LARGE: 'Arquivo muito grande.',
|
|
2839
|
+
NOT_FOUND: 'Arquivo não encontrado.',
|
|
2840
|
+
UNAUTHORIZED: 'Requisição não autorizada.',
|
|
2841
|
+
RATE_LIMIT_EXCEEDED: 'Limite de requisições excedido.',
|
|
2842
|
+
INTERNAL_ERROR: 'Erro interno do servidor.',
|
|
2843
|
+
QUOTA_EXCEEDED: 'Cota excedida.',
|
|
2844
|
+
SEC_VIRUS_DETECTED: 'Vírus detectado no arquivo.',
|
|
2845
|
+
SEC_MALICIOUS_CONTENT: 'Conteúdo malicioso detectado.',
|
|
2846
|
+
SEC_DANGEROUS_TYPE: 'Tipo de arquivo perigoso.',
|
|
2847
|
+
FMT_MAGIC_MISMATCH: 'Conteúdo do arquivo incompatível.',
|
|
2848
|
+
FMT_CORRUPTED: 'Arquivo corrompido.',
|
|
2849
|
+
FMT_UNSUPPORTED: 'Formato de arquivo não suportado.',
|
|
2850
|
+
SYS_STORAGE_ERROR: 'Erro de armazenamento.',
|
|
2851
|
+
SYS_SERVICE_DOWN: 'Serviço indisponível.',
|
|
2852
|
+
SYS_RATE_LIMIT: 'Limite de requisições excedido.',
|
|
2853
|
+
// aliases em português vindos do backend
|
|
2854
|
+
ARQUIVO_MUITO_GRANDE: 'Arquivo muito grande.',
|
|
2855
|
+
TIPO_ARQUIVO_INVALIDO: 'Tipo de arquivo inválido.',
|
|
2856
|
+
TIPO_MIDIA_NAO_SUPORTADO: 'Tipo de mídia não suportado.',
|
|
2857
|
+
CAMPO_OBRIGATORIO_AUSENTE: 'Campo obrigatório ausente.',
|
|
2858
|
+
OPCOES_JSON_INVALIDAS: 'JSON de opções inválido.',
|
|
2859
|
+
ARGUMENTO_INVALIDO: 'Argumento inválido.',
|
|
2860
|
+
NAO_AUTORIZADO: 'Autenticação necessária.',
|
|
2861
|
+
ERRO_INTERNO: 'Erro interno do servidor.',
|
|
2862
|
+
LIMITE_TAXA_EXCEDIDO: 'Limite de taxa excedido.',
|
|
2863
|
+
COTA_EXCEDIDA: 'Cota excedida.',
|
|
2864
|
+
ARQUIVO_JA_EXISTE: 'Arquivo já existe.',
|
|
2865
|
+
};
|
|
2866
|
+
class ErrorMapperService {
|
|
2867
|
+
i18n;
|
|
2868
|
+
customMessages;
|
|
2869
|
+
legacyTranslate;
|
|
2870
|
+
constructor(i18n, customMessages, legacyTranslate) {
|
|
2871
|
+
this.i18n = i18n;
|
|
2872
|
+
this.customMessages = customMessages;
|
|
2873
|
+
this.legacyTranslate = legacyTranslate;
|
|
2874
|
+
}
|
|
2875
|
+
map(error, headers) {
|
|
2876
|
+
const primary = getPrimaryErrorItem(error);
|
|
2877
|
+
const code = String(primary?.code || 'UNKNOWN_ERROR');
|
|
2878
|
+
const translatedMessage = resolvePraxisFilesUploadText(this.i18n, `errors.${code}`, '', undefined, this.legacyTranslate);
|
|
2879
|
+
const defaultMessage = DEFAULT_MESSAGES[code] ??
|
|
2880
|
+
primary?.message ??
|
|
2881
|
+
(isErrorEnvelope(error) ? error.message : undefined) ??
|
|
2882
|
+
'Erro desconhecido.';
|
|
2883
|
+
const configuredMessage = this.customMessages?.[code];
|
|
2884
|
+
const message = configuredMessage ??
|
|
2885
|
+
(translatedMessage || defaultMessage);
|
|
2886
|
+
// metadados do catálogo
|
|
2887
|
+
const meta = getErrorMeta(code);
|
|
2888
|
+
const mapped = {
|
|
2889
|
+
message,
|
|
2890
|
+
title: meta.title,
|
|
2891
|
+
severity: meta.severity,
|
|
2892
|
+
category: meta.category,
|
|
2893
|
+
phase: meta.phase,
|
|
2894
|
+
userAction: meta.userAction,
|
|
2895
|
+
};
|
|
2896
|
+
const isRateLimit = code === 'LIMITE_TAXA_EXCEDIDO' ||
|
|
2897
|
+
code === 'RATE_LIMIT_EXCEEDED' ||
|
|
2898
|
+
code === 'SYS_RATE_LIMIT';
|
|
2899
|
+
if (isRateLimit) {
|
|
2900
|
+
const limit = Number(headers?.get('X-RateLimit-Limit'));
|
|
2901
|
+
const remaining = Number(headers?.get('X-RateLimit-Remaining'));
|
|
2902
|
+
const resetEpochSeconds = Number(headers?.get('X-RateLimit-Reset'));
|
|
2903
|
+
if (!Number.isNaN(limit) &&
|
|
2904
|
+
!Number.isNaN(remaining) &&
|
|
2905
|
+
!Number.isNaN(resetEpochSeconds)) {
|
|
2906
|
+
mapped.rateLimit = { limit, remaining, resetEpochSeconds };
|
|
2907
|
+
}
|
|
2908
|
+
}
|
|
2909
|
+
return mapped;
|
|
2910
|
+
}
|
|
2911
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ErrorMapperService, deps: [{ token: i1$2.PraxisI18nService }, { token: FILES_UPLOAD_ERROR_MESSAGES, optional: true }, { token: TRANSLATE_LIKE, optional: true }], target: i0.ɵɵFactoryTarget.Injectable });
|
|
2912
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ErrorMapperService, providedIn: 'root' });
|
|
2913
|
+
}
|
|
2914
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: ErrorMapperService, decorators: [{
|
|
2915
|
+
type: Injectable,
|
|
2916
|
+
args: [{ providedIn: 'root' }]
|
|
2917
|
+
}], ctorParameters: () => [{ type: i1$2.PraxisI18nService }, { type: undefined, decorators: [{
|
|
2918
|
+
type: Optional
|
|
2919
|
+
}, {
|
|
2920
|
+
type: Inject,
|
|
2921
|
+
args: [FILES_UPLOAD_ERROR_MESSAGES]
|
|
2922
|
+
}] }, { type: undefined, decorators: [{
|
|
2923
|
+
type: Optional
|
|
2924
|
+
}, {
|
|
2925
|
+
type: Inject,
|
|
2926
|
+
args: [TRANSLATE_LIKE]
|
|
2927
|
+
}] }] });
|
|
2928
|
+
|
|
2929
|
+
let nextPraxisFilesUploadA11yId = 0;
|
|
2142
2930
|
class PraxisFilesUpload {
|
|
2143
2931
|
settingsPanel;
|
|
2144
2932
|
dragProx;
|
|
2145
|
-
defaultTexts;
|
|
2146
2933
|
overlay;
|
|
2147
2934
|
viewContainerRef;
|
|
2148
2935
|
cdr;
|
|
2149
2936
|
configStorage;
|
|
2937
|
+
i18n;
|
|
2938
|
+
legacyTexts;
|
|
2939
|
+
legacyTranslate;
|
|
2940
|
+
configService;
|
|
2150
2941
|
api;
|
|
2151
2942
|
presign;
|
|
2152
2943
|
errors;
|
|
2153
|
-
translate;
|
|
2154
2944
|
config = null;
|
|
2155
2945
|
_filesUploadId = '';
|
|
2156
|
-
_uploadId = '';
|
|
2157
2946
|
set filesUploadId(value) {
|
|
2158
2947
|
this._filesUploadId = value ?? '';
|
|
2159
|
-
if (!this._uploadId)
|
|
2160
|
-
this._uploadId = this._filesUploadId;
|
|
2161
2948
|
}
|
|
2162
2949
|
get filesUploadId() {
|
|
2163
2950
|
return this._filesUploadId;
|
|
2164
2951
|
}
|
|
2165
|
-
set uploadId(value) {
|
|
2166
|
-
const v = value ?? '';
|
|
2167
|
-
this._uploadId = v;
|
|
2168
|
-
if (!this._filesUploadId)
|
|
2169
|
-
this._filesUploadId = v;
|
|
2170
|
-
}
|
|
2171
|
-
get uploadId() {
|
|
2172
|
-
return this._uploadId;
|
|
2173
|
-
}
|
|
2174
2952
|
componentInstanceId;
|
|
2175
2953
|
baseUrl;
|
|
2176
2954
|
displayMode = 'full';
|
|
@@ -2182,15 +2960,15 @@ class PraxisFilesUpload {
|
|
|
2182
2960
|
rateLimited = new EventEmitter();
|
|
2183
2961
|
uploadStart = new EventEmitter();
|
|
2184
2962
|
uploadProgress = new EventEmitter();
|
|
2185
|
-
uploadError = new EventEmitter();
|
|
2186
2963
|
// Novos outputs Sub-issue 4
|
|
2187
2964
|
retry = new EventEmitter();
|
|
2188
2965
|
download = new EventEmitter();
|
|
2189
2966
|
copyLink = new EventEmitter();
|
|
2190
2967
|
detailsOpened = new EventEmitter();
|
|
2191
2968
|
detailsClosed = new EventEmitter();
|
|
2192
|
-
|
|
2969
|
+
pendingStateChange = new EventEmitter();
|
|
2193
2970
|
proximityChange = new EventEmitter();
|
|
2971
|
+
readinessChange = new EventEmitter();
|
|
2194
2972
|
fileInput;
|
|
2195
2973
|
hostRef;
|
|
2196
2974
|
overlayTmpl;
|
|
@@ -2217,6 +2995,8 @@ class PraxisFilesUpload {
|
|
|
2217
2995
|
currentFiles = [];
|
|
2218
2996
|
// Config efetiva do servidor para fallbacks
|
|
2219
2997
|
serverEffective;
|
|
2998
|
+
readinessState = 'idle';
|
|
2999
|
+
readinessSource = 'local';
|
|
2220
3000
|
// Proximidade/overlay
|
|
2221
3001
|
showProximityOverlay = false;
|
|
2222
3002
|
proxSub;
|
|
@@ -2241,19 +3021,31 @@ class PraxisFilesUpload {
|
|
|
2241
3021
|
selectedOverlayRef;
|
|
2242
3022
|
// Lista: colapso
|
|
2243
3023
|
showAllResults = false;
|
|
2244
|
-
|
|
3024
|
+
initialized = false;
|
|
3025
|
+
generatedA11yId = `files-upload-${++nextPraxisFilesUploadA11yId}`;
|
|
3026
|
+
constructor(settingsPanel, dragProx, overlay, viewContainerRef, cdr, configStorage, i18n, legacyTexts, legacyTranslate, configService, api, presign, errors) {
|
|
2245
3027
|
this.settingsPanel = settingsPanel;
|
|
2246
3028
|
this.dragProx = dragProx;
|
|
2247
|
-
this.defaultTexts = defaultTexts;
|
|
2248
3029
|
this.overlay = overlay;
|
|
2249
3030
|
this.viewContainerRef = viewContainerRef;
|
|
2250
3031
|
this.cdr = cdr;
|
|
2251
3032
|
this.configStorage = configStorage;
|
|
3033
|
+
this.i18n = i18n;
|
|
3034
|
+
this.legacyTexts = legacyTexts;
|
|
3035
|
+
this.legacyTranslate = legacyTranslate;
|
|
3036
|
+
this.configService = configService;
|
|
2252
3037
|
this.api = api;
|
|
2253
3038
|
this.presign = presign;
|
|
2254
3039
|
this.errors = errors;
|
|
2255
|
-
this.
|
|
2256
|
-
|
|
3040
|
+
this.texts = {
|
|
3041
|
+
settingsAriaLabel: '',
|
|
3042
|
+
dropzoneLabel: '',
|
|
3043
|
+
dropzoneButton: '',
|
|
3044
|
+
conflictPolicyLabel: '',
|
|
3045
|
+
metadataLabel: '',
|
|
3046
|
+
progressAriaLabel: '',
|
|
3047
|
+
rateLimitBanner: '',
|
|
3048
|
+
};
|
|
2257
3049
|
}
|
|
2258
3050
|
componentKeys = inject(ComponentKeyService);
|
|
2259
3051
|
route = (() => { try {
|
|
@@ -2263,6 +3055,18 @@ class PraxisFilesUpload {
|
|
|
2263
3055
|
return undefined;
|
|
2264
3056
|
} })();
|
|
2265
3057
|
warnedMissingId = false;
|
|
3058
|
+
requireApiClient() {
|
|
3059
|
+
if (!this.api) {
|
|
3060
|
+
throw new Error('FilesApiClient is not available.');
|
|
3061
|
+
}
|
|
3062
|
+
return this.api;
|
|
3063
|
+
}
|
|
3064
|
+
requireBaseUrl() {
|
|
3065
|
+
if (!this.baseUrl) {
|
|
3066
|
+
throw new Error(this.t('baseUrlMissing', 'Base URL não configurada. Informe [baseUrl] para habilitar o envio.'));
|
|
3067
|
+
}
|
|
3068
|
+
return this.baseUrl;
|
|
3069
|
+
}
|
|
2266
3070
|
// Helpers de UI
|
|
2267
3071
|
get showProgress() {
|
|
2268
3072
|
return this.uploading;
|
|
@@ -2280,6 +3084,24 @@ class PraxisFilesUpload {
|
|
|
2280
3084
|
get pendingCount() {
|
|
2281
3085
|
return this.currentFiles.length;
|
|
2282
3086
|
}
|
|
3087
|
+
get a11yBaseId() {
|
|
3088
|
+
return this.filesUploadId || this.componentInstanceId || this.generatedA11yId;
|
|
3089
|
+
}
|
|
3090
|
+
get messageRegionId() {
|
|
3091
|
+
return `${this.a11yBaseId}-messages`;
|
|
3092
|
+
}
|
|
3093
|
+
get progressRegionId() {
|
|
3094
|
+
return `${this.a11yBaseId}-progress`;
|
|
3095
|
+
}
|
|
3096
|
+
get conflictPolicyInputId() {
|
|
3097
|
+
return `${this.a11yBaseId}-conflict-policy`;
|
|
3098
|
+
}
|
|
3099
|
+
get metadataInputId() {
|
|
3100
|
+
return `${this.a11yBaseId}-metadata`;
|
|
3101
|
+
}
|
|
3102
|
+
get selectedOverlayTitleId() {
|
|
3103
|
+
return `${this.a11yBaseId}-selected-title`;
|
|
3104
|
+
}
|
|
2283
3105
|
get hasLastResults() {
|
|
2284
3106
|
return Array.isArray(this.lastResults) && this.lastResults.length > 0;
|
|
2285
3107
|
}
|
|
@@ -2290,7 +3112,7 @@ class PraxisFilesUpload {
|
|
|
2290
3112
|
}
|
|
2291
3113
|
}
|
|
2292
3114
|
// Expor método público para pais (wrapper) abrirem o seletor
|
|
2293
|
-
|
|
3115
|
+
openFileDialog() {
|
|
2294
3116
|
const el = this.fileInput?.nativeElement;
|
|
2295
3117
|
if (el)
|
|
2296
3118
|
this.openFile(el);
|
|
@@ -2301,9 +3123,15 @@ class PraxisFilesUpload {
|
|
|
2301
3123
|
}
|
|
2302
3124
|
formatBytes(bytes) {
|
|
2303
3125
|
const b = typeof bytes === 'number' ? bytes : 0;
|
|
2304
|
-
if (b < 1024)
|
|
2305
|
-
return `${b}
|
|
2306
|
-
|
|
3126
|
+
if (b < 1024) {
|
|
3127
|
+
return `${b} ${this.t('sizeUnitBytes', 'Bytes')}`;
|
|
3128
|
+
}
|
|
3129
|
+
const units = [
|
|
3130
|
+
this.t('sizeUnitKB', 'KB'),
|
|
3131
|
+
this.t('sizeUnitMB', 'MB'),
|
|
3132
|
+
this.t('sizeUnitGB', 'GB'),
|
|
3133
|
+
this.t('sizeUnitTB', 'TB'),
|
|
3134
|
+
];
|
|
2307
3135
|
let v = b / 1024;
|
|
2308
3136
|
let i = 0;
|
|
2309
3137
|
while (v >= 1024 && i < units.length - 1) {
|
|
@@ -2336,6 +3164,8 @@ class PraxisFilesUpload {
|
|
|
2336
3164
|
const key = this.storageKey();
|
|
2337
3165
|
this.applyConfig(this.config ?? {});
|
|
2338
3166
|
this.resolveTexts();
|
|
3167
|
+
this.loadServerEffectiveConfig();
|
|
3168
|
+
this.initialized = true;
|
|
2339
3169
|
if (key) {
|
|
2340
3170
|
this.configStorage.loadConfig(key).pipe(take(1)).subscribe((stored) => {
|
|
2341
3171
|
if (stored) {
|
|
@@ -2345,8 +3175,13 @@ class PraxisFilesUpload {
|
|
|
2345
3175
|
}
|
|
2346
3176
|
});
|
|
2347
3177
|
}
|
|
2348
|
-
|
|
2349
|
-
|
|
3178
|
+
}
|
|
3179
|
+
ngOnChanges() {
|
|
3180
|
+
this.applyConfig(this.config ?? {});
|
|
3181
|
+
this.resolveTexts();
|
|
3182
|
+
if (this.initialized) {
|
|
3183
|
+
this.loadServerEffectiveConfig();
|
|
3184
|
+
}
|
|
2350
3185
|
}
|
|
2351
3186
|
ngAfterViewInit() {
|
|
2352
3187
|
// Calcular retângulo do host
|
|
@@ -2553,24 +3388,38 @@ class PraxisFilesUpload {
|
|
|
2553
3388
|
if (this.showProximityOverlay) {
|
|
2554
3389
|
this.showProximityOverlay = false;
|
|
2555
3390
|
this.manageOverlay();
|
|
3391
|
+
this.proximityChange.emit(false);
|
|
2556
3392
|
}
|
|
2557
3393
|
}
|
|
2558
3394
|
// Helpers de configuração e armazenamento
|
|
2559
3395
|
resolveTexts() {
|
|
2560
3396
|
this.texts = {
|
|
2561
|
-
settingsAriaLabel: this.t('settingsAriaLabel',
|
|
2562
|
-
dropzoneLabel: this.t('dropzoneLabel',
|
|
2563
|
-
dropzoneButton: this.t('dropzoneButton',
|
|
2564
|
-
conflictPolicyLabel: this.t('conflictPolicyLabel',
|
|
2565
|
-
metadataLabel: this.t('metadataLabel',
|
|
2566
|
-
progressAriaLabel: this.t('progressAriaLabel',
|
|
2567
|
-
rateLimitBanner: this.t('rateLimitBanner',
|
|
3397
|
+
settingsAriaLabel: this.t('settingsAriaLabel', 'Abrir configurações'),
|
|
3398
|
+
dropzoneLabel: this.t('dropzoneLabel', 'Arraste arquivos ou'),
|
|
3399
|
+
dropzoneButton: this.t('dropzoneButton', 'selecionar'),
|
|
3400
|
+
conflictPolicyLabel: this.t('conflictPolicyLabel', 'Política de conflito'),
|
|
3401
|
+
metadataLabel: this.t('metadataLabel', 'Metadados (JSON)'),
|
|
3402
|
+
progressAriaLabel: this.t('progressAriaLabel', 'Progresso do upload'),
|
|
3403
|
+
rateLimitBanner: this.t('rateLimitBanner', 'Limite de requisições excedido. Tente novamente às'),
|
|
2568
3404
|
};
|
|
2569
3405
|
}
|
|
2570
3406
|
t(key, fallback) {
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
3407
|
+
return resolvePraxisFilesUploadText(this.i18n, key, fallback, this.legacyTexts, this.legacyTranslate);
|
|
3408
|
+
}
|
|
3409
|
+
get selectFilesAriaLabel() {
|
|
3410
|
+
return this.t('selectFiles', 'Selecionar arquivo(s)');
|
|
3411
|
+
}
|
|
3412
|
+
get policySummaryAriaLabel() {
|
|
3413
|
+
return this.t('policySummaryAction', 'Informacoes sobre politicas de upload');
|
|
3414
|
+
}
|
|
3415
|
+
pendingFileRemoveAriaLabel(file) {
|
|
3416
|
+
return `${this.t('removePendingFile', 'Remover arquivo selecionado')}: ${file.name}`;
|
|
3417
|
+
}
|
|
3418
|
+
pendingFileMoreActionsAriaLabel(file) {
|
|
3419
|
+
return `${this.t('moreActions', 'Mais ações')}: ${file.name}`;
|
|
3420
|
+
}
|
|
3421
|
+
resultActionAriaLabel(actionKey, fallback, fileName) {
|
|
3422
|
+
return `${this.t(actionKey, fallback)}: ${fileName}`;
|
|
2574
3423
|
}
|
|
2575
3424
|
openSettings() {
|
|
2576
3425
|
if (!this.editModeEnabled)
|
|
@@ -2601,11 +3450,42 @@ class PraxisFilesUpload {
|
|
|
2601
3450
|
}
|
|
2602
3451
|
applyConfig(cfg) {
|
|
2603
3452
|
this.config = { ...cfg };
|
|
2604
|
-
this.conflictPolicy.setValue((cfg.
|
|
3453
|
+
this.conflictPolicy.setValue((cfg.options?.defaultConflictPolicy ?? 'RENAME'));
|
|
2605
3454
|
this.allowMultiple = (cfg.limits?.maxFilesPerBulk ?? 1) > 1;
|
|
2606
3455
|
const acc = cfg.ui?.accept ?? [];
|
|
2607
3456
|
this.acceptString = acc.includes('*') ? '' : acc.join(',');
|
|
2608
3457
|
}
|
|
3458
|
+
loadServerEffectiveConfig() {
|
|
3459
|
+
if (!this.configService || !this.baseUrl) {
|
|
3460
|
+
this.setReadinessState('ready', 'local');
|
|
3461
|
+
return;
|
|
3462
|
+
}
|
|
3463
|
+
this.setReadinessState('loading', 'server');
|
|
3464
|
+
this.configService
|
|
3465
|
+
.getEffectiveConfig(this.baseUrl)
|
|
3466
|
+
.pipe(take(1))
|
|
3467
|
+
.subscribe({
|
|
3468
|
+
next: (cfg) => {
|
|
3469
|
+
this.serverEffective = cfg;
|
|
3470
|
+
this.setReadinessState('ready', 'server');
|
|
3471
|
+
},
|
|
3472
|
+
error: () => {
|
|
3473
|
+
// Fallback local segue válido quando a consulta de config falha.
|
|
3474
|
+
this.setReadinessState('ready', 'fallback');
|
|
3475
|
+
},
|
|
3476
|
+
});
|
|
3477
|
+
}
|
|
3478
|
+
setReadinessState(state, source) {
|
|
3479
|
+
this.readinessState = state;
|
|
3480
|
+
this.readinessSource = source;
|
|
3481
|
+
this.readinessChange.emit({
|
|
3482
|
+
state,
|
|
3483
|
+
source,
|
|
3484
|
+
filesUploadId: this.filesUploadId || undefined,
|
|
3485
|
+
baseUrl: this.baseUrl,
|
|
3486
|
+
});
|
|
3487
|
+
this.cdr.markForCheck();
|
|
3488
|
+
}
|
|
2609
3489
|
storageKey() {
|
|
2610
3490
|
const id = this.componentKeyId();
|
|
2611
3491
|
return id ? `files-upload-config:${id}` : null;
|
|
@@ -2627,7 +3507,7 @@ class PraxisFilesUpload {
|
|
|
2627
3507
|
if (this.warnedMissingId)
|
|
2628
3508
|
return;
|
|
2629
3509
|
this.warnedMissingId = true;
|
|
2630
|
-
console.warn('[PraxisFilesUpload] filesUploadId
|
|
3510
|
+
console.warn('[PraxisFilesUpload] filesUploadId e necessario para persistencia de configuracao.');
|
|
2631
3511
|
}
|
|
2632
3512
|
onDragOver(evt) {
|
|
2633
3513
|
evt.preventDefault();
|
|
@@ -2645,7 +3525,7 @@ class PraxisFilesUpload {
|
|
|
2645
3525
|
if (this.config?.ui?.manualUpload) {
|
|
2646
3526
|
this.currentFiles = files;
|
|
2647
3527
|
this.manageSelectedOverlay();
|
|
2648
|
-
this.
|
|
3528
|
+
this.emitPendingState();
|
|
2649
3529
|
}
|
|
2650
3530
|
else {
|
|
2651
3531
|
this.startUpload(files);
|
|
@@ -2659,7 +3539,7 @@ class PraxisFilesUpload {
|
|
|
2659
3539
|
if (this.config?.ui?.manualUpload) {
|
|
2660
3540
|
this.currentFiles = files;
|
|
2661
3541
|
this.manageSelectedOverlay();
|
|
2662
|
-
this.
|
|
3542
|
+
this.emitPendingState();
|
|
2663
3543
|
}
|
|
2664
3544
|
else {
|
|
2665
3545
|
this.startUpload(files);
|
|
@@ -2667,24 +3547,32 @@ class PraxisFilesUpload {
|
|
|
2667
3547
|
input.value = '';
|
|
2668
3548
|
}
|
|
2669
3549
|
}
|
|
2670
|
-
|
|
3550
|
+
submitPendingFiles() {
|
|
2671
3551
|
if (this.currentFiles.length) {
|
|
2672
3552
|
this.hideSelectedOverlay();
|
|
2673
3553
|
this.startUpload(this.currentFiles);
|
|
2674
3554
|
}
|
|
2675
3555
|
}
|
|
2676
|
-
|
|
3556
|
+
resetPendingFiles() {
|
|
2677
3557
|
this.currentFiles = [];
|
|
2678
|
-
this.
|
|
3558
|
+
this.emitPendingState();
|
|
2679
3559
|
this.errorMessage = '';
|
|
2680
3560
|
this.hideSelectedOverlay();
|
|
2681
3561
|
}
|
|
2682
3562
|
// Remoção individual dentro do overlay de seleção
|
|
2683
3563
|
removePendingFile(file) {
|
|
2684
3564
|
this.currentFiles = this.currentFiles.filter((f) => f !== file);
|
|
2685
|
-
this.
|
|
3565
|
+
this.emitPendingState();
|
|
2686
3566
|
this.manageSelectedOverlay();
|
|
2687
3567
|
}
|
|
3568
|
+
emitPendingState() {
|
|
3569
|
+
const files = [...this.currentFiles];
|
|
3570
|
+
this.pendingStateChange.emit({
|
|
3571
|
+
files,
|
|
3572
|
+
count: files.length,
|
|
3573
|
+
hasPending: files.length > 0,
|
|
3574
|
+
});
|
|
3575
|
+
}
|
|
2688
3576
|
startUpload(files) {
|
|
2689
3577
|
if (!this.baseUrl ||
|
|
2690
3578
|
(this.quotaExceeded && this.config?.quotas?.blockOnExceed)) {
|
|
@@ -2727,36 +3615,11 @@ class PraxisFilesUpload {
|
|
|
2727
3615
|
files,
|
|
2728
3616
|
mode: files.length > 1 && this.allowMultiple ? 'bulk' : 'single',
|
|
2729
3617
|
strategy,
|
|
2730
|
-
|
|
3618
|
+
filesUploadId: this.filesUploadId || undefined,
|
|
2731
3619
|
baseUrl: this.baseUrl,
|
|
2732
3620
|
});
|
|
2733
3621
|
if (files.length > 1 && this.allowMultiple) {
|
|
2734
|
-
|
|
2735
|
-
const bulkFiles = files.map((f) => ({
|
|
2736
|
-
file: f,
|
|
2737
|
-
metadata: metadataObj,
|
|
2738
|
-
conflictPolicy: policy,
|
|
2739
|
-
}));
|
|
2740
|
-
this.api.bulkUpload(this.baseUrl, bulkFiles, {
|
|
2741
|
-
optionsJson,
|
|
2742
|
-
failFastMode: this.config?.limits?.failFast,
|
|
2743
|
-
retryCount: this.config?.bulk?.retryCount,
|
|
2744
|
-
}).subscribe({
|
|
2745
|
-
next: (event) => {
|
|
2746
|
-
if (event.type === HttpEventType.Response) {
|
|
2747
|
-
const resp = event.body;
|
|
2748
|
-
this.bulkComplete.emit(resp);
|
|
2749
|
-
// Preenche estados ricos
|
|
2750
|
-
this.lastResults = resp?.results ?? null;
|
|
2751
|
-
this.lastStats = resp?.stats ?? null;
|
|
2752
|
-
this.finishUpload(true, undefined, resp.results);
|
|
2753
|
-
}
|
|
2754
|
-
else {
|
|
2755
|
-
this.handleProgress(event);
|
|
2756
|
-
}
|
|
2757
|
-
},
|
|
2758
|
-
error: (err) => this.handleError(err),
|
|
2759
|
-
});
|
|
3622
|
+
this.uploadMultipleFiles(files, strategy, metadataObj, optionsJson);
|
|
2760
3623
|
}
|
|
2761
3624
|
else if (strategy === 'presign' || strategy === 'auto') {
|
|
2762
3625
|
const file = files[0];
|
|
@@ -2768,20 +3631,29 @@ class PraxisFilesUpload {
|
|
|
2768
3631
|
});
|
|
2769
3632
|
return;
|
|
2770
3633
|
}
|
|
2771
|
-
this.
|
|
3634
|
+
const api = this.requireApiClient();
|
|
3635
|
+
const baseUrl = this.requireBaseUrl();
|
|
3636
|
+
api.presign(baseUrl, file.name).subscribe({
|
|
2772
3637
|
next: (target) => {
|
|
2773
|
-
|
|
3638
|
+
const uploadOptions = {
|
|
3639
|
+
optionsJson,
|
|
3640
|
+
metadata: metadataObj,
|
|
3641
|
+
conflictPolicy: this.conflictPolicy.value || 'RENAME',
|
|
3642
|
+
};
|
|
3643
|
+
this.presign.upload(target, file, uploadOptions, this.config?.bulk?.retryCount ?? 0).subscribe({
|
|
2774
3644
|
next: (event) => {
|
|
2775
3645
|
if (event.type === HttpEventType.Response) {
|
|
2776
|
-
|
|
2777
|
-
|
|
2778
|
-
|
|
2779
|
-
|
|
2780
|
-
|
|
2781
|
-
|
|
2782
|
-
|
|
2783
|
-
|
|
2784
|
-
|
|
3646
|
+
const responseEvent = event;
|
|
3647
|
+
const response = responseEvent.body instanceof HttpResponse
|
|
3648
|
+
? responseEvent.body.body
|
|
3649
|
+
: responseEvent.body;
|
|
3650
|
+
const meta = this.mapUploadResponseToFileMetadata(response);
|
|
3651
|
+
if (meta?.id) {
|
|
3652
|
+
this.finishUpload(true, meta);
|
|
3653
|
+
}
|
|
3654
|
+
else {
|
|
3655
|
+
this.handleError(new Error('Presigned upload completed without file metadata.'));
|
|
3656
|
+
}
|
|
2785
3657
|
}
|
|
2786
3658
|
else {
|
|
2787
3659
|
this.handleProgress(event);
|
|
@@ -2824,11 +3696,77 @@ class PraxisFilesUpload {
|
|
|
2824
3696
|
}
|
|
2825
3697
|
}
|
|
2826
3698
|
directUpload(file, options) {
|
|
2827
|
-
|
|
3699
|
+
const api = this.requireApiClient();
|
|
3700
|
+
const baseUrl = this.requireBaseUrl();
|
|
3701
|
+
api.upload(baseUrl, file, options).subscribe({
|
|
2828
3702
|
next: (event) => {
|
|
2829
3703
|
if (event.type === HttpEventType.Response) {
|
|
2830
3704
|
const resp = event.body;
|
|
2831
|
-
this.
|
|
3705
|
+
const meta = this.mapUploadResponseToFileMetadata(resp);
|
|
3706
|
+
this.finishUpload(true, meta);
|
|
3707
|
+
}
|
|
3708
|
+
else {
|
|
3709
|
+
this.handleProgress(event);
|
|
3710
|
+
}
|
|
3711
|
+
},
|
|
3712
|
+
error: (err) => this.handleError(err),
|
|
3713
|
+
});
|
|
3714
|
+
}
|
|
3715
|
+
uploadMultipleFiles(files, strategy, metadataObj, optionsJson) {
|
|
3716
|
+
if (strategy === 'direct') {
|
|
3717
|
+
this.uploadMultipleFilesDirect(files, optionsJson, metadataObj);
|
|
3718
|
+
return;
|
|
3719
|
+
}
|
|
3720
|
+
from(files)
|
|
3721
|
+
.pipe(concatMap((file) => this.uploadSingleFileByStrategy(file, strategy, metadataObj, optionsJson)), toArray())
|
|
3722
|
+
.subscribe({
|
|
3723
|
+
next: (results) => {
|
|
3724
|
+
const responseData = this.buildBulkResponseData(results);
|
|
3725
|
+
this.bulkComplete.emit(responseData);
|
|
3726
|
+
this.lastResults = results.length ? results : null;
|
|
3727
|
+
this.lastStats = {
|
|
3728
|
+
total: responseData.totalProcessed,
|
|
3729
|
+
succeeded: responseData.totalSuccess,
|
|
3730
|
+
failed: responseData.totalFailed,
|
|
3731
|
+
};
|
|
3732
|
+
if (responseData.totalSuccess === 0 && responseData.totalFailed > 0) {
|
|
3733
|
+
const firstError = results.find((result) => result.error)?.error;
|
|
3734
|
+
const mappedError = firstError ? this.mapErrorFull(firstError) : null;
|
|
3735
|
+
this.errorMessage =
|
|
3736
|
+
mappedError?.message ||
|
|
3737
|
+
this.t('genericUploadError', 'Erro no envio de arquivo.');
|
|
3738
|
+
}
|
|
3739
|
+
this.finishUpload(true, undefined, results);
|
|
3740
|
+
},
|
|
3741
|
+
});
|
|
3742
|
+
}
|
|
3743
|
+
uploadMultipleFilesDirect(files, optionsJson, metadataObj) {
|
|
3744
|
+
const api = this.requireApiClient();
|
|
3745
|
+
const baseUrl = this.requireBaseUrl();
|
|
3746
|
+
const policy = this.conflictPolicy.value || 'RENAME';
|
|
3747
|
+
const bulkFiles = files.map((f) => ({
|
|
3748
|
+
file: f,
|
|
3749
|
+
metadata: metadataObj,
|
|
3750
|
+
conflictPolicy: policy,
|
|
3751
|
+
}));
|
|
3752
|
+
api.bulkUpload(baseUrl, bulkFiles, {
|
|
3753
|
+
optionsJson,
|
|
3754
|
+
failFast: this.config?.bulk?.failFast,
|
|
3755
|
+
retryCount: this.config?.bulk?.retryCount,
|
|
3756
|
+
}).subscribe({
|
|
3757
|
+
next: (event) => {
|
|
3758
|
+
if (event.type === HttpEventType.Response) {
|
|
3759
|
+
const responseData = event.body.data;
|
|
3760
|
+
this.bulkComplete.emit(responseData);
|
|
3761
|
+
const results = responseData.results;
|
|
3762
|
+
const stats = {
|
|
3763
|
+
total: responseData.totalProcessed,
|
|
3764
|
+
succeeded: responseData.totalSuccess,
|
|
3765
|
+
failed: responseData.totalFailed,
|
|
3766
|
+
};
|
|
3767
|
+
this.lastResults = results.length ? results : null;
|
|
3768
|
+
this.lastStats = stats;
|
|
3769
|
+
this.finishUpload(true, undefined, results);
|
|
2832
3770
|
}
|
|
2833
3771
|
else {
|
|
2834
3772
|
this.handleProgress(event);
|
|
@@ -2837,17 +3775,132 @@ class PraxisFilesUpload {
|
|
|
2837
3775
|
error: (err) => this.handleError(err),
|
|
2838
3776
|
});
|
|
2839
3777
|
}
|
|
3778
|
+
uploadSingleFileByStrategy(file, strategy, metadataObj, optionsJson) {
|
|
3779
|
+
if (!this.presign) {
|
|
3780
|
+
return this.uploadSingleFileDirectResult(file, metadataObj, optionsJson);
|
|
3781
|
+
}
|
|
3782
|
+
const api = this.requireApiClient();
|
|
3783
|
+
const baseUrl = this.requireBaseUrl();
|
|
3784
|
+
const uploadOptions = {
|
|
3785
|
+
optionsJson,
|
|
3786
|
+
metadata: metadataObj,
|
|
3787
|
+
conflictPolicy: this.conflictPolicy.value || 'RENAME',
|
|
3788
|
+
retryCount: this.config?.bulk?.retryCount,
|
|
3789
|
+
};
|
|
3790
|
+
return api
|
|
3791
|
+
.presign(baseUrl, file.name)
|
|
3792
|
+
.pipe(concatMap((target) => this.presign.upload(target, file, uploadOptions, this.config?.bulk?.retryCount ?? 0)), tap((event) => {
|
|
3793
|
+
if (event.type !== HttpEventType.Response) {
|
|
3794
|
+
this.handleProgress(event);
|
|
3795
|
+
}
|
|
3796
|
+
}), filter((event) => event.type === HttpEventType.Response), take(1), map((event) => {
|
|
3797
|
+
const responseEvent = event;
|
|
3798
|
+
const meta = this.mapUploadResponseToFileMetadata(responseEvent.body instanceof HttpResponse
|
|
3799
|
+
? responseEvent.body.body
|
|
3800
|
+
: responseEvent.body);
|
|
3801
|
+
if (!meta?.id) {
|
|
3802
|
+
return this.buildBulkFailureResult(file, this.createOperationalError('PRESIGN_METADATA_MISSING', this.t('presignMetadataMissing', 'Upload assinado concluído sem metadados do arquivo.')));
|
|
3803
|
+
}
|
|
3804
|
+
return this.buildBulkSuccessResult(file, meta);
|
|
3805
|
+
}), catchError((err) => {
|
|
3806
|
+
if (strategy === 'auto') {
|
|
3807
|
+
return this.uploadSingleFileDirectResult(file, metadataObj, optionsJson);
|
|
3808
|
+
}
|
|
3809
|
+
return of(this.buildBulkFailureResult(file, this.normalizeBulkError(err)));
|
|
3810
|
+
}), defaultIfEmpty(this.buildBulkFailureResult(file, this.createOperationalError('UPLOAD_RESPONSE_MISSING', this.t('genericUploadError', 'Erro no envio de arquivo.')))));
|
|
3811
|
+
}
|
|
3812
|
+
uploadSingleFileDirectResult(file, metadataObj, optionsJson) {
|
|
3813
|
+
const api = this.requireApiClient();
|
|
3814
|
+
const baseUrl = this.requireBaseUrl();
|
|
3815
|
+
return api
|
|
3816
|
+
.upload(baseUrl, file, {
|
|
3817
|
+
optionsJson,
|
|
3818
|
+
retryCount: this.config?.bulk?.retryCount,
|
|
3819
|
+
metadata: metadataObj,
|
|
3820
|
+
conflictPolicy: this.conflictPolicy.value || 'RENAME',
|
|
3821
|
+
})
|
|
3822
|
+
.pipe(tap((event) => {
|
|
3823
|
+
if (event.type !== HttpEventType.Response) {
|
|
3824
|
+
this.handleProgress(event);
|
|
3825
|
+
}
|
|
3826
|
+
}), filter((event) => event.type === HttpEventType.Response), take(1), map((event) => {
|
|
3827
|
+
const responseEvent = event;
|
|
3828
|
+
const meta = this.mapUploadResponseToFileMetadata(responseEvent.body);
|
|
3829
|
+
if (!meta?.id) {
|
|
3830
|
+
return this.buildBulkFailureResult(file, this.createOperationalError('UPLOAD_RESPONSE_MISSING', this.t('genericUploadError', 'Erro no envio de arquivo.')));
|
|
3831
|
+
}
|
|
3832
|
+
return this.buildBulkSuccessResult(file, meta);
|
|
3833
|
+
}), catchError((err) => of(this.buildBulkFailureResult(file, this.normalizeBulkError(err)))), defaultIfEmpty(this.buildBulkFailureResult(file, this.createOperationalError('UPLOAD_RESPONSE_MISSING', this.t('genericUploadError', 'Erro no envio de arquivo.')))));
|
|
3834
|
+
}
|
|
3835
|
+
buildBulkResponseData(results) {
|
|
3836
|
+
const totalSuccess = results.filter((result) => result.status === BulkUploadResultStatus.SUCCESS).length;
|
|
3837
|
+
const totalFailed = results.length - totalSuccess;
|
|
3838
|
+
return {
|
|
3839
|
+
totalProcessed: results.length,
|
|
3840
|
+
totalSuccess,
|
|
3841
|
+
totalFailed,
|
|
3842
|
+
overallSuccess: totalFailed === 0,
|
|
3843
|
+
results,
|
|
3844
|
+
};
|
|
3845
|
+
}
|
|
3846
|
+
buildBulkSuccessResult(file, meta) {
|
|
3847
|
+
return {
|
|
3848
|
+
fileName: file.name,
|
|
3849
|
+
status: BulkUploadResultStatus.SUCCESS,
|
|
3850
|
+
file: meta,
|
|
3851
|
+
};
|
|
3852
|
+
}
|
|
3853
|
+
buildBulkFailureResult(file, error) {
|
|
3854
|
+
return {
|
|
3855
|
+
fileName: file.name,
|
|
3856
|
+
status: BulkUploadResultStatus.FAILED,
|
|
3857
|
+
error,
|
|
3858
|
+
};
|
|
3859
|
+
}
|
|
3860
|
+
normalizeBulkError(err) {
|
|
3861
|
+
if (err instanceof HttpErrorResponse && err.error) {
|
|
3862
|
+
return err.error;
|
|
3863
|
+
}
|
|
3864
|
+
return this.createOperationalError('UPLOAD_OPERATION_FAILED', this.t('genericUploadError', 'Erro no envio de arquivo.'), err);
|
|
3865
|
+
}
|
|
3866
|
+
createOperationalError(code, message, details) {
|
|
3867
|
+
return {
|
|
3868
|
+
code,
|
|
3869
|
+
message,
|
|
3870
|
+
details,
|
|
3871
|
+
};
|
|
3872
|
+
}
|
|
3873
|
+
normalizeErrorEnvelope(err) {
|
|
3874
|
+
if (err &&
|
|
3875
|
+
typeof err === 'object' &&
|
|
3876
|
+
'status' in err &&
|
|
3877
|
+
'message' in err &&
|
|
3878
|
+
'errors' in err) {
|
|
3879
|
+
return err;
|
|
3880
|
+
}
|
|
3881
|
+
const message = err instanceof Error && err.message
|
|
3882
|
+
? err.message
|
|
3883
|
+
: this.t('genericUploadError', 'Erro no envio de arquivo.');
|
|
3884
|
+
return {
|
|
3885
|
+
status: 'failure',
|
|
3886
|
+
message,
|
|
3887
|
+
errors: [
|
|
3888
|
+
this.createOperationalError('UPLOAD_OPERATION_FAILED', message, err),
|
|
3889
|
+
],
|
|
3890
|
+
timestamp: new Date().toISOString(),
|
|
3891
|
+
};
|
|
3892
|
+
}
|
|
2840
3893
|
buildOptionsJson(customMeta) {
|
|
2841
3894
|
const effective = this.serverEffective?.options;
|
|
2842
3895
|
const nameConflictPolicy = this.conflictPolicy.value ||
|
|
2843
|
-
this.config?.
|
|
3896
|
+
this.config?.options?.defaultConflictPolicy ||
|
|
2844
3897
|
effective?.nameConflictPolicy ||
|
|
2845
3898
|
'RENAME';
|
|
2846
3899
|
const limits = this.config?.limits ?? {};
|
|
2847
3900
|
const advanced = this.config?.options ?? {};
|
|
2848
3901
|
// Garantir maxUploadSizeMb positivo usando fallback do servidor ou default 50
|
|
2849
|
-
const maxUploadSizeMb = typeof
|
|
2850
|
-
?
|
|
3902
|
+
const maxUploadSizeMb = typeof advanced.maxUploadSizeMb === 'number'
|
|
3903
|
+
? advanced.maxUploadSizeMb
|
|
2851
3904
|
: typeof effective?.maxUploadSizeMb === 'number'
|
|
2852
3905
|
? effective?.maxUploadSizeMb
|
|
2853
3906
|
: 50;
|
|
@@ -2856,8 +3909,8 @@ class PraxisFilesUpload {
|
|
|
2856
3909
|
options.maxUploadSizeMb = maxUploadSizeMb;
|
|
2857
3910
|
}
|
|
2858
3911
|
// strictValidation: fallback para server
|
|
2859
|
-
if (typeof
|
|
2860
|
-
options.strictValidation =
|
|
3912
|
+
if (typeof advanced.strictValidation === 'boolean') {
|
|
3913
|
+
options.strictValidation = advanced.strictValidation;
|
|
2861
3914
|
}
|
|
2862
3915
|
else if (typeof effective?.strictValidation === 'boolean') {
|
|
2863
3916
|
options.strictValidation = effective.strictValidation;
|
|
@@ -2926,8 +3979,8 @@ class PraxisFilesUpload {
|
|
|
2926
3979
|
this.currentFiles = [];
|
|
2927
3980
|
}
|
|
2928
3981
|
if (err instanceof HttpErrorResponse && err.error) {
|
|
2929
|
-
const
|
|
2930
|
-
|
|
3982
|
+
const errorPayload = err.error;
|
|
3983
|
+
const mapped = this.errors?.map(errorPayload, err.headers) || err.error;
|
|
2931
3984
|
this.errorMessage =
|
|
2932
3985
|
mapped.message ||
|
|
2933
3986
|
this.t('genericUploadError', 'Erro no envio de arquivo.');
|
|
@@ -2935,17 +3988,17 @@ class PraxisFilesUpload {
|
|
|
2935
3988
|
this.rateLimit = mapped.rateLimit;
|
|
2936
3989
|
this.rateLimited.emit(mapped.rateLimit);
|
|
2937
3990
|
}
|
|
2938
|
-
const
|
|
3991
|
+
const primary = getPrimaryErrorItem(errorPayload);
|
|
3992
|
+
const code = String(primary?.code || '');
|
|
2939
3993
|
if (code === 'QUOTA_EXCEEDED' || code === 'COTA_EXCEDIDA') {
|
|
2940
3994
|
this.quotaExceeded = true;
|
|
2941
3995
|
}
|
|
2942
|
-
this.error.emit(
|
|
2943
|
-
this.uploadError.emit(err.error);
|
|
3996
|
+
this.error.emit(errorPayload);
|
|
2944
3997
|
}
|
|
2945
3998
|
else {
|
|
2946
|
-
|
|
2947
|
-
this.
|
|
2948
|
-
this.
|
|
3999
|
+
const normalizedError = this.normalizeErrorEnvelope(err);
|
|
4000
|
+
this.errorMessage = normalizedError.message;
|
|
4001
|
+
this.error.emit(normalizedError);
|
|
2949
4002
|
}
|
|
2950
4003
|
}
|
|
2951
4004
|
finishUpload(success, meta, results) {
|
|
@@ -2954,8 +4007,7 @@ class PraxisFilesUpload {
|
|
|
2954
4007
|
if (success) {
|
|
2955
4008
|
if (meta) {
|
|
2956
4009
|
this.uploadedFiles.push({ fileName: meta.fileName, success: true });
|
|
2957
|
-
this.uploadSuccess.emit(
|
|
2958
|
-
// Opcional: preencher lista rica também para single
|
|
4010
|
+
this.uploadSuccess.emit(meta);
|
|
2959
4011
|
this.lastResults = [
|
|
2960
4012
|
{
|
|
2961
4013
|
fileName: meta.fileName,
|
|
@@ -2966,7 +4018,6 @@ class PraxisFilesUpload {
|
|
|
2966
4018
|
this.lastStats = { total: 1, succeeded: 1, failed: 0 };
|
|
2967
4019
|
}
|
|
2968
4020
|
if (results) {
|
|
2969
|
-
// Mantém lista simples para compatibilidade
|
|
2970
4021
|
for (const r of results) {
|
|
2971
4022
|
if (r.status === BulkUploadResultStatus.SUCCESS) {
|
|
2972
4023
|
this.uploadedFiles.push({ fileName: r.fileName, success: true });
|
|
@@ -2975,7 +4026,6 @@ class PraxisFilesUpload {
|
|
|
2975
4026
|
this.uploadedFiles.push({ fileName: r.fileName, success: false });
|
|
2976
4027
|
}
|
|
2977
4028
|
}
|
|
2978
|
-
// Se stats não vieram do servidor, calcula minimamente
|
|
2979
4029
|
if (!this.lastStats) {
|
|
2980
4030
|
const succeeded = results.filter((r) => r.status === BulkUploadResultStatus.SUCCESS).length;
|
|
2981
4031
|
const failed = results.length - succeeded;
|
|
@@ -2996,6 +4046,31 @@ class PraxisFilesUpload {
|
|
|
2996
4046
|
this.hideSelectedOverlay();
|
|
2997
4047
|
this.cdr.markForCheck();
|
|
2998
4048
|
}
|
|
4049
|
+
mapUploadResponseToFileMetadata(resp) {
|
|
4050
|
+
const payload = resp?.data;
|
|
4051
|
+
if (!payload) {
|
|
4052
|
+
return undefined;
|
|
4053
|
+
}
|
|
4054
|
+
return {
|
|
4055
|
+
id: payload.fileId || '',
|
|
4056
|
+
fileName: payload.originalFilename,
|
|
4057
|
+
contentType: payload.mimeType || '',
|
|
4058
|
+
fileSize: payload.fileSize,
|
|
4059
|
+
uploadedAt: payload.uploadTimestamp,
|
|
4060
|
+
tenantId: '',
|
|
4061
|
+
scanStatus: ScanStatus.PENDING,
|
|
4062
|
+
};
|
|
4063
|
+
}
|
|
4064
|
+
primaryErrorCode(error) {
|
|
4065
|
+
return getPrimaryErrorItem(error)?.code ?? 'UNKNOWN_ERROR';
|
|
4066
|
+
}
|
|
4067
|
+
traceIdOf(error) {
|
|
4068
|
+
if (!error || typeof error !== 'object' || !('traceId' in error)) {
|
|
4069
|
+
return undefined;
|
|
4070
|
+
}
|
|
4071
|
+
const traceId = error.traceId;
|
|
4072
|
+
return typeof traceId === 'string' ? traceId : undefined;
|
|
4073
|
+
}
|
|
2999
4074
|
// Lista rica - helpers/ações
|
|
3000
4075
|
get visibleResults() {
|
|
3001
4076
|
if (!this.lastResults)
|
|
@@ -3104,7 +4179,6 @@ class PraxisFilesUpload {
|
|
|
3104
4179
|
this.t('maxBulkSizeError', 'Tamanho total dos arquivos excedido.');
|
|
3105
4180
|
}
|
|
3106
4181
|
this.error.emit(errors);
|
|
3107
|
-
this.uploadError.emit(errors);
|
|
3108
4182
|
return false;
|
|
3109
4183
|
}
|
|
3110
4184
|
return true;
|
|
@@ -3113,12 +4187,12 @@ class PraxisFilesUpload {
|
|
|
3113
4187
|
get policiesSummary() {
|
|
3114
4188
|
const acc = this.config?.ui?.accept?.length
|
|
3115
4189
|
? this.config.ui.accept.join(', ')
|
|
3116
|
-
: this.acceptString || 'qualquer';
|
|
4190
|
+
: this.acceptString || this.t('policySummaryAny', 'qualquer');
|
|
3117
4191
|
const maxFile = this.config?.limits?.maxFileSizeBytes
|
|
3118
4192
|
? this.formatBytes(this.config.limits.maxFileSizeBytes)
|
|
3119
|
-
: '—';
|
|
4193
|
+
: this.t('policySummaryNotConfigured', '—');
|
|
3120
4194
|
const maxCount = this.config?.limits?.maxFilesPerBulk ?? (this.allowMultiple ? 99 : 1);
|
|
3121
|
-
return
|
|
4195
|
+
return `${this.t('policySummaryTypesLabel', 'Tipos')}: ${acc} • ${this.t('policySummaryMaxPerFileLabel', 'Máx. por arquivo')}: ${maxFile} • ${this.t('policySummaryQuantityLabel', 'Qtde')}: ${maxCount}`;
|
|
3122
4196
|
}
|
|
3123
4197
|
resolveTemplateString(value) {
|
|
3124
4198
|
if (!this.context || !value.includes('${'))
|
|
@@ -3137,29 +4211,37 @@ class PraxisFilesUpload {
|
|
|
3137
4211
|
return acc[key];
|
|
3138
4212
|
}, context);
|
|
3139
4213
|
}
|
|
3140
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilesUpload, deps: [{ token: i1$
|
|
3141
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisFilesUpload, isStandalone: true, selector: "praxis-files-upload", inputs: { config: "config", filesUploadId: "filesUploadId",
|
|
3142
|
-
<div
|
|
4214
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilesUpload, deps: [{ token: i1$3.SettingsPanelService }, { token: DragProximityService }, { token: i3.Overlay }, { token: i0.ViewContainerRef }, { token: i0.ChangeDetectorRef }, { token: ASYNC_CONFIG_STORAGE }, { token: i1$2.PraxisI18nService }, { token: FILES_UPLOAD_TEXTS, optional: true }, { token: TRANSLATE_LIKE, optional: true }, { token: ConfigService, optional: true }, { token: FilesApiClient, optional: true }, { token: PresignedUploaderService, optional: true }, { token: ErrorMapperService, optional: true }], target: i0.ɵɵFactoryTarget.Component });
|
|
4215
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PraxisFilesUpload, isStandalone: true, selector: "praxis-files-upload", inputs: { config: "config", filesUploadId: "filesUploadId", componentInstanceId: "componentInstanceId", baseUrl: "baseUrl", displayMode: "displayMode", context: "context", editModeEnabled: "editModeEnabled" }, outputs: { uploadSuccess: "uploadSuccess", bulkComplete: "bulkComplete", error: "error", rateLimited: "rateLimited", uploadStart: "uploadStart", uploadProgress: "uploadProgress", retry: "retry", download: "download", copyLink: "copyLink", detailsOpened: "detailsOpened", detailsClosed: "detailsClosed", pendingStateChange: "pendingStateChange", proximityChange: "proximityChange", readinessChange: "readinessChange" }, providers: [providePraxisFilesUploadI18n()], viewQueries: [{ propertyName: "fileInput", first: true, predicate: ["fileInput"], descendants: true }, { propertyName: "hostRef", first: true, predicate: ["host"], descendants: true, static: true }, { propertyName: "overlayTmpl", first: true, predicate: ["proximityOverlayTmpl"], descendants: true }, { propertyName: "selectedOverlayTmpl", first: true, predicate: ["selectedOverlayTmpl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: `
|
|
4216
|
+
<div
|
|
4217
|
+
class="praxis-files-upload"
|
|
4218
|
+
[class.dense]="config?.ui?.dense"
|
|
4219
|
+
[attr.data-files-upload-id]="a11yBaseId"
|
|
4220
|
+
[attr.data-ready-state]="readinessState"
|
|
4221
|
+
[attr.data-ready-source]="readinessSource"
|
|
4222
|
+
[attr.aria-busy]="readinessState === 'loading' ? 'true' : 'false'"
|
|
4223
|
+
#host
|
|
4224
|
+
>
|
|
3143
4225
|
<!-- Proximity overlay inline (fallback quando expandMode === 'inline') -->
|
|
3144
|
-
|
|
3145
|
-
|
|
4226
|
+
<div
|
|
4227
|
+
class="proximity-overlay"
|
|
3146
4228
|
*ngIf="
|
|
3147
4229
|
showProximityOverlay &&
|
|
3148
4230
|
(config?.ui?.dropzone?.expandMode ?? 'overlay') === 'inline'
|
|
3149
4231
|
"
|
|
3150
4232
|
[style.height.px]="config?.ui?.dropzone?.expandHeight ?? 200"
|
|
3151
4233
|
aria-hidden="true"
|
|
3152
|
-
|
|
3153
|
-
|
|
3154
|
-
|
|
3155
|
-
|
|
3156
|
-
|
|
4234
|
+
>
|
|
4235
|
+
<mat-icon class="dz-icon" aria-hidden="true" [praxisIcon]="'cloud_upload'"></mat-icon>
|
|
4236
|
+
<p class="dz-title">{{ texts.dropzoneLabel }}</p>
|
|
4237
|
+
<p class="dz-hint">{{ t('dropzoneProximityHint', 'Solte aqui para enviar') }}</p>
|
|
4238
|
+
</div>
|
|
3157
4239
|
<!-- Template do overlay de proximidade (renderizado via CDK Overlay) -->
|
|
3158
4240
|
<ng-template #proximityOverlayTmpl>
|
|
3159
4241
|
<div class="proximity-overlay" aria-hidden="true">
|
|
3160
4242
|
<mat-icon class="dz-icon" aria-hidden="true" [praxisIcon]="'cloud_upload'"></mat-icon>
|
|
3161
4243
|
<p class="dz-title">{{ texts.dropzoneLabel }}</p>
|
|
3162
|
-
<p class="dz-hint">Solte aqui para enviar</p>
|
|
4244
|
+
<p class="dz-hint">{{ t('dropzoneProximityHint', 'Solte aqui para enviar') }}</p>
|
|
3163
4245
|
</div>
|
|
3164
4246
|
</ng-template>
|
|
3165
4247
|
|
|
@@ -3168,22 +4250,22 @@ class PraxisFilesUpload {
|
|
|
3168
4250
|
<ng-template #selectedOverlayTmpl>
|
|
3169
4251
|
<div
|
|
3170
4252
|
class="selected-overlay"
|
|
3171
|
-
role="
|
|
3172
|
-
aria-
|
|
4253
|
+
role="region"
|
|
4254
|
+
[attr.aria-labelledby]="selectedOverlayTitleId"
|
|
3173
4255
|
>
|
|
3174
4256
|
<header class="sel-header">
|
|
3175
|
-
<span class="title">Selecionados ({{ pendingCount }})</span>
|
|
4257
|
+
<span class="title" [id]="selectedOverlayTitleId">{{ t('selectedOverlayTitle', 'Selecionados') }} ({{ pendingCount }})</span>
|
|
3176
4258
|
<span class="spacer"></span>
|
|
3177
4259
|
<button
|
|
3178
4260
|
mat-stroked-button
|
|
3179
4261
|
color="primary"
|
|
3180
|
-
(click)="
|
|
4262
|
+
(click)="submitPendingFiles()"
|
|
3181
4263
|
[disabled]="!baseUrl || isUploading"
|
|
3182
4264
|
>
|
|
3183
|
-
Enviar
|
|
4265
|
+
{{ t('fieldActionUploadShort', 'Enviar') }}
|
|
3184
4266
|
</button>
|
|
3185
|
-
<button mat-button type="button" (click)="
|
|
3186
|
-
Cancelar
|
|
4267
|
+
<button mat-button type="button" (click)="resetPendingFiles()">
|
|
4268
|
+
{{ t('fieldActionCancelShort', 'Cancelar') }}
|
|
3187
4269
|
</button>
|
|
3188
4270
|
</header>
|
|
3189
4271
|
<ul class="sel-list">
|
|
@@ -3204,7 +4286,7 @@ class PraxisFilesUpload {
|
|
|
3204
4286
|
class="remove-btn"
|
|
3205
4287
|
[matTooltip]="t('remove', 'Remover')"
|
|
3206
4288
|
(click)="removePendingFile(f)"
|
|
3207
|
-
[attr.aria-label]="
|
|
4289
|
+
[attr.aria-label]="pendingFileRemoveAriaLabel(f)"
|
|
3208
4290
|
>
|
|
3209
4291
|
<mat-icon [praxisIcon]="'close'"></mat-icon>
|
|
3210
4292
|
</button>
|
|
@@ -3215,7 +4297,7 @@ class PraxisFilesUpload {
|
|
|
3215
4297
|
[matMenuTriggerData]="{ file: f }"
|
|
3216
4298
|
aria-haspopup="menu"
|
|
3217
4299
|
[matTooltip]="t('moreActions', 'Mais ações')"
|
|
3218
|
-
[attr.aria-label]="
|
|
4300
|
+
[attr.aria-label]="pendingFileMoreActionsAriaLabel(f)"
|
|
3219
4301
|
>
|
|
3220
4302
|
<mat-icon [praxisIcon]="'more_horiz'"></mat-icon>
|
|
3221
4303
|
</button>
|
|
@@ -3256,6 +4338,9 @@ class PraxisFilesUpload {
|
|
|
3256
4338
|
<div
|
|
3257
4339
|
class="rate-limit-banner"
|
|
3258
4340
|
*ngIf="rateLimit && config?.rateLimit?.showBannerOn429 !== false"
|
|
4341
|
+
[id]="messageRegionId"
|
|
4342
|
+
role="alert"
|
|
4343
|
+
aria-live="assertive"
|
|
3259
4344
|
>
|
|
3260
4345
|
{{ texts.rateLimitBanner }}
|
|
3261
4346
|
{{ rateLimit.resetEpochSeconds * 1000 | date: 'shortTime' }}.
|
|
@@ -3263,32 +4348,34 @@ class PraxisFilesUpload {
|
|
|
3263
4348
|
<div
|
|
3264
4349
|
class="quota-banner"
|
|
3265
4350
|
*ngIf="quotaExceeded && config?.quotas?.showQuotaWarnings !== false"
|
|
4351
|
+
[id]="messageRegionId"
|
|
4352
|
+
role="status"
|
|
4353
|
+
aria-live="polite"
|
|
3266
4354
|
>
|
|
3267
4355
|
{{ errorMessage }}
|
|
3268
4356
|
</div>
|
|
3269
|
-
<div
|
|
4357
|
+
<div
|
|
4358
|
+
class="error"
|
|
4359
|
+
*ngIf="errorMessage && !quotaExceeded"
|
|
4360
|
+
[id]="messageRegionId"
|
|
4361
|
+
role="alert"
|
|
4362
|
+
aria-live="assertive"
|
|
4363
|
+
>
|
|
3270
4364
|
{{ errorMessage }}
|
|
3271
4365
|
</div>
|
|
3272
|
-
<div
|
|
3273
|
-
|
|
4366
|
+
<div
|
|
4367
|
+
class="error"
|
|
4368
|
+
*ngIf="!baseUrl"
|
|
4369
|
+
[id]="messageRegionId"
|
|
4370
|
+
role="alert"
|
|
4371
|
+
aria-live="assertive"
|
|
4372
|
+
>
|
|
4373
|
+
{{ t('baseUrlMissing', 'Base URL not configured. Provide [baseUrl] to enable uploads.') }}
|
|
3274
4374
|
</div>
|
|
3275
4375
|
<div
|
|
3276
4376
|
class="dropzone"
|
|
3277
4377
|
*ngIf="config?.ui?.showDropzone !== false"
|
|
3278
|
-
|
|
3279
|
-
!baseUrl ||
|
|
3280
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3281
|
-
isUploading
|
|
3282
|
-
? null
|
|
3283
|
-
: 'button'
|
|
3284
|
-
"
|
|
3285
|
-
[attr.tabindex]="
|
|
3286
|
-
!baseUrl ||
|
|
3287
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3288
|
-
isUploading
|
|
3289
|
-
? -1
|
|
3290
|
-
: 0
|
|
3291
|
-
"
|
|
4378
|
+
role="group"
|
|
3292
4379
|
[class.dragover]="isDragging"
|
|
3293
4380
|
[class.disabled]="
|
|
3294
4381
|
!baseUrl ||
|
|
@@ -3302,39 +4389,18 @@ class PraxisFilesUpload {
|
|
|
3302
4389
|
? 'true'
|
|
3303
4390
|
: 'false'
|
|
3304
4391
|
"
|
|
3305
|
-
[attr.aria-label]="texts.dropzoneLabel
|
|
3306
|
-
|
|
3307
|
-
!baseUrl ||
|
|
3308
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3309
|
-
isUploading
|
|
3310
|
-
? null
|
|
3311
|
-
: openFile(fileInput)
|
|
3312
|
-
"
|
|
4392
|
+
[attr.aria-label]="texts.dropzoneLabel"
|
|
4393
|
+
[attr.aria-describedby]="messageRegionId"
|
|
3313
4394
|
(drop)="onDrop($event)"
|
|
3314
4395
|
(dragover)="onDragOver($event)"
|
|
3315
4396
|
(dragleave)="onDragLeave($event)"
|
|
3316
|
-
(keydown.enter)="
|
|
3317
|
-
$event.preventDefault();
|
|
3318
|
-
!baseUrl ||
|
|
3319
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3320
|
-
isUploading
|
|
3321
|
-
? null
|
|
3322
|
-
: openFile(fileInput)
|
|
3323
|
-
"
|
|
3324
|
-
(keydown.space)="
|
|
3325
|
-
$event.preventDefault();
|
|
3326
|
-
!baseUrl ||
|
|
3327
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3328
|
-
isUploading
|
|
3329
|
-
? null
|
|
3330
|
-
: openFile(fileInput)
|
|
3331
|
-
"
|
|
3332
4397
|
>
|
|
3333
4398
|
<!-- Ações internas (ícones) -->
|
|
3334
4399
|
<div class="field-actions" aria-hidden="false">
|
|
3335
4400
|
<button
|
|
3336
4401
|
mat-icon-button
|
|
3337
|
-
[matTooltip]="'
|
|
4402
|
+
[matTooltip]="t('selectFiles', 'Select file(s)')"
|
|
4403
|
+
[attr.aria-label]="selectFilesAriaLabel"
|
|
3338
4404
|
(click)="$event.stopPropagation(); openFile(fileInput)"
|
|
3339
4405
|
[disabled]="!baseUrl || isUploading"
|
|
3340
4406
|
>
|
|
@@ -3343,6 +4409,7 @@ class PraxisFilesUpload {
|
|
|
3343
4409
|
<button
|
|
3344
4410
|
mat-icon-button
|
|
3345
4411
|
[matTooltip]="policiesSummary"
|
|
4412
|
+
[attr.aria-label]="policySummaryAriaLabel"
|
|
3346
4413
|
(click)="$event.stopPropagation()"
|
|
3347
4414
|
[disabled]="false"
|
|
3348
4415
|
>
|
|
@@ -3353,9 +4420,15 @@ class PraxisFilesUpload {
|
|
|
3353
4420
|
<mat-icon class="dz-icon" aria-hidden="true" [praxisIcon]="'cloud_upload'"></mat-icon>
|
|
3354
4421
|
<p class="dz-title">{{ texts.dropzoneLabel }}</p>
|
|
3355
4422
|
<p class="dz-hint">
|
|
3356
|
-
Arraste e solte arquivos aqui ou clique para selecionar
|
|
4423
|
+
{{ t('dropzoneHint', 'Arraste e solte arquivos aqui ou clique para selecionar') }}
|
|
3357
4424
|
</p>
|
|
3358
|
-
<button
|
|
4425
|
+
<button
|
|
4426
|
+
type="button"
|
|
4427
|
+
mat-stroked-button
|
|
4428
|
+
[disabled]="!baseUrl || isUploading"
|
|
4429
|
+
[attr.aria-describedby]="messageRegionId"
|
|
4430
|
+
(click)="openFile(fileInput)"
|
|
4431
|
+
>
|
|
3359
4432
|
{{ texts.dropzoneButton }}
|
|
3360
4433
|
</button>
|
|
3361
4434
|
</div>
|
|
@@ -3375,8 +4448,8 @@ class PraxisFilesUpload {
|
|
|
3375
4448
|
class="conflict-policy"
|
|
3376
4449
|
*ngIf="config?.ui?.showConflictPolicySelector"
|
|
3377
4450
|
>
|
|
3378
|
-
<label for="
|
|
3379
|
-
<select id="
|
|
4451
|
+
<label [for]="conflictPolicyInputId">{{ texts.conflictPolicyLabel }}</label>
|
|
4452
|
+
<select [id]="conflictPolicyInputId" [formControl]="conflictPolicy">
|
|
3380
4453
|
<option *ngFor="let p of conflictPolicies" [value]="p">
|
|
3381
4454
|
{{ p }}
|
|
3382
4455
|
</option>
|
|
@@ -3384,18 +4457,27 @@ class PraxisFilesUpload {
|
|
|
3384
4457
|
</div>
|
|
3385
4458
|
|
|
3386
4459
|
<div class="metadata" *ngIf="config?.ui?.showMetadataForm">
|
|
3387
|
-
<label for="
|
|
3388
|
-
<textarea id="
|
|
4460
|
+
<label [for]="metadataInputId">{{ texts.metadataLabel }}</label>
|
|
4461
|
+
<textarea [id]="metadataInputId" [formControl]="metadata"></textarea>
|
|
3389
4462
|
<div class="error" *ngIf="metadataError">{{ metadataError }}</div>
|
|
3390
4463
|
</div>
|
|
3391
4464
|
|
|
3392
|
-
<div
|
|
4465
|
+
<div
|
|
4466
|
+
class="progress"
|
|
4467
|
+
*ngIf="showProgress"
|
|
4468
|
+
[id]="progressRegionId"
|
|
4469
|
+
role="status"
|
|
4470
|
+
aria-live="polite"
|
|
4471
|
+
>
|
|
3393
4472
|
<mat-progress-bar
|
|
3394
4473
|
mode="determinate"
|
|
3395
4474
|
[value]="uploadProgressValue"
|
|
3396
4475
|
[attr.aria-label]="texts.progressAriaLabel"
|
|
4476
|
+
[attr.aria-valuemin]="0"
|
|
4477
|
+
[attr.aria-valuemax]="100"
|
|
4478
|
+
[attr.aria-valuenow]="uploadProgressValue"
|
|
3397
4479
|
></mat-progress-bar>
|
|
3398
|
-
<span class="sr-only">{{ uploadProgressValue }}%</span>
|
|
4480
|
+
<span class="sr-only">{{ texts.progressAriaLabel }} {{ uploadProgressValue }}%</span>
|
|
3399
4481
|
</div>
|
|
3400
4482
|
|
|
3401
4483
|
<ul class="file-feedback" *ngIf="hasUploadedFiles">
|
|
@@ -3410,17 +4492,17 @@ class PraxisFilesUpload {
|
|
|
3410
4492
|
<section class="bulk-results" *ngIf="hasLastResults">
|
|
3411
4493
|
<header class="bulk-summary" *ngIf="lastStats">
|
|
3412
4494
|
<div class="stat">
|
|
3413
|
-
<span class="label">Total</span>
|
|
4495
|
+
<span class="label">{{ t('bulkSummaryTotal', 'Total') }}</span>
|
|
3414
4496
|
<span class="value">{{ lastStats.total }}</span>
|
|
3415
4497
|
</div>
|
|
3416
4498
|
<div class="stat success">
|
|
3417
4499
|
<mat-icon [praxisIcon]="'check_circle'"></mat-icon>
|
|
3418
|
-
<span class="label">Sucesso</span>
|
|
4500
|
+
<span class="label">{{ t('bulkSummarySuccess', 'Sucesso') }}</span>
|
|
3419
4501
|
<span class="value">{{ lastStats.succeeded }}</span>
|
|
3420
4502
|
</div>
|
|
3421
4503
|
<div class="stat failed">
|
|
3422
4504
|
<mat-icon [praxisIcon]="'error'"></mat-icon>
|
|
3423
|
-
<span class="label">Falhas</span>
|
|
4505
|
+
<span class="label">{{ t('bulkSummaryFailed', 'Falhas') }}</span>
|
|
3424
4506
|
<span class="value">{{ lastStats.failed }}</span>
|
|
3425
4507
|
</div>
|
|
3426
4508
|
</header>
|
|
@@ -3451,19 +4533,24 @@ class PraxisFilesUpload {
|
|
|
3451
4533
|
>
|
|
3452
4534
|
</div>
|
|
3453
4535
|
<div class="meta" *ngIf="r.file">
|
|
3454
|
-
<span *ngIf="r.file.id"
|
|
4536
|
+
<span *ngIf="r.file.id"
|
|
4537
|
+
>{{ t('resultMetaIdLabel', 'ID') }}: {{ r.file.id }}</span
|
|
4538
|
+
>
|
|
3455
4539
|
<span *ngIf="r.file.contentType"
|
|
3456
|
-
>
|
|
4540
|
+
>{{ t('resultMetaTypeLabel', 'Tipo') }}:
|
|
4541
|
+
{{ r.file.contentType }}</span
|
|
3457
4542
|
>
|
|
3458
4543
|
<span *ngIf="r.file.fileSize"
|
|
3459
|
-
>
|
|
4544
|
+
>{{ t('resultMetaSizeLabel', 'Tamanho') }}:
|
|
4545
|
+
{{ formatBytes(r.file.fileSize) }}</span
|
|
3460
4546
|
>
|
|
3461
4547
|
<span *ngIf="r.file.uploadedAt"
|
|
3462
|
-
>
|
|
4548
|
+
>{{ t('resultMetaUploadedAtLabel', 'Enviado em') }}:
|
|
4549
|
+
{{ r.file.uploadedAt | date: 'short' }}</span
|
|
3463
4550
|
>
|
|
3464
4551
|
</div>
|
|
3465
4552
|
<div class="error" *ngIf="r.error as e">
|
|
3466
|
-
<span class="code">{{ e
|
|
4553
|
+
<span class="code">{{ primaryErrorCode(e) }}</span>
|
|
3467
4554
|
<ng-container *ngIf="mapErrorFull(e) as me">
|
|
3468
4555
|
<span class="title" *ngIf="me.title">{{ me.title }}:</span>
|
|
3469
4556
|
<span class="msg">{{ me.message }}</span>
|
|
@@ -3471,13 +4558,13 @@ class PraxisFilesUpload {
|
|
|
3471
4558
|
— {{ me.userAction }}</span
|
|
3472
4559
|
>
|
|
3473
4560
|
</ng-container>
|
|
3474
|
-
<span class="trace" *ngIf="e
|
|
3475
|
-
>(trace {{
|
|
4561
|
+
<span class="trace" *ngIf="traceIdOf(e) as traceId"
|
|
4562
|
+
>(trace {{ traceId }})</span
|
|
3476
4563
|
>
|
|
3477
4564
|
</div>
|
|
3478
4565
|
<div class="extra" *ngIf="r.file?.metadata as m">
|
|
3479
4566
|
<details (toggle)="onDetailsToggle($event, r)">
|
|
3480
|
-
<summary>Metadados</summary>
|
|
4567
|
+
<summary>{{ t('detailsMetadata', 'Metadados') }}</summary>
|
|
3481
4568
|
<pre>{{ m | json }}</pre>
|
|
3482
4569
|
</details>
|
|
3483
4570
|
</div>
|
|
@@ -3485,14 +4572,16 @@ class PraxisFilesUpload {
|
|
|
3485
4572
|
<div class="item-actions">
|
|
3486
4573
|
<button
|
|
3487
4574
|
mat-icon-button
|
|
3488
|
-
[matTooltip]="'Detalhes'"
|
|
4575
|
+
[matTooltip]="t('details', 'Detalhes')"
|
|
4576
|
+
[attr.aria-label]="resultActionAriaLabel('details', 'Detalhes', r.fileName)"
|
|
3489
4577
|
(click)="toggleDetailsFor(r)"
|
|
3490
4578
|
>
|
|
3491
4579
|
<mat-icon [praxisIcon]="'info'"></mat-icon>
|
|
3492
4580
|
</button>
|
|
3493
4581
|
<button
|
|
3494
4582
|
mat-icon-button
|
|
3495
|
-
[matTooltip]="'Reenviar'"
|
|
4583
|
+
[matTooltip]="t('retry', 'Reenviar')"
|
|
4584
|
+
[attr.aria-label]="resultActionAriaLabel('retry', 'Reenviar', r.fileName)"
|
|
3496
4585
|
(click)="onRetry(r)"
|
|
3497
4586
|
[disabled]="isUploading"
|
|
3498
4587
|
>
|
|
@@ -3500,7 +4589,8 @@ class PraxisFilesUpload {
|
|
|
3500
4589
|
</button>
|
|
3501
4590
|
<button
|
|
3502
4591
|
mat-icon-button
|
|
3503
|
-
[matTooltip]="'Baixar'"
|
|
4592
|
+
[matTooltip]="t('download', 'Baixar')"
|
|
4593
|
+
[attr.aria-label]="resultActionAriaLabel('download', 'Baixar', r.fileName)"
|
|
3504
4594
|
(click)="onDownload(r)"
|
|
3505
4595
|
[disabled]="r.status !== BulkUploadResultStatus.SUCCESS"
|
|
3506
4596
|
>
|
|
@@ -3508,7 +4598,8 @@ class PraxisFilesUpload {
|
|
|
3508
4598
|
</button>
|
|
3509
4599
|
<button
|
|
3510
4600
|
mat-icon-button
|
|
3511
|
-
[matTooltip]="'Copiar link'"
|
|
4601
|
+
[matTooltip]="t('copyLink', 'Copiar link')"
|
|
4602
|
+
[attr.aria-label]="resultActionAriaLabel('copyLink', 'Copiar link', r.fileName)"
|
|
3512
4603
|
(click)="onCopyLink(r)"
|
|
3513
4604
|
[disabled]="r.status !== BulkUploadResultStatus.SUCCESS"
|
|
3514
4605
|
>
|
|
@@ -3516,7 +4607,8 @@ class PraxisFilesUpload {
|
|
|
3516
4607
|
</button>
|
|
3517
4608
|
<button
|
|
3518
4609
|
mat-icon-button
|
|
3519
|
-
[matTooltip]="'Remover'"
|
|
4610
|
+
[matTooltip]="t('remove', 'Remover')"
|
|
4611
|
+
[attr.aria-label]="resultActionAriaLabel('remove', 'Remover', r.fileName)"
|
|
3520
4612
|
(click)="removeResult(r)"
|
|
3521
4613
|
>
|
|
3522
4614
|
<mat-icon [praxisIcon]="'close'"></mat-icon>
|
|
@@ -3533,7 +4625,7 @@ class PraxisFilesUpload {
|
|
|
3533
4625
|
"
|
|
3534
4626
|
>
|
|
3535
4627
|
<button mat-button type="button" (click)="showAllResults = true">
|
|
3536
|
-
Ver todos
|
|
4628
|
+
{{ t('showAll', 'Ver todos') }}
|
|
3537
4629
|
</button>
|
|
3538
4630
|
</div>
|
|
3539
4631
|
<div
|
|
@@ -3546,18 +4638,18 @@ class PraxisFilesUpload {
|
|
|
3546
4638
|
"
|
|
3547
4639
|
>
|
|
3548
4640
|
<button mat-button type="button" (click)="showAllResults = false">
|
|
3549
|
-
Ver menos
|
|
4641
|
+
{{ t('showLess', 'Ver menos') }}
|
|
3550
4642
|
</button>
|
|
3551
4643
|
</div>
|
|
3552
4644
|
</section>
|
|
3553
4645
|
|
|
3554
4646
|
<ng-content></ng-content>
|
|
3555
4647
|
</div>
|
|
3556
|
-
`, isInline: true, styles: ["@charset \"UTF-8\";.praxis-files-upload{position:relative}.praxis-files-upload .settings-btn{position:absolute;top:.5rem;right:.5rem}.praxis-files-upload .rate-limit-banner,.praxis-files-upload .quota-banner,.praxis-files-upload .error{margin:.5rem 0;padding:.5rem 1rem;border-radius:var(--md-sys-shape-corner-small, 4px)}.praxis-files-upload .rate-limit-banner{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .quota-banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.praxis-files-upload .dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface);text-align:center;cursor:pointer;transition:background-color .2s;width:100%;min-height:160px;margin:.5rem 0;gap:.5rem;position:relative}.praxis-files-upload .dropzone:focus,.praxis-files-upload .dropzone:hover{outline:none;background:var(--md-sys-color-surface-container-high);border-color:var(--md-sys-color-primary)}.praxis-files-upload .dropzone.dragover{background:var(--md-sys-color-surface-container-high);border-color:var(--md-sys-color-primary)}.praxis-files-upload .dropzone.disabled{opacity:.6;cursor:not-allowed;pointer-events:none}.praxis-files-upload .dropzone p{margin:0 0 .5rem}.praxis-files-upload .dropzone .dz-icon{font-size:40px;color:var(--md-sys-color-primary);margin-bottom:.25rem}.praxis-files-upload .dropzone .dz-title{font-weight:600}.praxis-files-upload .dropzone .dz-hint{margin:0;opacity:.8;font-size:.9rem}.praxis-files-upload .dropzone .field-actions{position:absolute;top:.25rem;right:.25rem;display:flex;gap:.25rem;pointer-events:auto}.praxis-files-upload .dropzone .field-actions button[mat-icon-button]{width:32px;height:32px}.praxis-files-upload .dropzone .field-actions mat-icon{font-size:20px}.praxis-files-upload .progress{margin-top:1rem}.praxis-files-upload .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}.praxis-files-upload .bulk-results{margin-top:1rem}.praxis-files-upload .bulk-results .bulk-summary{display:flex;gap:1rem;align-items:center;margin-bottom:.5rem}.praxis-files-upload .bulk-results .bulk-summary .stat{display:flex;align-items:center;gap:.35rem;padding:.25rem .5rem;border-radius:6px;background:var(--md-sys-color-surface-container-low)}.praxis-files-upload .bulk-results .bulk-summary .stat.success{color:var(--md-sys-color-primary)}.praxis-files-upload .bulk-results .bulk-summary .stat.failed{color:var(--md-sys-color-error)}.praxis-files-upload .bulk-results .bulk-summary .stat .label{opacity:.9}.praxis-files-upload .bulk-results .bulk-summary .stat .value{font-weight:600}.praxis-files-upload .bulk-results .results-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:.5rem}.praxis-files-upload .bulk-results .result-item{display:flex;gap:.75rem;padding:.5rem .75rem;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-lowest)}.praxis-files-upload .bulk-results .result-item.ok{border-color:var(--md-sys-color-primary)}.praxis-files-upload .bulk-results .result-item.err{border-color:var(--md-sys-color-error)}.praxis-files-upload .bulk-results .result-item .status-icon{align-self:flex-start;color:currentColor}.praxis-files-upload .bulk-results .result-item .main{flex:1;min-width:0}.praxis-files-upload .bulk-results .result-item .title{display:flex;align-items:center;gap:.5rem}.praxis-files-upload .bulk-results .result-item .name{font-weight:600}.praxis-files-upload .bulk-results .result-item .badge{font-size:.75rem;padding:.1rem .4rem;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.praxis-files-upload .bulk-results .result-item .badge.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .bulk-results .result-item .meta,.praxis-files-upload .bulk-results .result-item .error{display:flex;flex-wrap:wrap;gap:.5rem 1rem;margin-top:.25rem}.praxis-files-upload .bulk-results .result-item .error .code{font-weight:600}.praxis-files-upload .bulk-results .result-item .extra{margin-top:.25rem}.praxis-files-upload .bulk-results .result-item .item-actions{display:flex;align-items:center;gap:.25rem}.praxis-files-upload .bulk-results .list-footer{margin-top:.5rem;display:flex;justify-content:center}.praxis-files-upload .proximity-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);text-align:center;z-index:2;pointer-events:none}.praxis-files-upload .proximity-overlay .dz-icon{font-size:40px;color:var(--md-sys-color-primary)}.praxis-files-upload .proximity-overlay .dz-title{font-weight:600}.praxis-files-upload .proximity-overlay .dz-hint{opacity:.85}.praxis-files-upload .selected-overlay{background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0, 0, 0, .15));max-height:260px;overflow:auto;padding:.5rem}.praxis-files-upload .selected-overlay .sel-header{position:sticky;top:0;z-index:1;background:var(--md-sys-color-surface);display:flex;align-items:center;gap:.5rem;padding-bottom:.25rem;margin-bottom:.25rem}.praxis-files-upload .selected-overlay .sel-header .title{font-weight:600}.praxis-files-upload .selected-overlay .sel-header .spacer{flex:1}.praxis-files-upload .selected-overlay .sel-list{list-style:none;margin:0;padding:0}.praxis-files-upload .selected-overlay .sel-list .sel-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;border-bottom:1px solid var(--md-sys-color-outline-variant, #ccc);transition:background-color .12s ease}.praxis-files-upload .selected-overlay .sel-list .sel-item .file-icon{opacity:.9;color:var(--md-sys-color-primary, #1976d2)}.praxis-files-upload .selected-overlay .sel-list .sel-item .info{flex:1;min-width:0}.praxis-files-upload .selected-overlay .sel-list .sel-item .info .name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-files-upload .selected-overlay .sel-list .sel-item .info .meta{font-size:.8rem;opacity:.7}.praxis-files-upload .selected-overlay .sel-list .sel-item .remove-btn{color:var(--md-sys-color-error, #b00020)}.praxis-files-upload .selected-overlay .sel-list .sel-item .more-btn{margin-left:.25rem}.praxis-files-upload .selected-overlay .sel-list .sel-item:hover{background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .praxis-files-upload-overlay-panel{pointer-events:none}:host ::ng-deep .praxis-files-selected-overlay-panel{pointer-events:auto;z-index:3}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i7.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i7.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i9.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i9.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i10.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i11$1.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i3$1.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i3$1.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i3$1.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i7.JsonPipe, name: "json" }, { kind: "pipe", type: i7.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4648
|
+
`, isInline: true, styles: ["@charset \"UTF-8\";.praxis-files-upload{position:relative}.praxis-files-upload .settings-btn{position:absolute;top:.5rem;right:.5rem}.praxis-files-upload .rate-limit-banner,.praxis-files-upload .quota-banner,.praxis-files-upload .error{margin:.5rem 0;padding:.5rem 1rem;border-radius:var(--md-sys-shape-corner-small, 4px)}.praxis-files-upload .rate-limit-banner{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .quota-banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.praxis-files-upload .dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface);text-align:center;transition:background-color .2s;width:100%;min-height:160px;margin:.5rem 0;gap:.5rem;position:relative}.praxis-files-upload .dropzone:hover{outline:none;background:var(--md-sys-color-surface-container-high);border-color:var(--md-sys-color-primary)}.praxis-files-upload .dropzone.dragover{background:var(--md-sys-color-surface-container-high);border-color:var(--md-sys-color-primary)}.praxis-files-upload .dropzone.disabled{opacity:.6;cursor:not-allowed;pointer-events:none}.praxis-files-upload .dropzone p{margin:0 0 .5rem}.praxis-files-upload .dropzone .dz-icon{font-size:40px;color:var(--md-sys-color-primary);margin-bottom:.25rem}.praxis-files-upload .dropzone .dz-title{font-weight:600}.praxis-files-upload .dropzone .dz-hint{margin:0;opacity:.8;font-size:.9rem}.praxis-files-upload .dropzone .field-actions{position:absolute;top:.25rem;right:.25rem;display:flex;gap:.25rem;pointer-events:auto}.praxis-files-upload .dropzone .field-actions button[mat-icon-button]{width:32px;height:32px}.praxis-files-upload .dropzone .field-actions mat-icon{font-size:20px}.praxis-files-upload .progress{margin-top:1rem}.praxis-files-upload .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}.praxis-files-upload .bulk-results{margin-top:1rem}.praxis-files-upload .bulk-results .bulk-summary{display:flex;gap:1rem;align-items:center;margin-bottom:.5rem}.praxis-files-upload .bulk-results .bulk-summary .stat{display:flex;align-items:center;gap:.35rem;padding:.25rem .5rem;border-radius:6px;background:var(--md-sys-color-surface-container-low)}.praxis-files-upload .bulk-results .bulk-summary .stat.success{color:var(--md-sys-color-primary)}.praxis-files-upload .bulk-results .bulk-summary .stat.failed{color:var(--md-sys-color-error)}.praxis-files-upload .bulk-results .bulk-summary .stat .label{opacity:.9}.praxis-files-upload .bulk-results .bulk-summary .stat .value{font-weight:600}.praxis-files-upload .bulk-results .results-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:.5rem}.praxis-files-upload .bulk-results .result-item{display:flex;gap:.75rem;padding:.5rem .75rem;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-lowest)}.praxis-files-upload .bulk-results .result-item.ok{border-color:var(--md-sys-color-primary)}.praxis-files-upload .bulk-results .result-item.err{border-color:var(--md-sys-color-error)}.praxis-files-upload .bulk-results .result-item .status-icon{align-self:flex-start;color:currentColor}.praxis-files-upload .bulk-results .result-item .main{flex:1;min-width:0}.praxis-files-upload .bulk-results .result-item .title{display:flex;align-items:center;gap:.5rem}.praxis-files-upload .bulk-results .result-item .name{font-weight:600}.praxis-files-upload .bulk-results .result-item .badge{font-size:.75rem;padding:.1rem .4rem;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.praxis-files-upload .bulk-results .result-item .badge.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .bulk-results .result-item .meta,.praxis-files-upload .bulk-results .result-item .error{display:flex;flex-wrap:wrap;gap:.5rem 1rem;margin-top:.25rem}.praxis-files-upload .bulk-results .result-item .error .code{font-weight:600}.praxis-files-upload .bulk-results .result-item .extra{margin-top:.25rem}.praxis-files-upload .bulk-results .result-item .item-actions{display:flex;align-items:center;gap:.25rem}.praxis-files-upload .bulk-results .list-footer{margin-top:.5rem;display:flex;justify-content:center}.praxis-files-upload .proximity-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);text-align:center;z-index:2;pointer-events:none}.praxis-files-upload .proximity-overlay .dz-icon{font-size:40px;color:var(--md-sys-color-primary)}.praxis-files-upload .proximity-overlay .dz-title{font-weight:600}.praxis-files-upload .proximity-overlay .dz-hint{opacity:.85}.praxis-files-upload .selected-overlay{background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0, 0, 0, .15));max-height:260px;overflow:auto;padding:.5rem}.praxis-files-upload .selected-overlay .sel-header{position:sticky;top:0;z-index:1;background:var(--md-sys-color-surface);display:flex;align-items:center;gap:.5rem;padding-bottom:.25rem;margin-bottom:.25rem}.praxis-files-upload .selected-overlay .sel-header .title{font-weight:600}.praxis-files-upload .selected-overlay .sel-header .spacer{flex:1}.praxis-files-upload .selected-overlay .sel-list{list-style:none;margin:0;padding:0}.praxis-files-upload .selected-overlay .sel-list .sel-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;border-bottom:1px solid var(--md-sys-color-outline-variant, #ccc);transition:background-color .12s ease}.praxis-files-upload .selected-overlay .sel-list .sel-item .file-icon{opacity:.9;color:var(--md-sys-color-primary, #1976d2)}.praxis-files-upload .selected-overlay .sel-list .sel-item .info{flex:1;min-width:0}.praxis-files-upload .selected-overlay .sel-list .sel-item .info .name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-files-upload .selected-overlay .sel-list .sel-item .info .meta{font-size:.8rem;opacity:.7}.praxis-files-upload .selected-overlay .sel-list .sel-item .remove-btn{color:var(--md-sys-color-error, #b00020)}.praxis-files-upload .selected-overlay .sel-list .sel-item .more-btn{margin-left:.25rem}.praxis-files-upload .selected-overlay .sel-list .sel-item:hover{background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .praxis-files-upload-overlay-panel{pointer-events:none}:host ::ng-deep .praxis-files-selected-overlay-panel{pointer-events:auto;z-index:3}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i9.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i9.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$1.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$1.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$1.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "ngmodule", type: MatButtonModule }, { kind: "component", type: i11.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i11.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i10.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: PraxisIconDirective, selector: "mat-icon[praxisIcon]", inputs: ["praxisIcon"] }, { kind: "ngmodule", type: MatProgressBarModule }, { kind: "component", type: i13.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i14.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i14.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i14.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i9.JsonPipe, name: "json" }, { kind: "pipe", type: i9.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3557
4649
|
}
|
|
3558
4650
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PraxisFilesUpload, decorators: [{
|
|
3559
4651
|
type: Component,
|
|
3560
|
-
args: [{ selector: 'praxis-files-upload', standalone: true, imports: [
|
|
4652
|
+
args: [{ selector: 'praxis-files-upload', standalone: true, providers: [providePraxisFilesUploadI18n()], imports: [
|
|
3561
4653
|
CommonModule,
|
|
3562
4654
|
ReactiveFormsModule,
|
|
3563
4655
|
MatButtonModule,
|
|
@@ -3567,27 +4659,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3567
4659
|
MatMenuModule,
|
|
3568
4660
|
MatTooltipModule,
|
|
3569
4661
|
], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
3570
|
-
<div
|
|
4662
|
+
<div
|
|
4663
|
+
class="praxis-files-upload"
|
|
4664
|
+
[class.dense]="config?.ui?.dense"
|
|
4665
|
+
[attr.data-files-upload-id]="a11yBaseId"
|
|
4666
|
+
[attr.data-ready-state]="readinessState"
|
|
4667
|
+
[attr.data-ready-source]="readinessSource"
|
|
4668
|
+
[attr.aria-busy]="readinessState === 'loading' ? 'true' : 'false'"
|
|
4669
|
+
#host
|
|
4670
|
+
>
|
|
3571
4671
|
<!-- Proximity overlay inline (fallback quando expandMode === 'inline') -->
|
|
3572
|
-
|
|
3573
|
-
|
|
4672
|
+
<div
|
|
4673
|
+
class="proximity-overlay"
|
|
3574
4674
|
*ngIf="
|
|
3575
4675
|
showProximityOverlay &&
|
|
3576
4676
|
(config?.ui?.dropzone?.expandMode ?? 'overlay') === 'inline'
|
|
3577
4677
|
"
|
|
3578
4678
|
[style.height.px]="config?.ui?.dropzone?.expandHeight ?? 200"
|
|
3579
4679
|
aria-hidden="true"
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
4680
|
+
>
|
|
4681
|
+
<mat-icon class="dz-icon" aria-hidden="true" [praxisIcon]="'cloud_upload'"></mat-icon>
|
|
4682
|
+
<p class="dz-title">{{ texts.dropzoneLabel }}</p>
|
|
4683
|
+
<p class="dz-hint">{{ t('dropzoneProximityHint', 'Solte aqui para enviar') }}</p>
|
|
4684
|
+
</div>
|
|
3585
4685
|
<!-- Template do overlay de proximidade (renderizado via CDK Overlay) -->
|
|
3586
4686
|
<ng-template #proximityOverlayTmpl>
|
|
3587
4687
|
<div class="proximity-overlay" aria-hidden="true">
|
|
3588
4688
|
<mat-icon class="dz-icon" aria-hidden="true" [praxisIcon]="'cloud_upload'"></mat-icon>
|
|
3589
4689
|
<p class="dz-title">{{ texts.dropzoneLabel }}</p>
|
|
3590
|
-
<p class="dz-hint">Solte aqui para enviar</p>
|
|
4690
|
+
<p class="dz-hint">{{ t('dropzoneProximityHint', 'Solte aqui para enviar') }}</p>
|
|
3591
4691
|
</div>
|
|
3592
4692
|
</ng-template>
|
|
3593
4693
|
|
|
@@ -3596,22 +4696,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3596
4696
|
<ng-template #selectedOverlayTmpl>
|
|
3597
4697
|
<div
|
|
3598
4698
|
class="selected-overlay"
|
|
3599
|
-
role="
|
|
3600
|
-
aria-
|
|
4699
|
+
role="region"
|
|
4700
|
+
[attr.aria-labelledby]="selectedOverlayTitleId"
|
|
3601
4701
|
>
|
|
3602
4702
|
<header class="sel-header">
|
|
3603
|
-
<span class="title">Selecionados ({{ pendingCount }})</span>
|
|
4703
|
+
<span class="title" [id]="selectedOverlayTitleId">{{ t('selectedOverlayTitle', 'Selecionados') }} ({{ pendingCount }})</span>
|
|
3604
4704
|
<span class="spacer"></span>
|
|
3605
4705
|
<button
|
|
3606
4706
|
mat-stroked-button
|
|
3607
4707
|
color="primary"
|
|
3608
|
-
(click)="
|
|
4708
|
+
(click)="submitPendingFiles()"
|
|
3609
4709
|
[disabled]="!baseUrl || isUploading"
|
|
3610
4710
|
>
|
|
3611
|
-
Enviar
|
|
4711
|
+
{{ t('fieldActionUploadShort', 'Enviar') }}
|
|
3612
4712
|
</button>
|
|
3613
|
-
<button mat-button type="button" (click)="
|
|
3614
|
-
Cancelar
|
|
4713
|
+
<button mat-button type="button" (click)="resetPendingFiles()">
|
|
4714
|
+
{{ t('fieldActionCancelShort', 'Cancelar') }}
|
|
3615
4715
|
</button>
|
|
3616
4716
|
</header>
|
|
3617
4717
|
<ul class="sel-list">
|
|
@@ -3632,7 +4732,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3632
4732
|
class="remove-btn"
|
|
3633
4733
|
[matTooltip]="t('remove', 'Remover')"
|
|
3634
4734
|
(click)="removePendingFile(f)"
|
|
3635
|
-
[attr.aria-label]="
|
|
4735
|
+
[attr.aria-label]="pendingFileRemoveAriaLabel(f)"
|
|
3636
4736
|
>
|
|
3637
4737
|
<mat-icon [praxisIcon]="'close'"></mat-icon>
|
|
3638
4738
|
</button>
|
|
@@ -3643,7 +4743,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3643
4743
|
[matMenuTriggerData]="{ file: f }"
|
|
3644
4744
|
aria-haspopup="menu"
|
|
3645
4745
|
[matTooltip]="t('moreActions', 'Mais ações')"
|
|
3646
|
-
[attr.aria-label]="
|
|
4746
|
+
[attr.aria-label]="pendingFileMoreActionsAriaLabel(f)"
|
|
3647
4747
|
>
|
|
3648
4748
|
<mat-icon [praxisIcon]="'more_horiz'"></mat-icon>
|
|
3649
4749
|
</button>
|
|
@@ -3684,6 +4784,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3684
4784
|
<div
|
|
3685
4785
|
class="rate-limit-banner"
|
|
3686
4786
|
*ngIf="rateLimit && config?.rateLimit?.showBannerOn429 !== false"
|
|
4787
|
+
[id]="messageRegionId"
|
|
4788
|
+
role="alert"
|
|
4789
|
+
aria-live="assertive"
|
|
3687
4790
|
>
|
|
3688
4791
|
{{ texts.rateLimitBanner }}
|
|
3689
4792
|
{{ rateLimit.resetEpochSeconds * 1000 | date: 'shortTime' }}.
|
|
@@ -3691,32 +4794,34 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3691
4794
|
<div
|
|
3692
4795
|
class="quota-banner"
|
|
3693
4796
|
*ngIf="quotaExceeded && config?.quotas?.showQuotaWarnings !== false"
|
|
4797
|
+
[id]="messageRegionId"
|
|
4798
|
+
role="status"
|
|
4799
|
+
aria-live="polite"
|
|
3694
4800
|
>
|
|
3695
4801
|
{{ errorMessage }}
|
|
3696
4802
|
</div>
|
|
3697
|
-
<div
|
|
4803
|
+
<div
|
|
4804
|
+
class="error"
|
|
4805
|
+
*ngIf="errorMessage && !quotaExceeded"
|
|
4806
|
+
[id]="messageRegionId"
|
|
4807
|
+
role="alert"
|
|
4808
|
+
aria-live="assertive"
|
|
4809
|
+
>
|
|
3698
4810
|
{{ errorMessage }}
|
|
3699
4811
|
</div>
|
|
3700
|
-
<div
|
|
3701
|
-
|
|
4812
|
+
<div
|
|
4813
|
+
class="error"
|
|
4814
|
+
*ngIf="!baseUrl"
|
|
4815
|
+
[id]="messageRegionId"
|
|
4816
|
+
role="alert"
|
|
4817
|
+
aria-live="assertive"
|
|
4818
|
+
>
|
|
4819
|
+
{{ t('baseUrlMissing', 'Base URL not configured. Provide [baseUrl] to enable uploads.') }}
|
|
3702
4820
|
</div>
|
|
3703
4821
|
<div
|
|
3704
4822
|
class="dropzone"
|
|
3705
4823
|
*ngIf="config?.ui?.showDropzone !== false"
|
|
3706
|
-
|
|
3707
|
-
!baseUrl ||
|
|
3708
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3709
|
-
isUploading
|
|
3710
|
-
? null
|
|
3711
|
-
: 'button'
|
|
3712
|
-
"
|
|
3713
|
-
[attr.tabindex]="
|
|
3714
|
-
!baseUrl ||
|
|
3715
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3716
|
-
isUploading
|
|
3717
|
-
? -1
|
|
3718
|
-
: 0
|
|
3719
|
-
"
|
|
4824
|
+
role="group"
|
|
3720
4825
|
[class.dragover]="isDragging"
|
|
3721
4826
|
[class.disabled]="
|
|
3722
4827
|
!baseUrl ||
|
|
@@ -3730,39 +4835,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3730
4835
|
? 'true'
|
|
3731
4836
|
: 'false'
|
|
3732
4837
|
"
|
|
3733
|
-
[attr.aria-label]="texts.dropzoneLabel
|
|
3734
|
-
|
|
3735
|
-
!baseUrl ||
|
|
3736
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3737
|
-
isUploading
|
|
3738
|
-
? null
|
|
3739
|
-
: openFile(fileInput)
|
|
3740
|
-
"
|
|
4838
|
+
[attr.aria-label]="texts.dropzoneLabel"
|
|
4839
|
+
[attr.aria-describedby]="messageRegionId"
|
|
3741
4840
|
(drop)="onDrop($event)"
|
|
3742
4841
|
(dragover)="onDragOver($event)"
|
|
3743
4842
|
(dragleave)="onDragLeave($event)"
|
|
3744
|
-
(keydown.enter)="
|
|
3745
|
-
$event.preventDefault();
|
|
3746
|
-
!baseUrl ||
|
|
3747
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3748
|
-
isUploading
|
|
3749
|
-
? null
|
|
3750
|
-
: openFile(fileInput)
|
|
3751
|
-
"
|
|
3752
|
-
(keydown.space)="
|
|
3753
|
-
$event.preventDefault();
|
|
3754
|
-
!baseUrl ||
|
|
3755
|
-
(quotaExceeded && config?.quotas?.blockOnExceed) ||
|
|
3756
|
-
isUploading
|
|
3757
|
-
? null
|
|
3758
|
-
: openFile(fileInput)
|
|
3759
|
-
"
|
|
3760
4843
|
>
|
|
3761
4844
|
<!-- Ações internas (ícones) -->
|
|
3762
4845
|
<div class="field-actions" aria-hidden="false">
|
|
3763
4846
|
<button
|
|
3764
4847
|
mat-icon-button
|
|
3765
|
-
[matTooltip]="'
|
|
4848
|
+
[matTooltip]="t('selectFiles', 'Select file(s)')"
|
|
4849
|
+
[attr.aria-label]="selectFilesAriaLabel"
|
|
3766
4850
|
(click)="$event.stopPropagation(); openFile(fileInput)"
|
|
3767
4851
|
[disabled]="!baseUrl || isUploading"
|
|
3768
4852
|
>
|
|
@@ -3771,6 +4855,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3771
4855
|
<button
|
|
3772
4856
|
mat-icon-button
|
|
3773
4857
|
[matTooltip]="policiesSummary"
|
|
4858
|
+
[attr.aria-label]="policySummaryAriaLabel"
|
|
3774
4859
|
(click)="$event.stopPropagation()"
|
|
3775
4860
|
[disabled]="false"
|
|
3776
4861
|
>
|
|
@@ -3781,9 +4866,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3781
4866
|
<mat-icon class="dz-icon" aria-hidden="true" [praxisIcon]="'cloud_upload'"></mat-icon>
|
|
3782
4867
|
<p class="dz-title">{{ texts.dropzoneLabel }}</p>
|
|
3783
4868
|
<p class="dz-hint">
|
|
3784
|
-
Arraste e solte arquivos aqui ou clique para selecionar
|
|
4869
|
+
{{ t('dropzoneHint', 'Arraste e solte arquivos aqui ou clique para selecionar') }}
|
|
3785
4870
|
</p>
|
|
3786
|
-
<button
|
|
4871
|
+
<button
|
|
4872
|
+
type="button"
|
|
4873
|
+
mat-stroked-button
|
|
4874
|
+
[disabled]="!baseUrl || isUploading"
|
|
4875
|
+
[attr.aria-describedby]="messageRegionId"
|
|
4876
|
+
(click)="openFile(fileInput)"
|
|
4877
|
+
>
|
|
3787
4878
|
{{ texts.dropzoneButton }}
|
|
3788
4879
|
</button>
|
|
3789
4880
|
</div>
|
|
@@ -3803,8 +4894,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3803
4894
|
class="conflict-policy"
|
|
3804
4895
|
*ngIf="config?.ui?.showConflictPolicySelector"
|
|
3805
4896
|
>
|
|
3806
|
-
<label for="
|
|
3807
|
-
<select id="
|
|
4897
|
+
<label [for]="conflictPolicyInputId">{{ texts.conflictPolicyLabel }}</label>
|
|
4898
|
+
<select [id]="conflictPolicyInputId" [formControl]="conflictPolicy">
|
|
3808
4899
|
<option *ngFor="let p of conflictPolicies" [value]="p">
|
|
3809
4900
|
{{ p }}
|
|
3810
4901
|
</option>
|
|
@@ -3812,18 +4903,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3812
4903
|
</div>
|
|
3813
4904
|
|
|
3814
4905
|
<div class="metadata" *ngIf="config?.ui?.showMetadataForm">
|
|
3815
|
-
<label for="
|
|
3816
|
-
<textarea id="
|
|
4906
|
+
<label [for]="metadataInputId">{{ texts.metadataLabel }}</label>
|
|
4907
|
+
<textarea [id]="metadataInputId" [formControl]="metadata"></textarea>
|
|
3817
4908
|
<div class="error" *ngIf="metadataError">{{ metadataError }}</div>
|
|
3818
4909
|
</div>
|
|
3819
4910
|
|
|
3820
|
-
<div
|
|
4911
|
+
<div
|
|
4912
|
+
class="progress"
|
|
4913
|
+
*ngIf="showProgress"
|
|
4914
|
+
[id]="progressRegionId"
|
|
4915
|
+
role="status"
|
|
4916
|
+
aria-live="polite"
|
|
4917
|
+
>
|
|
3821
4918
|
<mat-progress-bar
|
|
3822
4919
|
mode="determinate"
|
|
3823
4920
|
[value]="uploadProgressValue"
|
|
3824
4921
|
[attr.aria-label]="texts.progressAriaLabel"
|
|
4922
|
+
[attr.aria-valuemin]="0"
|
|
4923
|
+
[attr.aria-valuemax]="100"
|
|
4924
|
+
[attr.aria-valuenow]="uploadProgressValue"
|
|
3825
4925
|
></mat-progress-bar>
|
|
3826
|
-
<span class="sr-only">{{ uploadProgressValue }}%</span>
|
|
4926
|
+
<span class="sr-only">{{ texts.progressAriaLabel }} {{ uploadProgressValue }}%</span>
|
|
3827
4927
|
</div>
|
|
3828
4928
|
|
|
3829
4929
|
<ul class="file-feedback" *ngIf="hasUploadedFiles">
|
|
@@ -3838,17 +4938,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3838
4938
|
<section class="bulk-results" *ngIf="hasLastResults">
|
|
3839
4939
|
<header class="bulk-summary" *ngIf="lastStats">
|
|
3840
4940
|
<div class="stat">
|
|
3841
|
-
<span class="label">Total</span>
|
|
4941
|
+
<span class="label">{{ t('bulkSummaryTotal', 'Total') }}</span>
|
|
3842
4942
|
<span class="value">{{ lastStats.total }}</span>
|
|
3843
4943
|
</div>
|
|
3844
4944
|
<div class="stat success">
|
|
3845
4945
|
<mat-icon [praxisIcon]="'check_circle'"></mat-icon>
|
|
3846
|
-
<span class="label">Sucesso</span>
|
|
4946
|
+
<span class="label">{{ t('bulkSummarySuccess', 'Sucesso') }}</span>
|
|
3847
4947
|
<span class="value">{{ lastStats.succeeded }}</span>
|
|
3848
4948
|
</div>
|
|
3849
4949
|
<div class="stat failed">
|
|
3850
4950
|
<mat-icon [praxisIcon]="'error'"></mat-icon>
|
|
3851
|
-
<span class="label">Falhas</span>
|
|
4951
|
+
<span class="label">{{ t('bulkSummaryFailed', 'Falhas') }}</span>
|
|
3852
4952
|
<span class="value">{{ lastStats.failed }}</span>
|
|
3853
4953
|
</div>
|
|
3854
4954
|
</header>
|
|
@@ -3879,19 +4979,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3879
4979
|
>
|
|
3880
4980
|
</div>
|
|
3881
4981
|
<div class="meta" *ngIf="r.file">
|
|
3882
|
-
<span *ngIf="r.file.id"
|
|
4982
|
+
<span *ngIf="r.file.id"
|
|
4983
|
+
>{{ t('resultMetaIdLabel', 'ID') }}: {{ r.file.id }}</span
|
|
4984
|
+
>
|
|
3883
4985
|
<span *ngIf="r.file.contentType"
|
|
3884
|
-
>
|
|
4986
|
+
>{{ t('resultMetaTypeLabel', 'Tipo') }}:
|
|
4987
|
+
{{ r.file.contentType }}</span
|
|
3885
4988
|
>
|
|
3886
4989
|
<span *ngIf="r.file.fileSize"
|
|
3887
|
-
>
|
|
4990
|
+
>{{ t('resultMetaSizeLabel', 'Tamanho') }}:
|
|
4991
|
+
{{ formatBytes(r.file.fileSize) }}</span
|
|
3888
4992
|
>
|
|
3889
4993
|
<span *ngIf="r.file.uploadedAt"
|
|
3890
|
-
>
|
|
4994
|
+
>{{ t('resultMetaUploadedAtLabel', 'Enviado em') }}:
|
|
4995
|
+
{{ r.file.uploadedAt | date: 'short' }}</span
|
|
3891
4996
|
>
|
|
3892
4997
|
</div>
|
|
3893
4998
|
<div class="error" *ngIf="r.error as e">
|
|
3894
|
-
<span class="code">{{ e
|
|
4999
|
+
<span class="code">{{ primaryErrorCode(e) }}</span>
|
|
3895
5000
|
<ng-container *ngIf="mapErrorFull(e) as me">
|
|
3896
5001
|
<span class="title" *ngIf="me.title">{{ me.title }}:</span>
|
|
3897
5002
|
<span class="msg">{{ me.message }}</span>
|
|
@@ -3899,13 +5004,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3899
5004
|
— {{ me.userAction }}</span
|
|
3900
5005
|
>
|
|
3901
5006
|
</ng-container>
|
|
3902
|
-
<span class="trace" *ngIf="e
|
|
3903
|
-
>(trace {{
|
|
5007
|
+
<span class="trace" *ngIf="traceIdOf(e) as traceId"
|
|
5008
|
+
>(trace {{ traceId }})</span
|
|
3904
5009
|
>
|
|
3905
5010
|
</div>
|
|
3906
5011
|
<div class="extra" *ngIf="r.file?.metadata as m">
|
|
3907
5012
|
<details (toggle)="onDetailsToggle($event, r)">
|
|
3908
|
-
<summary>Metadados</summary>
|
|
5013
|
+
<summary>{{ t('detailsMetadata', 'Metadados') }}</summary>
|
|
3909
5014
|
<pre>{{ m | json }}</pre>
|
|
3910
5015
|
</details>
|
|
3911
5016
|
</div>
|
|
@@ -3913,14 +5018,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3913
5018
|
<div class="item-actions">
|
|
3914
5019
|
<button
|
|
3915
5020
|
mat-icon-button
|
|
3916
|
-
[matTooltip]="'Detalhes'"
|
|
5021
|
+
[matTooltip]="t('details', 'Detalhes')"
|
|
5022
|
+
[attr.aria-label]="resultActionAriaLabel('details', 'Detalhes', r.fileName)"
|
|
3917
5023
|
(click)="toggleDetailsFor(r)"
|
|
3918
5024
|
>
|
|
3919
5025
|
<mat-icon [praxisIcon]="'info'"></mat-icon>
|
|
3920
5026
|
</button>
|
|
3921
5027
|
<button
|
|
3922
5028
|
mat-icon-button
|
|
3923
|
-
[matTooltip]="'Reenviar'"
|
|
5029
|
+
[matTooltip]="t('retry', 'Reenviar')"
|
|
5030
|
+
[attr.aria-label]="resultActionAriaLabel('retry', 'Reenviar', r.fileName)"
|
|
3924
5031
|
(click)="onRetry(r)"
|
|
3925
5032
|
[disabled]="isUploading"
|
|
3926
5033
|
>
|
|
@@ -3928,7 +5035,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3928
5035
|
</button>
|
|
3929
5036
|
<button
|
|
3930
5037
|
mat-icon-button
|
|
3931
|
-
[matTooltip]="'Baixar'"
|
|
5038
|
+
[matTooltip]="t('download', 'Baixar')"
|
|
5039
|
+
[attr.aria-label]="resultActionAriaLabel('download', 'Baixar', r.fileName)"
|
|
3932
5040
|
(click)="onDownload(r)"
|
|
3933
5041
|
[disabled]="r.status !== BulkUploadResultStatus.SUCCESS"
|
|
3934
5042
|
>
|
|
@@ -3936,7 +5044,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3936
5044
|
</button>
|
|
3937
5045
|
<button
|
|
3938
5046
|
mat-icon-button
|
|
3939
|
-
[matTooltip]="'Copiar link'"
|
|
5047
|
+
[matTooltip]="t('copyLink', 'Copiar link')"
|
|
5048
|
+
[attr.aria-label]="resultActionAriaLabel('copyLink', 'Copiar link', r.fileName)"
|
|
3940
5049
|
(click)="onCopyLink(r)"
|
|
3941
5050
|
[disabled]="r.status !== BulkUploadResultStatus.SUCCESS"
|
|
3942
5051
|
>
|
|
@@ -3944,7 +5053,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3944
5053
|
</button>
|
|
3945
5054
|
<button
|
|
3946
5055
|
mat-icon-button
|
|
3947
|
-
[matTooltip]="'Remover'"
|
|
5056
|
+
[matTooltip]="t('remove', 'Remover')"
|
|
5057
|
+
[attr.aria-label]="resultActionAriaLabel('remove', 'Remover', r.fileName)"
|
|
3948
5058
|
(click)="removeResult(r)"
|
|
3949
5059
|
>
|
|
3950
5060
|
<mat-icon [praxisIcon]="'close'"></mat-icon>
|
|
@@ -3961,7 +5071,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3961
5071
|
"
|
|
3962
5072
|
>
|
|
3963
5073
|
<button mat-button type="button" (click)="showAllResults = true">
|
|
3964
|
-
Ver todos
|
|
5074
|
+
{{ t('showAll', 'Ver todos') }}
|
|
3965
5075
|
</button>
|
|
3966
5076
|
</div>
|
|
3967
5077
|
<div
|
|
@@ -3974,37 +5084,39 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
3974
5084
|
"
|
|
3975
5085
|
>
|
|
3976
5086
|
<button mat-button type="button" (click)="showAllResults = false">
|
|
3977
|
-
Ver menos
|
|
5087
|
+
{{ t('showLess', 'Ver menos') }}
|
|
3978
5088
|
</button>
|
|
3979
5089
|
</div>
|
|
3980
5090
|
</section>
|
|
3981
5091
|
|
|
3982
5092
|
<ng-content></ng-content>
|
|
3983
5093
|
</div>
|
|
3984
|
-
`, styles: ["@charset \"UTF-8\";.praxis-files-upload{position:relative}.praxis-files-upload .settings-btn{position:absolute;top:.5rem;right:.5rem}.praxis-files-upload .rate-limit-banner,.praxis-files-upload .quota-banner,.praxis-files-upload .error{margin:.5rem 0;padding:.5rem 1rem;border-radius:var(--md-sys-shape-corner-small, 4px)}.praxis-files-upload .rate-limit-banner{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .quota-banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.praxis-files-upload .dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface);text-align:center;
|
|
3985
|
-
}], ctorParameters: () => [{ type: i1$
|
|
5094
|
+
`, styles: ["@charset \"UTF-8\";.praxis-files-upload{position:relative}.praxis-files-upload .settings-btn{position:absolute;top:.5rem;right:.5rem}.praxis-files-upload .rate-limit-banner,.praxis-files-upload .quota-banner,.praxis-files-upload .error{margin:.5rem 0;padding:.5rem 1rem;border-radius:var(--md-sys-shape-corner-small, 4px)}.praxis-files-upload .rate-limit-banner{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .quota-banner{background:var(--md-sys-color-tertiary-container);color:var(--md-sys-color-on-tertiary-container)}.praxis-files-upload .dropzone{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:2rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-low);color:var(--md-sys-color-on-surface);text-align:center;transition:background-color .2s;width:100%;min-height:160px;margin:.5rem 0;gap:.5rem;position:relative}.praxis-files-upload .dropzone:hover{outline:none;background:var(--md-sys-color-surface-container-high);border-color:var(--md-sys-color-primary)}.praxis-files-upload .dropzone.dragover{background:var(--md-sys-color-surface-container-high);border-color:var(--md-sys-color-primary)}.praxis-files-upload .dropzone.disabled{opacity:.6;cursor:not-allowed;pointer-events:none}.praxis-files-upload .dropzone p{margin:0 0 .5rem}.praxis-files-upload .dropzone .dz-icon{font-size:40px;color:var(--md-sys-color-primary);margin-bottom:.25rem}.praxis-files-upload .dropzone .dz-title{font-weight:600}.praxis-files-upload .dropzone .dz-hint{margin:0;opacity:.8;font-size:.9rem}.praxis-files-upload .dropzone .field-actions{position:absolute;top:.25rem;right:.25rem;display:flex;gap:.25rem;pointer-events:auto}.praxis-files-upload .dropzone .field-actions button[mat-icon-button]{width:32px;height:32px}.praxis-files-upload .dropzone .field-actions mat-icon{font-size:20px}.praxis-files-upload .progress{margin-top:1rem}.praxis-files-upload .sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0 0 0 0);border:0}.praxis-files-upload .bulk-results{margin-top:1rem}.praxis-files-upload .bulk-results .bulk-summary{display:flex;gap:1rem;align-items:center;margin-bottom:.5rem}.praxis-files-upload .bulk-results .bulk-summary .stat{display:flex;align-items:center;gap:.35rem;padding:.25rem .5rem;border-radius:6px;background:var(--md-sys-color-surface-container-low)}.praxis-files-upload .bulk-results .bulk-summary .stat.success{color:var(--md-sys-color-primary)}.praxis-files-upload .bulk-results .bulk-summary .stat.failed{color:var(--md-sys-color-error)}.praxis-files-upload .bulk-results .bulk-summary .stat .label{opacity:.9}.praxis-files-upload .bulk-results .bulk-summary .stat .value{font-weight:600}.praxis-files-upload .bulk-results .results-list{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:.5rem}.praxis-files-upload .bulk-results .result-item{display:flex;gap:.75rem;padding:.5rem .75rem;border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;background:var(--md-sys-color-surface-container-lowest)}.praxis-files-upload .bulk-results .result-item.ok{border-color:var(--md-sys-color-primary)}.praxis-files-upload .bulk-results .result-item.err{border-color:var(--md-sys-color-error)}.praxis-files-upload .bulk-results .result-item .status-icon{align-self:flex-start;color:currentColor}.praxis-files-upload .bulk-results .result-item .main{flex:1;min-width:0}.praxis-files-upload .bulk-results .result-item .title{display:flex;align-items:center;gap:.5rem}.praxis-files-upload .bulk-results .result-item .name{font-weight:600}.praxis-files-upload .bulk-results .result-item .badge{font-size:.75rem;padding:.1rem .4rem;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.praxis-files-upload .bulk-results .result-item .badge.err{background:var(--md-sys-color-error-container);color:var(--md-sys-color-on-error-container)}.praxis-files-upload .bulk-results .result-item .meta,.praxis-files-upload .bulk-results .result-item .error{display:flex;flex-wrap:wrap;gap:.5rem 1rem;margin-top:.25rem}.praxis-files-upload .bulk-results .result-item .error .code{font-weight:600}.praxis-files-upload .bulk-results .result-item .extra{margin-top:.25rem}.praxis-files-upload .bulk-results .result-item .item-actions{display:flex;align-items:center;gap:.25rem}.praxis-files-upload .bulk-results .list-footer{margin-top:.5rem;display:flex;justify-content:center}.praxis-files-upload .proximity-overlay{position:absolute;inset:0;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:.5rem;border:2px dashed var(--md-sys-color-outline);border-radius:var(--md-sys-shape-corner-medium, 4px);background:var(--md-sys-color-surface-container-high);color:var(--md-sys-color-on-surface);text-align:center;z-index:2;pointer-events:none}.praxis-files-upload .proximity-overlay .dz-icon{font-size:40px;color:var(--md-sys-color-primary)}.praxis-files-upload .proximity-overlay .dz-title{font-weight:600}.praxis-files-upload .proximity-overlay .dz-hint{opacity:.85}.praxis-files-upload .selected-overlay{background:var(--md-sys-color-surface);color:var(--md-sys-color-on-surface);border:1px solid var(--md-sys-color-outline-variant);border-radius:8px;box-shadow:var(--md-sys-elevation-level2, 0 2px 6px rgba(0, 0, 0, .15));max-height:260px;overflow:auto;padding:.5rem}.praxis-files-upload .selected-overlay .sel-header{position:sticky;top:0;z-index:1;background:var(--md-sys-color-surface);display:flex;align-items:center;gap:.5rem;padding-bottom:.25rem;margin-bottom:.25rem}.praxis-files-upload .selected-overlay .sel-header .title{font-weight:600}.praxis-files-upload .selected-overlay .sel-header .spacer{flex:1}.praxis-files-upload .selected-overlay .sel-list{list-style:none;margin:0;padding:0}.praxis-files-upload .selected-overlay .sel-list .sel-item{display:flex;align-items:center;gap:.75rem;padding:.5rem .75rem;border-bottom:1px solid var(--md-sys-color-outline-variant, #ccc);transition:background-color .12s ease}.praxis-files-upload .selected-overlay .sel-list .sel-item .file-icon{opacity:.9;color:var(--md-sys-color-primary, #1976d2)}.praxis-files-upload .selected-overlay .sel-list .sel-item .info{flex:1;min-width:0}.praxis-files-upload .selected-overlay .sel-list .sel-item .info .name{font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.praxis-files-upload .selected-overlay .sel-list .sel-item .info .meta{font-size:.8rem;opacity:.7}.praxis-files-upload .selected-overlay .sel-list .sel-item .remove-btn{color:var(--md-sys-color-error, #b00020)}.praxis-files-upload .selected-overlay .sel-list .sel-item .more-btn{margin-left:.25rem}.praxis-files-upload .selected-overlay .sel-list .sel-item:hover{background:var(--md-sys-color-surface-container-low)}:host ::ng-deep .praxis-files-upload-overlay-panel{pointer-events:none}:host ::ng-deep .praxis-files-selected-overlay-panel{pointer-events:auto;z-index:3}\n"] }]
|
|
5095
|
+
}], ctorParameters: () => [{ type: i1$3.SettingsPanelService }, { type: DragProximityService }, { type: i3.Overlay }, { type: i0.ViewContainerRef }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
|
|
5096
|
+
type: Inject,
|
|
5097
|
+
args: [ASYNC_CONFIG_STORAGE]
|
|
5098
|
+
}] }, { type: i1$2.PraxisI18nService }, { type: undefined, decorators: [{
|
|
5099
|
+
type: Optional
|
|
5100
|
+
}, {
|
|
3986
5101
|
type: Inject,
|
|
3987
5102
|
args: [FILES_UPLOAD_TEXTS]
|
|
3988
|
-
}] }, { type:
|
|
5103
|
+
}] }, { type: undefined, decorators: [{
|
|
5104
|
+
type: Optional
|
|
5105
|
+
}, {
|
|
3989
5106
|
type: Inject,
|
|
3990
|
-
args: [
|
|
5107
|
+
args: [TRANSLATE_LIKE]
|
|
5108
|
+
}] }, { type: ConfigService, decorators: [{
|
|
5109
|
+
type: Optional
|
|
3991
5110
|
}] }, { type: FilesApiClient, decorators: [{
|
|
3992
5111
|
type: Optional
|
|
3993
5112
|
}] }, { type: PresignedUploaderService, decorators: [{
|
|
3994
5113
|
type: Optional
|
|
3995
5114
|
}] }, { type: ErrorMapperService, decorators: [{
|
|
3996
5115
|
type: Optional
|
|
3997
|
-
}] }, { type: undefined, decorators: [{
|
|
3998
|
-
type: Optional
|
|
3999
|
-
}, {
|
|
4000
|
-
type: Inject,
|
|
4001
|
-
args: [TRANSLATE_LIKE]
|
|
4002
5116
|
}] }], propDecorators: { config: [{
|
|
4003
5117
|
type: Input
|
|
4004
5118
|
}], filesUploadId: [{
|
|
4005
5119
|
type: Input
|
|
4006
|
-
}], uploadId: [{
|
|
4007
|
-
type: Input
|
|
4008
5120
|
}], componentInstanceId: [{
|
|
4009
5121
|
type: Input
|
|
4010
5122
|
}], baseUrl: [{
|
|
@@ -4027,8 +5139,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4027
5139
|
type: Output
|
|
4028
5140
|
}], uploadProgress: [{
|
|
4029
5141
|
type: Output
|
|
4030
|
-
}], uploadError: [{
|
|
4031
|
-
type: Output
|
|
4032
5142
|
}], retry: [{
|
|
4033
5143
|
type: Output
|
|
4034
5144
|
}], download: [{
|
|
@@ -4039,10 +5149,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4039
5149
|
type: Output
|
|
4040
5150
|
}], detailsClosed: [{
|
|
4041
5151
|
type: Output
|
|
4042
|
-
}],
|
|
5152
|
+
}], pendingStateChange: [{
|
|
4043
5153
|
type: Output
|
|
4044
5154
|
}], proximityChange: [{
|
|
4045
5155
|
type: Output
|
|
5156
|
+
}], readinessChange: [{
|
|
5157
|
+
type: Output
|
|
4046
5158
|
}], fileInput: [{
|
|
4047
5159
|
type: ViewChild,
|
|
4048
5160
|
args: ['fileInput']
|
|
@@ -4061,22 +5173,49 @@ class PdxFilesUploadFieldComponent extends SimpleBaseInputComponent {
|
|
|
4061
5173
|
config = null;
|
|
4062
5174
|
valueMode = 'metadata';
|
|
4063
5175
|
baseUrl;
|
|
5176
|
+
uploadSuccess = new EventEmitter();
|
|
5177
|
+
bulkComplete = new EventEmitter();
|
|
5178
|
+
error = new EventEmitter();
|
|
4064
5179
|
uploadError = null;
|
|
4065
5180
|
uploader;
|
|
5181
|
+
pendingTrigger;
|
|
5182
|
+
pendingPanel;
|
|
4066
5183
|
// Estado para UI de seleção manual
|
|
4067
5184
|
pendingFiles = [];
|
|
5185
|
+
pendingPanelOpen = false;
|
|
5186
|
+
pendingOverlayPositions = [
|
|
5187
|
+
{
|
|
5188
|
+
originX: 'start',
|
|
5189
|
+
originY: 'bottom',
|
|
5190
|
+
overlayX: 'start',
|
|
5191
|
+
overlayY: 'top',
|
|
5192
|
+
offsetY: 8,
|
|
5193
|
+
},
|
|
5194
|
+
{
|
|
5195
|
+
originX: 'start',
|
|
5196
|
+
originY: 'top',
|
|
5197
|
+
overlayX: 'start',
|
|
5198
|
+
overlayY: 'bottom',
|
|
5199
|
+
offsetY: -8,
|
|
5200
|
+
},
|
|
5201
|
+
];
|
|
4068
5202
|
// Ações para UI de seleção manual
|
|
4069
5203
|
onTriggerUpload() {
|
|
4070
|
-
this.
|
|
5204
|
+
this.closePendingPanel(false);
|
|
5205
|
+
this.uploader?.submitPendingFiles();
|
|
4071
5206
|
}
|
|
4072
5207
|
onClearSelection() {
|
|
4073
|
-
this.
|
|
5208
|
+
this.closePendingPanel(false);
|
|
5209
|
+
this.uploader?.resetPendingFiles();
|
|
4074
5210
|
}
|
|
4075
5211
|
// Recebe o estado do componente filho
|
|
4076
|
-
|
|
4077
|
-
this.pendingFiles = files;
|
|
4078
|
-
if (
|
|
4079
|
-
this.control().markAsTouched();
|
|
5212
|
+
handlePendingState(state) {
|
|
5213
|
+
this.pendingFiles = state.files;
|
|
5214
|
+
if (state.hasPending) {
|
|
5215
|
+
this.control().markAsTouched();
|
|
5216
|
+
}
|
|
5217
|
+
else {
|
|
5218
|
+
this.closePendingPanel(false);
|
|
4080
5219
|
}
|
|
4081
5220
|
}
|
|
4082
5221
|
// Estado visual do campo compacto
|
|
@@ -4084,48 +5223,102 @@ class PdxFilesUploadFieldComponent extends SimpleBaseInputComponent {
|
|
|
4084
5223
|
batchCount = 0;
|
|
4085
5224
|
isProximityActive = false;
|
|
4086
5225
|
errors = inject(ErrorMapperService);
|
|
4087
|
-
|
|
5226
|
+
filesUploadI18n = inject(PraxisI18nService);
|
|
5227
|
+
legacyTexts = inject(FILES_UPLOAD_TEXTS, { optional: true });
|
|
5228
|
+
legacyTranslate = inject(TRANSLATE_LIKE, { optional: true });
|
|
4088
5229
|
get showUploadError() {
|
|
4089
5230
|
const c = this.control();
|
|
4090
|
-
return
|
|
5231
|
+
return (!!this.uploadError &&
|
|
5232
|
+
c.touched &&
|
|
5233
|
+
(c.hasError('upload') || c.hasError('uploadPartial')));
|
|
4091
5234
|
}
|
|
4092
|
-
|
|
4093
|
-
|
|
4094
|
-
|
|
4095
|
-
|
|
5235
|
+
resolveSingleValue(file) {
|
|
5236
|
+
if (this.valueMode === 'id')
|
|
5237
|
+
return file.id;
|
|
5238
|
+
if (this.valueMode === 'metadata[]')
|
|
5239
|
+
return [file];
|
|
5240
|
+
if (this.valueMode === 'id[]')
|
|
5241
|
+
return [file.id];
|
|
5242
|
+
return file;
|
|
5243
|
+
}
|
|
5244
|
+
resolveBulkValue(files) {
|
|
5245
|
+
if (files.length === 0)
|
|
5246
|
+
return null;
|
|
5247
|
+
if (this.valueMode === 'metadata[]')
|
|
5248
|
+
return files;
|
|
5249
|
+
if (this.valueMode === 'id[]')
|
|
5250
|
+
return files.map((file) => file.id);
|
|
5251
|
+
return this.resolveSingleValue(files[0]);
|
|
4096
5252
|
}
|
|
4097
|
-
|
|
4098
|
-
const value = this.
|
|
5253
|
+
applyResolvedValue(file) {
|
|
5254
|
+
const value = this.resolveSingleValue(file);
|
|
4099
5255
|
this.setValue(value);
|
|
4100
|
-
this.
|
|
5256
|
+
this.lastFileName = file.fileName || null;
|
|
5257
|
+
this.control().markAsTouched();
|
|
5258
|
+
this.control().markAsDirty();
|
|
5259
|
+
}
|
|
5260
|
+
clearUploadState() {
|
|
5261
|
+
this.updateUploadValidationState();
|
|
4101
5262
|
this.uploadError = null;
|
|
4102
|
-
|
|
4103
|
-
|
|
5263
|
+
}
|
|
5264
|
+
applyUploadErrorState(message, validationKey) {
|
|
5265
|
+
this.updateUploadValidationState(validationKey);
|
|
5266
|
+
this.control().markAsTouched();
|
|
5267
|
+
this.control().markAsDirty();
|
|
5268
|
+
this.uploadError = message;
|
|
5269
|
+
}
|
|
5270
|
+
t(key, fallback) {
|
|
5271
|
+
return resolvePraxisFilesUploadText(this.filesUploadI18n, key, fallback, this.legacyTexts ?? undefined, this.legacyTranslate ?? undefined);
|
|
5272
|
+
}
|
|
5273
|
+
get additionalFilesChipAriaLabel() {
|
|
5274
|
+
const extraFiles = Math.max(this.batchCount - 1, 0);
|
|
5275
|
+
return `${extraFiles} ${this.t('fieldChipAria', 'arquivos adicionais selecionados')}`;
|
|
5276
|
+
}
|
|
5277
|
+
onUploadSuccess(file) {
|
|
5278
|
+
this.applyResolvedValue(file);
|
|
5279
|
+
this.clearUploadState();
|
|
4104
5280
|
this.batchCount = 1;
|
|
5281
|
+
this.uploadSuccess.emit(file);
|
|
4105
5282
|
}
|
|
4106
|
-
onBulkComplete(
|
|
4107
|
-
|
|
4108
|
-
const
|
|
4109
|
-
|
|
4110
|
-
const
|
|
4111
|
-
|
|
4112
|
-
|
|
5283
|
+
onBulkComplete(data) {
|
|
5284
|
+
const results = data.results;
|
|
5285
|
+
const successfulResults = results.filter((r) => r?.file?.id || r?.file?.fileName);
|
|
5286
|
+
const failedResults = results.filter((r) => !r?.file?.id && !r?.file?.fileName);
|
|
5287
|
+
const successfulFiles = successfulResults
|
|
5288
|
+
.map((result) => result?.file)
|
|
5289
|
+
.filter((file) => !!file);
|
|
5290
|
+
this.batchCount = successfulFiles.length;
|
|
5291
|
+
if (successfulFiles.length > 0) {
|
|
5292
|
+
const value = this.resolveBulkValue(successfulFiles);
|
|
5293
|
+
this.setValue(value);
|
|
5294
|
+
this.lastFileName = successfulFiles[0]?.fileName || null;
|
|
5295
|
+
}
|
|
5296
|
+
if (failedResults.length > 0) {
|
|
5297
|
+
this.applyUploadErrorState(this.t('partialUploadError', 'Upload parcial concluído com falhas em parte do lote.'), 'uploadPartial');
|
|
5298
|
+
}
|
|
5299
|
+
else if (successfulResults.length > 0) {
|
|
5300
|
+
this.clearUploadState();
|
|
4113
5301
|
}
|
|
5302
|
+
this.bulkComplete.emit(data);
|
|
4114
5303
|
}
|
|
4115
5304
|
// When an error occurs, mark the control invalid and show message
|
|
4116
5305
|
onUploadError(event) {
|
|
4117
|
-
|
|
4118
|
-
|
|
4119
|
-
|
|
4120
|
-
|
|
4121
|
-
|
|
5306
|
+
let message;
|
|
5307
|
+
if (event &&
|
|
5308
|
+
typeof event === 'object' &&
|
|
5309
|
+
('code' in event ||
|
|
5310
|
+
'errors' in event ||
|
|
5311
|
+
'status' in event)) {
|
|
5312
|
+
message = this.errors.map(event).message;
|
|
4122
5313
|
}
|
|
4123
5314
|
else if (event && typeof event.message === 'string') {
|
|
4124
|
-
|
|
5315
|
+
message = event.message;
|
|
4125
5316
|
}
|
|
4126
5317
|
else {
|
|
4127
|
-
|
|
5318
|
+
message = this.t('genericUploadError', 'Erro no envio de arquivo.');
|
|
4128
5319
|
}
|
|
5320
|
+
this.applyUploadErrorState(message, 'upload');
|
|
5321
|
+
this.error.emit(event);
|
|
4129
5322
|
}
|
|
4130
5323
|
isShellDisabled() {
|
|
4131
5324
|
try {
|
|
@@ -4135,13 +5328,92 @@ class PdxFilesUploadFieldComponent extends SimpleBaseInputComponent {
|
|
|
4135
5328
|
return false;
|
|
4136
5329
|
}
|
|
4137
5330
|
}
|
|
5331
|
+
isUploadingState() {
|
|
5332
|
+
return !!this.uploader?.isUploading;
|
|
5333
|
+
}
|
|
5334
|
+
get shellAriaLabel() {
|
|
5335
|
+
const base = this.lastFileName
|
|
5336
|
+
? `${this.t('fieldSelectedSummary', 'Arquivo selecionado')}: ${this.lastFileName}`
|
|
5337
|
+
: this.t('placeholder', 'Selecione ou solte arquivos…');
|
|
5338
|
+
if (this.pendingFiles.length > 0) {
|
|
5339
|
+
return `${this.pendingFiles.length} ${this.t('fieldPendingAriaSuffix', 'arquivos prontos para envio')}`;
|
|
5340
|
+
}
|
|
5341
|
+
return `${base}. ${this.statusSummary}`;
|
|
5342
|
+
}
|
|
5343
|
+
get statusSummary() {
|
|
5344
|
+
if (this.showUploadError) {
|
|
5345
|
+
return this.t('fieldErrorSummary', 'Corrija o erro para continuar');
|
|
5346
|
+
}
|
|
5347
|
+
if (this.pendingFiles.length > 0) {
|
|
5348
|
+
return this.t('fieldPendingSummary', 'Arquivos prontos para envio');
|
|
5349
|
+
}
|
|
5350
|
+
if (this.batchCount > 1) {
|
|
5351
|
+
return `${this.batchCount} ${this.t('fieldSelectedPlural', 'arquivos vinculados')}`;
|
|
5352
|
+
}
|
|
5353
|
+
if (this.lastFileName) {
|
|
5354
|
+
return this.t('fieldSelectedSummary', 'Arquivo selecionado');
|
|
5355
|
+
}
|
|
5356
|
+
return this.t('fieldEmptySummary', 'Nenhum arquivo selecionado');
|
|
5357
|
+
}
|
|
4138
5358
|
onProximityChange(isActive) {
|
|
4139
5359
|
this.isProximityActive = isActive;
|
|
4140
5360
|
}
|
|
4141
5361
|
openFromShell() {
|
|
4142
5362
|
if (this.isShellDisabled())
|
|
4143
5363
|
return;
|
|
4144
|
-
this.uploader?.
|
|
5364
|
+
this.uploader?.openFileDialog();
|
|
5365
|
+
}
|
|
5366
|
+
get pendingPanelId() {
|
|
5367
|
+
return `${this.componentId()}-pending-panel`;
|
|
5368
|
+
}
|
|
5369
|
+
togglePendingPanel() {
|
|
5370
|
+
if (!this.pendingFiles.length) {
|
|
5371
|
+
this.closePendingPanel(false);
|
|
5372
|
+
return;
|
|
5373
|
+
}
|
|
5374
|
+
if (this.pendingPanelOpen) {
|
|
5375
|
+
this.closePendingPanel();
|
|
5376
|
+
return;
|
|
5377
|
+
}
|
|
5378
|
+
this.pendingPanelOpen = true;
|
|
5379
|
+
this.focusPendingPanelSoon();
|
|
5380
|
+
}
|
|
5381
|
+
closePendingPanel(restoreFocus = true) {
|
|
5382
|
+
if (!this.pendingPanelOpen) {
|
|
5383
|
+
if (restoreFocus) {
|
|
5384
|
+
this.pendingTrigger?.nativeElement.focus();
|
|
5385
|
+
}
|
|
5386
|
+
return;
|
|
5387
|
+
}
|
|
5388
|
+
this.pendingPanelOpen = false;
|
|
5389
|
+
if (restoreFocus) {
|
|
5390
|
+
this.pendingTrigger?.nativeElement.focus();
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
onPendingTriggerKeydown(event) {
|
|
5394
|
+
if (event.key === 'ArrowDown' && this.pendingFiles.length > 0) {
|
|
5395
|
+
event.preventDefault();
|
|
5396
|
+
if (!this.pendingPanelOpen) {
|
|
5397
|
+
this.pendingPanelOpen = true;
|
|
5398
|
+
}
|
|
5399
|
+
this.focusPendingPanelSoon();
|
|
5400
|
+
}
|
|
5401
|
+
if (event.key === 'Escape') {
|
|
5402
|
+
event.preventDefault();
|
|
5403
|
+
this.closePendingPanel();
|
|
5404
|
+
}
|
|
5405
|
+
}
|
|
5406
|
+
onPendingPanelKeydown(event) {
|
|
5407
|
+
if (event.key === 'Escape') {
|
|
5408
|
+
event.preventDefault();
|
|
5409
|
+
this.closePendingPanel();
|
|
5410
|
+
}
|
|
5411
|
+
}
|
|
5412
|
+
onPendingOverlayDetach() {
|
|
5413
|
+
if (this.pendingPanelOpen) {
|
|
5414
|
+
this.pendingPanelOpen = false;
|
|
5415
|
+
this.pendingTrigger?.nativeElement.focus();
|
|
5416
|
+
}
|
|
4145
5417
|
}
|
|
4146
5418
|
// Ações dos ícones (placeholders para próxima sub-issue)
|
|
4147
5419
|
onOpenInfo() { }
|
|
@@ -4150,117 +5422,215 @@ class PdxFilesUploadFieldComponent extends SimpleBaseInputComponent {
|
|
|
4150
5422
|
this.lastFileName = null;
|
|
4151
5423
|
this.batchCount = 0;
|
|
4152
5424
|
this.uploadError = null;
|
|
5425
|
+
this.pendingFiles = [];
|
|
5426
|
+
this.uploader?.resetPendingFiles();
|
|
4153
5427
|
try {
|
|
4154
5428
|
this.setValue(null);
|
|
4155
5429
|
const c = this.control();
|
|
4156
5430
|
c.markAsPristine();
|
|
4157
5431
|
c.markAsUntouched();
|
|
4158
|
-
|
|
5432
|
+
this.updateUploadValidationState();
|
|
4159
5433
|
}
|
|
4160
5434
|
catch { }
|
|
4161
5435
|
}
|
|
4162
5436
|
get policySummary() {
|
|
4163
|
-
const
|
|
4164
|
-
|
|
4165
|
-
|
|
4166
|
-
const
|
|
4167
|
-
|
|
5437
|
+
const accepts = this.config?.ui?.accept?.length
|
|
5438
|
+
? this.config.ui.accept.join(', ')
|
|
5439
|
+
: this.t('fieldPolicyNotConfigured', 'Não configurado');
|
|
5440
|
+
const maxBytes = this.config?.limits?.maxFileSizeBytes;
|
|
5441
|
+
const maxPerFile = maxBytes
|
|
5442
|
+
? this.formatBytes(maxBytes)
|
|
5443
|
+
: this.t('fieldPolicyNotConfigured', 'Não configurado');
|
|
5444
|
+
const maxItems = this.config?.limits?.maxFilesPerBulk ?? 1;
|
|
5445
|
+
return [
|
|
5446
|
+
`${this.t('fieldPolicyTypes', 'Tipos')}: ${accepts}`,
|
|
5447
|
+
`${this.t('fieldPolicyMaxPerFile', 'Máx/arquivo')}: ${maxPerFile}`,
|
|
5448
|
+
`${this.t('fieldPolicyMaxItems', 'Máx/itens')}: ${maxItems}`,
|
|
5449
|
+
].join(' • ');
|
|
4168
5450
|
}
|
|
4169
5451
|
onRemovePendingFile(file, event) {
|
|
4170
|
-
event.stopPropagation();
|
|
5452
|
+
event.stopPropagation();
|
|
4171
5453
|
this.uploader?.removePendingFile(file);
|
|
4172
5454
|
}
|
|
5455
|
+
pendingFileSummary(file) {
|
|
5456
|
+
return `${file.name} ${this.formatBytes(file.size)}`;
|
|
5457
|
+
}
|
|
4173
5458
|
formatBytes(bytes) {
|
|
4174
|
-
if (bytes === 0)
|
|
4175
|
-
return
|
|
5459
|
+
if (bytes === 0) {
|
|
5460
|
+
return `0 ${this.t('sizeUnitBytes', 'Bytes')}`;
|
|
5461
|
+
}
|
|
4176
5462
|
const k = 1024;
|
|
4177
|
-
const sizes = [
|
|
5463
|
+
const sizes = [
|
|
5464
|
+
this.t('sizeUnitBytes', 'Bytes'),
|
|
5465
|
+
this.t('sizeUnitKB', 'KB'),
|
|
5466
|
+
this.t('sizeUnitMB', 'MB'),
|
|
5467
|
+
this.t('sizeUnitGB', 'GB'),
|
|
5468
|
+
this.t('sizeUnitTB', 'TB'),
|
|
5469
|
+
];
|
|
4178
5470
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
4179
5471
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
|
|
4180
5472
|
}
|
|
5473
|
+
focusPendingPanelSoon() {
|
|
5474
|
+
setTimeout(() => {
|
|
5475
|
+
this.pendingPanel?.nativeElement.focus();
|
|
5476
|
+
});
|
|
5477
|
+
}
|
|
5478
|
+
updateUploadValidationState(validationKey) {
|
|
5479
|
+
const currentErrors = { ...(this.control().errors ?? {}) };
|
|
5480
|
+
delete currentErrors.upload;
|
|
5481
|
+
delete currentErrors.uploadPartial;
|
|
5482
|
+
if (validationKey) {
|
|
5483
|
+
currentErrors[validationKey] = true;
|
|
5484
|
+
}
|
|
5485
|
+
this.control().setErrors(Object.keys(currentErrors).length ? currentErrors : null);
|
|
5486
|
+
}
|
|
4181
5487
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PdxFilesUploadFieldComponent, deps: null, target: i0.ɵɵFactoryTarget.Component });
|
|
4182
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PdxFilesUploadFieldComponent, isStandalone: true, selector: "pdx-material-files-upload", inputs: { config: "config", valueMode: "valueMode", baseUrl: "baseUrl" }, host: { properties: { "class": "componentCssClasses()", "attr.data-field-type": "\"files-upload\"", "attr.data-field-name": "metadata()?.name", "attr.data-component-id": "componentId()" } }, viewQueries: [{ propertyName: "uploader", first: true, predicate: ["uploader"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
|
|
5488
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.17", type: PdxFilesUploadFieldComponent, isStandalone: true, selector: "pdx-material-files-upload", inputs: { config: "config", valueMode: "valueMode", baseUrl: "baseUrl" }, outputs: { uploadSuccess: "uploadSuccess", bulkComplete: "bulkComplete", error: "error" }, host: { properties: { "class": "componentCssClasses()", "attr.data-field-type": "\"files-upload\"", "attr.data-field-name": "metadata()?.name", "attr.data-component-id": "componentId()" } }, providers: [providePraxisFilesUploadI18n()], viewQueries: [{ propertyName: "uploader", first: true, predicate: ["uploader"], descendants: true }, { propertyName: "pendingTrigger", first: true, predicate: ["pendingTrigger"], descendants: true }, { propertyName: "pendingPanel", first: true, predicate: ["pendingPanel"], descendants: true }], usesInheritance: true, ngImport: i0, template: `
|
|
4183
5489
|
<!-- Casca compacta tipo input -->
|
|
4184
5490
|
<div class="pdx-upload-field-shell"
|
|
4185
|
-
role="
|
|
4186
|
-
tabindex="0"
|
|
4187
|
-
[attr.aria-describedby]="componentId() + '-messages'"
|
|
5491
|
+
role="group"
|
|
4188
5492
|
[class.error]="showUploadError"
|
|
4189
5493
|
[class.disabled]="isShellDisabled()"
|
|
4190
|
-
[attr.aria-disabled]="isShellDisabled() ? 'true' : null"
|
|
4191
5494
|
[class.proximity-active]="isProximityActive"
|
|
4192
|
-
|
|
4193
|
-
|
|
4194
|
-
|
|
5495
|
+
[attr.aria-busy]="isUploadingState()"
|
|
5496
|
+
[attr.aria-invalid]="showUploadError"
|
|
5497
|
+
[attr.aria-label]="shellAriaLabel">
|
|
4195
5498
|
<mat-icon class="prefix" aria-hidden="true">attach_file</mat-icon>
|
|
4196
5499
|
<div class="content">
|
|
4197
5500
|
<!-- ESTADO 1: Sem arquivos pendentes -->
|
|
4198
|
-
<ng-container *ngIf="pendingFiles.length === 0
|
|
4199
|
-
<
|
|
4200
|
-
|
|
4201
|
-
|
|
5501
|
+
<ng-container *ngIf="pendingFiles.length === 0">
|
|
5502
|
+
<button
|
|
5503
|
+
type="button"
|
|
5504
|
+
class="shell-main-btn"
|
|
5505
|
+
[disabled]="isShellDisabled()"
|
|
5506
|
+
[attr.aria-describedby]="componentId() + '-messages'"
|
|
5507
|
+
(click)="openFromShell()"
|
|
5508
|
+
>
|
|
5509
|
+
<span class="text-stack">
|
|
5510
|
+
<span class="placeholder" *ngIf="!lastFileName">{{ t('placeholder','Selecione ou solte arquivos…') }}</span>
|
|
5511
|
+
<span class="value" *ngIf="lastFileName" [title]="lastFileName">{{ lastFileName }}</span>
|
|
5512
|
+
<span class="meta">{{ statusSummary }}</span>
|
|
5513
|
+
</span>
|
|
5514
|
+
<span class="chip" *ngIf="batchCount > 1" [attr.aria-label]="additionalFilesChipAriaLabel">+{{ batchCount - 1 }}</span>
|
|
5515
|
+
</button>
|
|
4202
5516
|
</ng-container>
|
|
4203
5517
|
|
|
4204
5518
|
<!-- ESTADO 2: Com arquivos pendentes -->
|
|
4205
|
-
<ng-
|
|
4206
|
-
<button
|
|
4207
|
-
|
|
4208
|
-
|
|
5519
|
+
<ng-container *ngIf="pendingFiles.length > 0">
|
|
5520
|
+
<button
|
|
5521
|
+
#pendingTrigger
|
|
5522
|
+
type="button"
|
|
5523
|
+
class="pending-text-btn"
|
|
5524
|
+
[attr.aria-describedby]="componentId() + '-messages'"
|
|
5525
|
+
[attr.aria-controls]="pendingPanelId"
|
|
5526
|
+
[attr.aria-expanded]="pendingPanelOpen"
|
|
5527
|
+
cdkOverlayOrigin
|
|
5528
|
+
#pendingOverlayOrigin="cdkOverlayOrigin"
|
|
5529
|
+
(click)="togglePendingPanel()"
|
|
5530
|
+
(keydown)="onPendingTriggerKeydown($event)"
|
|
5531
|
+
>
|
|
5532
|
+
<span class="text-stack">
|
|
5533
|
+
<span class="value">{{ pendingFiles.length }} {{ t('fieldPendingCountSuffix','file(s) selected') }}</span>
|
|
5534
|
+
<span class="meta">{{ t('fieldPendingSummary','Arquivos prontos para envio') }}</span>
|
|
5535
|
+
</span>
|
|
5536
|
+
<mat-icon aria-hidden="true">arrow_drop_down</mat-icon>
|
|
4209
5537
|
</button>
|
|
4210
|
-
|
|
5538
|
+
<ng-template
|
|
5539
|
+
cdkConnectedOverlay
|
|
5540
|
+
[cdkConnectedOverlayOrigin]="pendingOverlayOrigin"
|
|
5541
|
+
[cdkConnectedOverlayOpen]="pendingPanelOpen"
|
|
5542
|
+
[cdkConnectedOverlayHasBackdrop]="true"
|
|
5543
|
+
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
|
|
5544
|
+
[cdkConnectedOverlayPositions]="pendingOverlayPositions"
|
|
5545
|
+
(backdropClick)="closePendingPanel()"
|
|
5546
|
+
(detach)="onPendingOverlayDetach()"
|
|
5547
|
+
>
|
|
5548
|
+
<div
|
|
5549
|
+
#pendingPanel
|
|
5550
|
+
class="pending-files-overlay"
|
|
5551
|
+
[id]="pendingPanelId"
|
|
5552
|
+
role="list"
|
|
5553
|
+
tabindex="-1"
|
|
5554
|
+
[attr.aria-label]="t('fieldPendingSummary','Arquivos prontos para envio')"
|
|
5555
|
+
(keydown)="onPendingPanelKeydown($event)"
|
|
5556
|
+
>
|
|
5557
|
+
<div
|
|
5558
|
+
class="pending-file-item"
|
|
5559
|
+
*ngFor="let file of pendingFiles"
|
|
5560
|
+
role="listitem"
|
|
5561
|
+
[attr.aria-label]="pendingFileSummary(file)"
|
|
5562
|
+
>
|
|
5563
|
+
<div class="file-info" [title]="file.name">
|
|
5564
|
+
<span class="file-name">{{ file.name }}</span>
|
|
5565
|
+
<span class="file-size">({{ formatBytes(file.size) }})</span>
|
|
5566
|
+
</div>
|
|
5567
|
+
<span class="spacer"></span>
|
|
5568
|
+
<button
|
|
5569
|
+
type="button"
|
|
5570
|
+
mat-icon-button
|
|
5571
|
+
class="remove-pending-btn"
|
|
5572
|
+
(click)="onRemovePendingFile(file, $event)"
|
|
5573
|
+
[attr.aria-label]="t('removePendingFile','Remove selected file') + ': ' + file.name"
|
|
5574
|
+
>
|
|
5575
|
+
<mat-icon>close</mat-icon>
|
|
5576
|
+
</button>
|
|
5577
|
+
</div>
|
|
5578
|
+
</div>
|
|
5579
|
+
</ng-template>
|
|
5580
|
+
</ng-container>
|
|
4211
5581
|
</div>
|
|
4212
5582
|
<div class="suffixes">
|
|
4213
5583
|
<!-- Ações para arquivos pendentes -->
|
|
4214
5584
|
<ng-container *ngIf="pendingFiles.length > 0">
|
|
4215
|
-
<button
|
|
4216
|
-
|
|
5585
|
+
<button
|
|
5586
|
+
type="button"
|
|
5587
|
+
class="action-btn primary"
|
|
5588
|
+
[attr.aria-label]="t('fieldActionUpload','Enviar arquivos selecionados')"
|
|
5589
|
+
(click)="onTriggerUpload(); $event.stopPropagation()"
|
|
5590
|
+
>
|
|
5591
|
+
<mat-icon aria-hidden="true">upload</mat-icon>
|
|
5592
|
+
<span>{{ t('fieldActionUploadShort','Enviar') }}</span>
|
|
4217
5593
|
</button>
|
|
4218
|
-
<button
|
|
4219
|
-
|
|
5594
|
+
<button
|
|
5595
|
+
type="button"
|
|
5596
|
+
class="action-btn"
|
|
5597
|
+
[attr.aria-label]="t('fieldActionCancel','Cancelar seleção atual')"
|
|
5598
|
+
(click)="onClearSelection(); $event.stopPropagation()"
|
|
5599
|
+
>
|
|
5600
|
+
<mat-icon aria-hidden="true">cancel</mat-icon>
|
|
5601
|
+
<span>{{ t('fieldActionCancelShort','Cancelar') }}</span>
|
|
4220
5602
|
</button>
|
|
4221
5603
|
</ng-container>
|
|
4222
5604
|
|
|
4223
5605
|
<!-- Ações padrão -->
|
|
4224
|
-
<button type="button" class="icon-btn" title="Informações" aria-label="Informações" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matTooltip]="policySummary">
|
|
5606
|
+
<button type="button" class="icon-btn" [attr.title]="t('fieldInfoAction','Informações')" [attr.aria-label]="t('fieldInfoAction','Informações')" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matTooltip]="policySummary">
|
|
4225
5607
|
<mat-icon>info</mat-icon>
|
|
4226
5608
|
</button>
|
|
4227
|
-
<button type="button" class="icon-btn" title="Mais ações" aria-label="Mais ações" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matMenuTriggerFor]="moreMenu">
|
|
5609
|
+
<button type="button" class="icon-btn" [attr.title]="t('fieldMoreActions','Mais ações')" [attr.aria-label]="t('fieldMoreActions','Mais ações')" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matMenuTriggerFor]="moreMenu">
|
|
4228
5610
|
<mat-icon>more_horiz</mat-icon>
|
|
4229
5611
|
</button>
|
|
4230
5612
|
<mat-menu #moreMenu="matMenu">
|
|
4231
5613
|
<button mat-menu-item (click)="openFromShell()">
|
|
4232
5614
|
<mat-icon>upload_file</mat-icon>
|
|
4233
|
-
<span>Selecionar arquivo(s)</span>
|
|
5615
|
+
<span>{{ t('fieldActionSelect','Selecionar arquivo(s)') }}</span>
|
|
4234
5616
|
</button>
|
|
4235
5617
|
<button mat-menu-item (click)="onClear()" [disabled]="!lastFileName && batchCount === 0">
|
|
4236
5618
|
<mat-icon>clear</mat-icon>
|
|
4237
|
-
<span>Limpar</span>
|
|
5619
|
+
<span>{{ t('fieldActionClear','Limpar') }}</span>
|
|
4238
5620
|
</button>
|
|
4239
5621
|
</mat-menu>
|
|
4240
5622
|
</div>
|
|
4241
5623
|
</div>
|
|
4242
5624
|
|
|
4243
|
-
<!-- Menu para exibir arquivos pendentes -->
|
|
4244
|
-
<mat-menu #pendingFilesMenu="matMenu" class="pending-files-menu">
|
|
4245
|
-
<ng-template matMenuContent>
|
|
4246
|
-
<button mat-menu-item *ngFor="let file of pendingFiles" (click)="$event.stopPropagation(); $event.preventDefault();" [title]="file.name">
|
|
4247
|
-
<div class="pending-file-item">
|
|
4248
|
-
<div class="file-info">
|
|
4249
|
-
<span class="file-name">{{ file.name }}</span>
|
|
4250
|
-
<span class="file-size">({{ formatBytes(file.size) }})</span>
|
|
4251
|
-
</div>
|
|
4252
|
-
<span class="spacer"></span>
|
|
4253
|
-
<button mat-icon-button class="remove-pending-btn" (click)="onRemovePendingFile(file, $event)" [attr.aria-label]="'Remover ' + file.name">
|
|
4254
|
-
<mat-icon>close</mat-icon>
|
|
4255
|
-
</button>
|
|
4256
|
-
</div>
|
|
4257
|
-
</button>
|
|
4258
|
-
</ng-template>
|
|
4259
|
-
</mat-menu>
|
|
4260
|
-
|
|
4261
5625
|
<!-- Mensagens do campo (hint/erro) -->
|
|
4262
|
-
<div
|
|
5626
|
+
<div
|
|
5627
|
+
class="field-messages"
|
|
5628
|
+
[id]="componentId() + '-messages'"
|
|
5629
|
+
[attr.role]="showUploadError ? 'alert' : 'status'"
|
|
5630
|
+
[attr.aria-live]="showUploadError ? 'assertive' : 'polite'"
|
|
5631
|
+
>
|
|
4263
5632
|
<div class="hint" *ngIf="metadata()?.hint && !showUploadError">{{ metadata()?.hint }}</div>
|
|
5633
|
+
<div class="hint" *ngIf="!metadata()?.hint && !showUploadError">{{ statusSummary }}</div>
|
|
4264
5634
|
<div class="error" *ngIf="showUploadError">{{ uploadError || t('genericUploadError','Erro no envio de arquivo.') }}</div>
|
|
4265
5635
|
</div>
|
|
4266
5636
|
|
|
@@ -4275,94 +5645,167 @@ class PdxFilesUploadFieldComponent extends SimpleBaseInputComponent {
|
|
|
4275
5645
|
(uploadSuccess)="onUploadSuccess($event)"
|
|
4276
5646
|
(bulkComplete)="onBulkComplete($event)"
|
|
4277
5647
|
(error)="onUploadError($event)"
|
|
4278
|
-
(
|
|
5648
|
+
(proximityChange)="onProximityChange($event)"
|
|
5649
|
+
(pendingStateChange)="handlePendingState($event)"
|
|
4279
5650
|
/>
|
|
4280
|
-
`, isInline: true, styles: [":host{position:relative}.pdx-upload-field-shell{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;border:1px solid var(--pfx-surface-border, #ccc);border-radius:8px;background:var(--pfx-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface);min-height:40px
|
|
5651
|
+
`, isInline: true, styles: [":host{position:relative}.pdx-upload-field-shell{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;border:1px solid var(--pfx-surface-border, #ccc);border-radius:8px;background:var(--pfx-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface);min-height:40px}.pdx-upload-field-shell:focus-within{outline:2px solid var(--md-sys-color-primary,#1976d2);outline-offset:2px}.pdx-upload-field-shell.error{border-color:var(--md-sys-color-error,#b00020)}.pdx-upload-field-shell.disabled{opacity:.6;cursor:not-allowed}.pdx-upload-field-shell .prefix{color:var(--md-sys-color-primary,#1976d2)}.pdx-upload-field-shell .content{flex:1;min-width:0;display:flex;align-items:center;gap:.5rem}.pdx-upload-field-shell .shell-main-btn{display:flex;align-items:center;gap:.75rem;flex:1;min-width:0;padding:0;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}.pdx-upload-field-shell .shell-main-btn:disabled{cursor:not-allowed}.pdx-upload-field-shell .text-stack{display:flex;flex-direction:column;min-width:0;gap:.125rem}.pdx-upload-field-shell .placeholder{color:var(--md-sys-color-on-surface-variant)}.pdx-upload-field-shell .value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-upload-field-shell .meta{font-size:.78rem;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-upload-field-shell .chip{font-size:.75rem;padding:0 .4rem;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.pdx-upload-field-shell .suffixes{display:flex;align-items:center;gap:.375rem}.pdx-upload-field-shell .action-btn{display:inline-flex;align-items:center;gap:.35rem;border:1px solid var(--pfx-surface-border, #ccc);background:transparent;padding:.35rem .6rem;border-radius:999px;cursor:pointer;color:var(--md-sys-color-on-surface);font:inherit}.pdx-upload-field-shell .action-btn.primary{background:var(--md-sys-color-primary);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pdx-upload-field-shell .action-btn mat-icon{font-size:18px;width:18px;height:18px}.pdx-upload-field-shell .icon-btn{border:0;background:transparent;padding:.25rem;border-radius:6px;cursor:pointer;color:var(--md-sys-color-on-surface-variant)}.pdx-upload-field-shell .icon-btn:hover{background:#ffffff0d}.pdx-upload-field-shell.disabled .icon-btn,.pdx-upload-field-shell.disabled .action-btn{cursor:not-allowed}.field-messages{font-size:.82rem;margin-top:.25rem}.field-messages .hint{opacity:.8}.field-messages .error{color:var(--md-sys-color-error,#b00020)}.pending-text-btn{display:flex;align-items:center;gap:.5rem;background:0;border:0;color:var(--md-sys-color-on-surface);font-family:inherit;font-size:inherit;padding:0;margin:0;cursor:pointer;min-width:0;text-align:left}.pending-text-btn span,.pending-text-btn mat-icon{color:inherit}.pending-files-overlay{display:flex;flex-direction:column;min-width:min(320px,calc(100vw - 32px));max-width:min(480px,calc(100vw - 32px));max-height:240px;overflow:auto;padding:.25rem 0;border:1px solid var(--pfx-surface-border, #ccc);border-radius:12px;background:var(--pfx-surface, var(--md-sys-color-surface));box-shadow:0 12px 30px #0000002e;outline:none}.pending-file-item{display:flex;align-items:center;width:100%;gap:.5rem;padding:8px 16px}.pending-file-item .file-info{flex:1;min-width:0;text-align:left}.pending-file-item .file-name{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pending-file-item .file-size{font-size:.8rem;opacity:.7}.pending-file-item .spacer{flex:1 1 auto}.pending-file-item .remove-pending-btn{color:var(--md-sys-color-on-surface-variant)}.pdx-upload-field-shell.proximity-active{border-style:dashed;border-color:var(--md-sys-color-primary);transform:scale(1.02);background:var(--md-sys-color-surface-container-low);transition:all .2s ease-in-out}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i9.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i9.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: PraxisFilesUpload, selector: "praxis-files-upload", inputs: ["config", "filesUploadId", "componentInstanceId", "baseUrl", "displayMode", "context", "editModeEnabled"], outputs: ["uploadSuccess", "bulkComplete", "error", "rateLimited", "uploadStart", "uploadProgress", "retry", "download", "copyLink", "detailsOpened", "detailsClosed", "pendingStateChange", "proximityChange", "readinessChange"] }, { kind: "ngmodule", type: MatIconModule }, { kind: "component", type: i10.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "ngmodule", type: MatMenuModule }, { kind: "component", type: i14.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i14.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i14.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "ngmodule", type: MatTooltipModule }, { kind: "directive", type: i11$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }] });
|
|
4281
5652
|
}
|
|
4282
5653
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: PdxFilesUploadFieldComponent, decorators: [{
|
|
4283
5654
|
type: Component,
|
|
4284
|
-
args: [{ selector: 'pdx-material-files-upload', standalone: true,
|
|
5655
|
+
args: [{ selector: 'pdx-material-files-upload', standalone: true, providers: [providePraxisFilesUploadI18n()], imports: [
|
|
5656
|
+
CommonModule,
|
|
5657
|
+
PraxisFilesUpload,
|
|
5658
|
+
MatIconModule,
|
|
5659
|
+
MatMenuModule,
|
|
5660
|
+
MatTooltipModule,
|
|
5661
|
+
CdkConnectedOverlay,
|
|
5662
|
+
CdkOverlayOrigin,
|
|
5663
|
+
], template: `
|
|
4285
5664
|
<!-- Casca compacta tipo input -->
|
|
4286
5665
|
<div class="pdx-upload-field-shell"
|
|
4287
|
-
role="
|
|
4288
|
-
tabindex="0"
|
|
4289
|
-
[attr.aria-describedby]="componentId() + '-messages'"
|
|
5666
|
+
role="group"
|
|
4290
5667
|
[class.error]="showUploadError"
|
|
4291
5668
|
[class.disabled]="isShellDisabled()"
|
|
4292
|
-
[attr.aria-disabled]="isShellDisabled() ? 'true' : null"
|
|
4293
5669
|
[class.proximity-active]="isProximityActive"
|
|
4294
|
-
|
|
4295
|
-
|
|
4296
|
-
|
|
5670
|
+
[attr.aria-busy]="isUploadingState()"
|
|
5671
|
+
[attr.aria-invalid]="showUploadError"
|
|
5672
|
+
[attr.aria-label]="shellAriaLabel">
|
|
4297
5673
|
<mat-icon class="prefix" aria-hidden="true">attach_file</mat-icon>
|
|
4298
5674
|
<div class="content">
|
|
4299
5675
|
<!-- ESTADO 1: Sem arquivos pendentes -->
|
|
4300
|
-
<ng-container *ngIf="pendingFiles.length === 0
|
|
4301
|
-
<
|
|
4302
|
-
|
|
4303
|
-
|
|
5676
|
+
<ng-container *ngIf="pendingFiles.length === 0">
|
|
5677
|
+
<button
|
|
5678
|
+
type="button"
|
|
5679
|
+
class="shell-main-btn"
|
|
5680
|
+
[disabled]="isShellDisabled()"
|
|
5681
|
+
[attr.aria-describedby]="componentId() + '-messages'"
|
|
5682
|
+
(click)="openFromShell()"
|
|
5683
|
+
>
|
|
5684
|
+
<span class="text-stack">
|
|
5685
|
+
<span class="placeholder" *ngIf="!lastFileName">{{ t('placeholder','Selecione ou solte arquivos…') }}</span>
|
|
5686
|
+
<span class="value" *ngIf="lastFileName" [title]="lastFileName">{{ lastFileName }}</span>
|
|
5687
|
+
<span class="meta">{{ statusSummary }}</span>
|
|
5688
|
+
</span>
|
|
5689
|
+
<span class="chip" *ngIf="batchCount > 1" [attr.aria-label]="additionalFilesChipAriaLabel">+{{ batchCount - 1 }}</span>
|
|
5690
|
+
</button>
|
|
4304
5691
|
</ng-container>
|
|
4305
5692
|
|
|
4306
5693
|
<!-- ESTADO 2: Com arquivos pendentes -->
|
|
4307
|
-
<ng-
|
|
4308
|
-
<button
|
|
4309
|
-
|
|
4310
|
-
|
|
5694
|
+
<ng-container *ngIf="pendingFiles.length > 0">
|
|
5695
|
+
<button
|
|
5696
|
+
#pendingTrigger
|
|
5697
|
+
type="button"
|
|
5698
|
+
class="pending-text-btn"
|
|
5699
|
+
[attr.aria-describedby]="componentId() + '-messages'"
|
|
5700
|
+
[attr.aria-controls]="pendingPanelId"
|
|
5701
|
+
[attr.aria-expanded]="pendingPanelOpen"
|
|
5702
|
+
cdkOverlayOrigin
|
|
5703
|
+
#pendingOverlayOrigin="cdkOverlayOrigin"
|
|
5704
|
+
(click)="togglePendingPanel()"
|
|
5705
|
+
(keydown)="onPendingTriggerKeydown($event)"
|
|
5706
|
+
>
|
|
5707
|
+
<span class="text-stack">
|
|
5708
|
+
<span class="value">{{ pendingFiles.length }} {{ t('fieldPendingCountSuffix','file(s) selected') }}</span>
|
|
5709
|
+
<span class="meta">{{ t('fieldPendingSummary','Arquivos prontos para envio') }}</span>
|
|
5710
|
+
</span>
|
|
5711
|
+
<mat-icon aria-hidden="true">arrow_drop_down</mat-icon>
|
|
4311
5712
|
</button>
|
|
4312
|
-
|
|
5713
|
+
<ng-template
|
|
5714
|
+
cdkConnectedOverlay
|
|
5715
|
+
[cdkConnectedOverlayOrigin]="pendingOverlayOrigin"
|
|
5716
|
+
[cdkConnectedOverlayOpen]="pendingPanelOpen"
|
|
5717
|
+
[cdkConnectedOverlayHasBackdrop]="true"
|
|
5718
|
+
cdkConnectedOverlayBackdropClass="cdk-overlay-transparent-backdrop"
|
|
5719
|
+
[cdkConnectedOverlayPositions]="pendingOverlayPositions"
|
|
5720
|
+
(backdropClick)="closePendingPanel()"
|
|
5721
|
+
(detach)="onPendingOverlayDetach()"
|
|
5722
|
+
>
|
|
5723
|
+
<div
|
|
5724
|
+
#pendingPanel
|
|
5725
|
+
class="pending-files-overlay"
|
|
5726
|
+
[id]="pendingPanelId"
|
|
5727
|
+
role="list"
|
|
5728
|
+
tabindex="-1"
|
|
5729
|
+
[attr.aria-label]="t('fieldPendingSummary','Arquivos prontos para envio')"
|
|
5730
|
+
(keydown)="onPendingPanelKeydown($event)"
|
|
5731
|
+
>
|
|
5732
|
+
<div
|
|
5733
|
+
class="pending-file-item"
|
|
5734
|
+
*ngFor="let file of pendingFiles"
|
|
5735
|
+
role="listitem"
|
|
5736
|
+
[attr.aria-label]="pendingFileSummary(file)"
|
|
5737
|
+
>
|
|
5738
|
+
<div class="file-info" [title]="file.name">
|
|
5739
|
+
<span class="file-name">{{ file.name }}</span>
|
|
5740
|
+
<span class="file-size">({{ formatBytes(file.size) }})</span>
|
|
5741
|
+
</div>
|
|
5742
|
+
<span class="spacer"></span>
|
|
5743
|
+
<button
|
|
5744
|
+
type="button"
|
|
5745
|
+
mat-icon-button
|
|
5746
|
+
class="remove-pending-btn"
|
|
5747
|
+
(click)="onRemovePendingFile(file, $event)"
|
|
5748
|
+
[attr.aria-label]="t('removePendingFile','Remove selected file') + ': ' + file.name"
|
|
5749
|
+
>
|
|
5750
|
+
<mat-icon>close</mat-icon>
|
|
5751
|
+
</button>
|
|
5752
|
+
</div>
|
|
5753
|
+
</div>
|
|
5754
|
+
</ng-template>
|
|
5755
|
+
</ng-container>
|
|
4313
5756
|
</div>
|
|
4314
5757
|
<div class="suffixes">
|
|
4315
5758
|
<!-- Ações para arquivos pendentes -->
|
|
4316
5759
|
<ng-container *ngIf="pendingFiles.length > 0">
|
|
4317
|
-
<button
|
|
4318
|
-
|
|
5760
|
+
<button
|
|
5761
|
+
type="button"
|
|
5762
|
+
class="action-btn primary"
|
|
5763
|
+
[attr.aria-label]="t('fieldActionUpload','Enviar arquivos selecionados')"
|
|
5764
|
+
(click)="onTriggerUpload(); $event.stopPropagation()"
|
|
5765
|
+
>
|
|
5766
|
+
<mat-icon aria-hidden="true">upload</mat-icon>
|
|
5767
|
+
<span>{{ t('fieldActionUploadShort','Enviar') }}</span>
|
|
4319
5768
|
</button>
|
|
4320
|
-
<button
|
|
4321
|
-
|
|
5769
|
+
<button
|
|
5770
|
+
type="button"
|
|
5771
|
+
class="action-btn"
|
|
5772
|
+
[attr.aria-label]="t('fieldActionCancel','Cancelar seleção atual')"
|
|
5773
|
+
(click)="onClearSelection(); $event.stopPropagation()"
|
|
5774
|
+
>
|
|
5775
|
+
<mat-icon aria-hidden="true">cancel</mat-icon>
|
|
5776
|
+
<span>{{ t('fieldActionCancelShort','Cancelar') }}</span>
|
|
4322
5777
|
</button>
|
|
4323
5778
|
</ng-container>
|
|
4324
5779
|
|
|
4325
5780
|
<!-- Ações padrão -->
|
|
4326
|
-
<button type="button" class="icon-btn" title="Informações" aria-label="Informações" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matTooltip]="policySummary">
|
|
5781
|
+
<button type="button" class="icon-btn" [attr.title]="t('fieldInfoAction','Informações')" [attr.aria-label]="t('fieldInfoAction','Informações')" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matTooltip]="policySummary">
|
|
4327
5782
|
<mat-icon>info</mat-icon>
|
|
4328
5783
|
</button>
|
|
4329
|
-
<button type="button" class="icon-btn" title="Mais ações" aria-label="Mais ações" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matMenuTriggerFor]="moreMenu">
|
|
5784
|
+
<button type="button" class="icon-btn" [attr.title]="t('fieldMoreActions','Mais ações')" [attr.aria-label]="t('fieldMoreActions','Mais ações')" (click)="$event.stopPropagation()" [disabled]="isShellDisabled()" [matMenuTriggerFor]="moreMenu">
|
|
4330
5785
|
<mat-icon>more_horiz</mat-icon>
|
|
4331
5786
|
</button>
|
|
4332
5787
|
<mat-menu #moreMenu="matMenu">
|
|
4333
5788
|
<button mat-menu-item (click)="openFromShell()">
|
|
4334
5789
|
<mat-icon>upload_file</mat-icon>
|
|
4335
|
-
<span>Selecionar arquivo(s)</span>
|
|
5790
|
+
<span>{{ t('fieldActionSelect','Selecionar arquivo(s)') }}</span>
|
|
4336
5791
|
</button>
|
|
4337
5792
|
<button mat-menu-item (click)="onClear()" [disabled]="!lastFileName && batchCount === 0">
|
|
4338
5793
|
<mat-icon>clear</mat-icon>
|
|
4339
|
-
<span>Limpar</span>
|
|
5794
|
+
<span>{{ t('fieldActionClear','Limpar') }}</span>
|
|
4340
5795
|
</button>
|
|
4341
5796
|
</mat-menu>
|
|
4342
5797
|
</div>
|
|
4343
5798
|
</div>
|
|
4344
5799
|
|
|
4345
|
-
<!-- Menu para exibir arquivos pendentes -->
|
|
4346
|
-
<mat-menu #pendingFilesMenu="matMenu" class="pending-files-menu">
|
|
4347
|
-
<ng-template matMenuContent>
|
|
4348
|
-
<button mat-menu-item *ngFor="let file of pendingFiles" (click)="$event.stopPropagation(); $event.preventDefault();" [title]="file.name">
|
|
4349
|
-
<div class="pending-file-item">
|
|
4350
|
-
<div class="file-info">
|
|
4351
|
-
<span class="file-name">{{ file.name }}</span>
|
|
4352
|
-
<span class="file-size">({{ formatBytes(file.size) }})</span>
|
|
4353
|
-
</div>
|
|
4354
|
-
<span class="spacer"></span>
|
|
4355
|
-
<button mat-icon-button class="remove-pending-btn" (click)="onRemovePendingFile(file, $event)" [attr.aria-label]="'Remover ' + file.name">
|
|
4356
|
-
<mat-icon>close</mat-icon>
|
|
4357
|
-
</button>
|
|
4358
|
-
</div>
|
|
4359
|
-
</button>
|
|
4360
|
-
</ng-template>
|
|
4361
|
-
</mat-menu>
|
|
4362
|
-
|
|
4363
5800
|
<!-- Mensagens do campo (hint/erro) -->
|
|
4364
|
-
<div
|
|
5801
|
+
<div
|
|
5802
|
+
class="field-messages"
|
|
5803
|
+
[id]="componentId() + '-messages'"
|
|
5804
|
+
[attr.role]="showUploadError ? 'alert' : 'status'"
|
|
5805
|
+
[attr.aria-live]="showUploadError ? 'assertive' : 'polite'"
|
|
5806
|
+
>
|
|
4365
5807
|
<div class="hint" *ngIf="metadata()?.hint && !showUploadError">{{ metadata()?.hint }}</div>
|
|
5808
|
+
<div class="hint" *ngIf="!metadata()?.hint && !showUploadError">{{ statusSummary }}</div>
|
|
4366
5809
|
<div class="error" *ngIf="showUploadError">{{ uploadError || t('genericUploadError','Erro no envio de arquivo.') }}</div>
|
|
4367
5810
|
</div>
|
|
4368
5811
|
|
|
@@ -4377,83 +5820,38 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
|
|
|
4377
5820
|
(uploadSuccess)="onUploadSuccess($event)"
|
|
4378
5821
|
(bulkComplete)="onBulkComplete($event)"
|
|
4379
5822
|
(error)="onUploadError($event)"
|
|
4380
|
-
(
|
|
5823
|
+
(proximityChange)="onProximityChange($event)"
|
|
5824
|
+
(pendingStateChange)="handlePendingState($event)"
|
|
4381
5825
|
/>
|
|
4382
5826
|
`, host: {
|
|
4383
5827
|
'[class]': 'componentCssClasses()',
|
|
4384
5828
|
'[attr.data-field-type]': '"files-upload"',
|
|
4385
5829
|
'[attr.data-field-name]': 'metadata()?.name',
|
|
4386
5830
|
'[attr.data-component-id]': 'componentId()',
|
|
4387
|
-
}, styles: [":host{position:relative}.pdx-upload-field-shell{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;border:1px solid var(--pfx-surface-border, #ccc);border-radius:8px;background:var(--pfx-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface);min-height:40px
|
|
5831
|
+
}, styles: [":host{position:relative}.pdx-upload-field-shell{display:flex;align-items:center;gap:.5rem;padding:.5rem .75rem;border:1px solid var(--pfx-surface-border, #ccc);border-radius:8px;background:var(--pfx-surface, var(--md-sys-color-surface));color:var(--md-sys-color-on-surface);min-height:40px}.pdx-upload-field-shell:focus-within{outline:2px solid var(--md-sys-color-primary,#1976d2);outline-offset:2px}.pdx-upload-field-shell.error{border-color:var(--md-sys-color-error,#b00020)}.pdx-upload-field-shell.disabled{opacity:.6;cursor:not-allowed}.pdx-upload-field-shell .prefix{color:var(--md-sys-color-primary,#1976d2)}.pdx-upload-field-shell .content{flex:1;min-width:0;display:flex;align-items:center;gap:.5rem}.pdx-upload-field-shell .shell-main-btn{display:flex;align-items:center;gap:.75rem;flex:1;min-width:0;padding:0;border:0;background:transparent;color:inherit;font:inherit;text-align:left;cursor:pointer}.pdx-upload-field-shell .shell-main-btn:disabled{cursor:not-allowed}.pdx-upload-field-shell .text-stack{display:flex;flex-direction:column;min-width:0;gap:.125rem}.pdx-upload-field-shell .placeholder{color:var(--md-sys-color-on-surface-variant)}.pdx-upload-field-shell .value{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-upload-field-shell .meta{font-size:.78rem;color:var(--md-sys-color-on-surface-variant);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pdx-upload-field-shell .chip{font-size:.75rem;padding:0 .4rem;border-radius:999px;background:var(--md-sys-color-primary-container);color:var(--md-sys-color-on-primary-container)}.pdx-upload-field-shell .suffixes{display:flex;align-items:center;gap:.375rem}.pdx-upload-field-shell .action-btn{display:inline-flex;align-items:center;gap:.35rem;border:1px solid var(--pfx-surface-border, #ccc);background:transparent;padding:.35rem .6rem;border-radius:999px;cursor:pointer;color:var(--md-sys-color-on-surface);font:inherit}.pdx-upload-field-shell .action-btn.primary{background:var(--md-sys-color-primary);border-color:var(--md-sys-color-primary);color:var(--md-sys-color-on-primary)}.pdx-upload-field-shell .action-btn mat-icon{font-size:18px;width:18px;height:18px}.pdx-upload-field-shell .icon-btn{border:0;background:transparent;padding:.25rem;border-radius:6px;cursor:pointer;color:var(--md-sys-color-on-surface-variant)}.pdx-upload-field-shell .icon-btn:hover{background:#ffffff0d}.pdx-upload-field-shell.disabled .icon-btn,.pdx-upload-field-shell.disabled .action-btn{cursor:not-allowed}.field-messages{font-size:.82rem;margin-top:.25rem}.field-messages .hint{opacity:.8}.field-messages .error{color:var(--md-sys-color-error,#b00020)}.pending-text-btn{display:flex;align-items:center;gap:.5rem;background:0;border:0;color:var(--md-sys-color-on-surface);font-family:inherit;font-size:inherit;padding:0;margin:0;cursor:pointer;min-width:0;text-align:left}.pending-text-btn span,.pending-text-btn mat-icon{color:inherit}.pending-files-overlay{display:flex;flex-direction:column;min-width:min(320px,calc(100vw - 32px));max-width:min(480px,calc(100vw - 32px));max-height:240px;overflow:auto;padding:.25rem 0;border:1px solid var(--pfx-surface-border, #ccc);border-radius:12px;background:var(--pfx-surface, var(--md-sys-color-surface));box-shadow:0 12px 30px #0000002e;outline:none}.pending-file-item{display:flex;align-items:center;width:100%;gap:.5rem;padding:8px 16px}.pending-file-item .file-info{flex:1;min-width:0;text-align:left}.pending-file-item .file-name{display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.pending-file-item .file-size{font-size:.8rem;opacity:.7}.pending-file-item .spacer{flex:1 1 auto}.pending-file-item .remove-pending-btn{color:var(--md-sys-color-on-surface-variant)}.pdx-upload-field-shell.proximity-active{border-style:dashed;border-color:var(--md-sys-color-primary);transform:scale(1.02);background:var(--md-sys-color-surface-container-low);transition:all .2s ease-in-out}\n"] }]
|
|
4388
5832
|
}], propDecorators: { config: [{
|
|
4389
5833
|
type: Input
|
|
4390
5834
|
}], valueMode: [{
|
|
4391
5835
|
type: Input
|
|
4392
5836
|
}], baseUrl: [{
|
|
4393
5837
|
type: Input
|
|
5838
|
+
}], uploadSuccess: [{
|
|
5839
|
+
type: Output
|
|
5840
|
+
}], bulkComplete: [{
|
|
5841
|
+
type: Output
|
|
5842
|
+
}], error: [{
|
|
5843
|
+
type: Output
|
|
4394
5844
|
}], uploader: [{
|
|
4395
5845
|
type: ViewChild,
|
|
4396
5846
|
args: ['uploader']
|
|
5847
|
+
}], pendingTrigger: [{
|
|
5848
|
+
type: ViewChild,
|
|
5849
|
+
args: ['pendingTrigger']
|
|
5850
|
+
}], pendingPanel: [{
|
|
5851
|
+
type: ViewChild,
|
|
5852
|
+
args: ['pendingPanel']
|
|
4397
5853
|
}] } });
|
|
4398
5854
|
|
|
4399
|
-
const FILES_UPLOAD_PT_BR = {
|
|
4400
|
-
'praxis.filesUpload.settingsAriaLabel': 'Abrir configurações',
|
|
4401
|
-
'praxis.filesUpload.dropzoneLabel': 'Arraste arquivos ou',
|
|
4402
|
-
'praxis.filesUpload.dropzoneButton': 'selecionar',
|
|
4403
|
-
'praxis.filesUpload.conflictPolicyLabel': 'Política de conflito',
|
|
4404
|
-
'praxis.filesUpload.metadataLabel': 'Metadados (JSON)',
|
|
4405
|
-
'praxis.filesUpload.progressAriaLabel': 'Progresso do upload',
|
|
4406
|
-
'praxis.filesUpload.rateLimitBanner': 'Limite de requisições excedido. Tente novamente às',
|
|
4407
|
-
'praxis.filesUpload.settingsTitle': 'Configuração de Upload de Arquivos',
|
|
4408
|
-
'praxis.filesUpload.statusSuccess': 'Enviado',
|
|
4409
|
-
'praxis.filesUpload.statusError': 'Erro',
|
|
4410
|
-
'praxis.filesUpload.invalidMetadata': 'Metadados inválidos.',
|
|
4411
|
-
'praxis.filesUpload.genericUploadError': 'Erro no envio de arquivo.',
|
|
4412
|
-
'praxis.filesUpload.acceptError': 'Tipo de arquivo não permitido.',
|
|
4413
|
-
'praxis.filesUpload.maxFileSizeError': 'Arquivo excede o tamanho máximo.',
|
|
4414
|
-
'praxis.filesUpload.maxFilesPerBulkError': 'Quantidade de arquivos excedida.',
|
|
4415
|
-
'praxis.filesUpload.maxBulkSizeError': 'Tamanho total dos arquivos excedido.',
|
|
4416
|
-
'praxis.filesUpload.errors.INVALID_FILE_TYPE': 'Tipo de arquivo inválido.',
|
|
4417
|
-
'praxis.filesUpload.errors.FILE_TOO_LARGE': 'Arquivo muito grande.',
|
|
4418
|
-
'praxis.filesUpload.errors.NOT_FOUND': 'Arquivo não encontrado.',
|
|
4419
|
-
'praxis.filesUpload.errors.UNAUTHORIZED': 'Requisição não autorizada.',
|
|
4420
|
-
'praxis.filesUpload.errors.RATE_LIMIT_EXCEEDED': 'Limite de requisições excedido.',
|
|
4421
|
-
'praxis.filesUpload.errors.INTERNAL_ERROR': 'Erro interno do servidor.',
|
|
4422
|
-
'praxis.filesUpload.errors.QUOTA_EXCEEDED': 'Cota excedida.',
|
|
4423
|
-
'praxis.filesUpload.errors.SEC_VIRUS_DETECTED': 'Vírus detectado no arquivo.',
|
|
4424
|
-
'praxis.filesUpload.errors.SEC_MALICIOUS_CONTENT': 'Conteúdo malicioso detectado.',
|
|
4425
|
-
'praxis.filesUpload.errors.SEC_DANGEROUS_TYPE': 'Tipo de arquivo perigoso.',
|
|
4426
|
-
'praxis.filesUpload.errors.FMT_MAGIC_MISMATCH': 'Conteúdo do arquivo incompatível.',
|
|
4427
|
-
'praxis.filesUpload.errors.FMT_CORRUPTED': 'Arquivo corrompido.',
|
|
4428
|
-
'praxis.filesUpload.errors.FMT_UNSUPPORTED': 'Formato de arquivo não suportado.',
|
|
4429
|
-
'praxis.filesUpload.errors.SYS_STORAGE_ERROR': 'Erro de armazenamento.',
|
|
4430
|
-
'praxis.filesUpload.errors.SYS_SERVICE_DOWN': 'Serviço indisponível.',
|
|
4431
|
-
'praxis.filesUpload.errors.SYS_RATE_LIMIT': 'Limite de requisições excedido.',
|
|
4432
|
-
// Aliases PT-BR do backend e códigos adicionais
|
|
4433
|
-
'praxis.filesUpload.errors.ARQUIVO_MUITO_GRANDE': 'Arquivo muito grande.',
|
|
4434
|
-
'praxis.filesUpload.errors.TIPO_ARQUIVO_INVALIDO': 'Tipo de arquivo inválido.',
|
|
4435
|
-
'praxis.filesUpload.errors.TIPO_MIDIA_NAO_SUPORTADO': 'Tipo de mídia não suportado.',
|
|
4436
|
-
'praxis.filesUpload.errors.CAMPO_OBRIGATORIO_AUSENTE': 'Campo obrigatório ausente.',
|
|
4437
|
-
'praxis.filesUpload.errors.OPCOES_JSON_INVALIDAS': 'JSON de opções inválido.',
|
|
4438
|
-
'praxis.filesUpload.errors.ARGUMENTO_INVALIDO': 'Argumento inválido.',
|
|
4439
|
-
'praxis.filesUpload.errors.NAO_AUTORIZADO': 'Autenticação necessária.',
|
|
4440
|
-
'praxis.filesUpload.errors.ERRO_INTERNO': 'Erro interno do servidor.',
|
|
4441
|
-
'praxis.filesUpload.errors.LIMITE_TAXA_EXCEDIDO': 'Limite de taxa excedido.',
|
|
4442
|
-
'praxis.filesUpload.errors.COTA_EXCEDIDA': 'Cota excedida.',
|
|
4443
|
-
'praxis.filesUpload.errors.ARQUIVO_JA_EXISTE': 'Arquivo já existe.',
|
|
4444
|
-
// Outros possíveis códigos mapeados no catálogo
|
|
4445
|
-
'praxis.filesUpload.errors.INVALID_JSON_OPTIONS': 'JSON de opções inválido.',
|
|
4446
|
-
'praxis.filesUpload.errors.EMPTY_FILENAME': 'Nome do arquivo obrigatório.',
|
|
4447
|
-
'praxis.filesUpload.errors.FILE_EXISTS': 'Arquivo já existe.',
|
|
4448
|
-
'praxis.filesUpload.errors.PATH_TRAVERSAL': 'Path traversal detectado.',
|
|
4449
|
-
'praxis.filesUpload.errors.INSUFFICIENT_STORAGE': 'Sem espaço em disco.',
|
|
4450
|
-
'praxis.filesUpload.errors.UPLOAD_TIMEOUT': 'Tempo esgotado no upload.',
|
|
4451
|
-
'praxis.filesUpload.errors.BULK_UPLOAD_TIMEOUT': 'Tempo esgotado no upload em lote.',
|
|
4452
|
-
'praxis.filesUpload.errors.BULK_UPLOAD_CANCELLED': 'Upload em lote cancelado.',
|
|
4453
|
-
'praxis.filesUpload.errors.USER_CANCELLED': 'Upload cancelado pelo usuário.',
|
|
4454
|
-
'praxis.filesUpload.errors.UNKNOWN_ERROR': 'Erro desconhecido.',
|
|
4455
|
-
};
|
|
4456
|
-
|
|
4457
5855
|
const PRAXIS_FILES_UPLOAD_COMPONENT_METADATA = {
|
|
4458
5856
|
id: 'praxis-files-upload',
|
|
4459
5857
|
selector: 'praxis-files-upload',
|
|
@@ -4474,12 +5872,6 @@ const PRAXIS_FILES_UPLOAD_COMPONENT_METADATA = {
|
|
|
4474
5872
|
label: 'Upload ID',
|
|
4475
5873
|
description: 'Identificador logico do upload para persistencia e rastreamento.',
|
|
4476
5874
|
},
|
|
4477
|
-
{
|
|
4478
|
-
name: 'uploadId',
|
|
4479
|
-
type: 'string',
|
|
4480
|
-
label: 'Upload ID (alias)',
|
|
4481
|
-
description: 'Alias funcional de filesUploadId para compatibilidade com hosts.',
|
|
4482
|
-
},
|
|
4483
5875
|
{
|
|
4484
5876
|
name: 'componentInstanceId',
|
|
4485
5877
|
type: 'string',
|
|
@@ -4516,13 +5908,13 @@ const PRAXIS_FILES_UPLOAD_COMPONENT_METADATA = {
|
|
|
4516
5908
|
outputs: [
|
|
4517
5909
|
{
|
|
4518
5910
|
name: 'uploadSuccess',
|
|
4519
|
-
type: '
|
|
5911
|
+
type: 'FileMetadata',
|
|
4520
5912
|
label: 'Upload concluido',
|
|
4521
5913
|
description: 'Disparado quando o upload e finalizado com sucesso.',
|
|
4522
5914
|
},
|
|
4523
5915
|
{
|
|
4524
5916
|
name: 'bulkComplete',
|
|
4525
|
-
type: '
|
|
5917
|
+
type: 'BulkUploadResponseData',
|
|
4526
5918
|
label: 'Upload em lote concluido',
|
|
4527
5919
|
description: 'Disparado quando o upload em lote e finalizado.',
|
|
4528
5920
|
},
|
|
@@ -4550,12 +5942,6 @@ const PRAXIS_FILES_UPLOAD_COMPONENT_METADATA = {
|
|
|
4550
5942
|
label: 'Progresso',
|
|
4551
5943
|
description: 'Disparado conforme o progresso de upload.',
|
|
4552
5944
|
},
|
|
4553
|
-
{
|
|
4554
|
-
name: 'uploadError',
|
|
4555
|
-
type: 'ErrorResponse',
|
|
4556
|
-
label: 'Erro no upload',
|
|
4557
|
-
description: 'Disparado quando ocorre erro durante o upload.',
|
|
4558
|
-
},
|
|
4559
5945
|
{
|
|
4560
5946
|
name: 'retry',
|
|
4561
5947
|
type: 'BulkUploadFileResult',
|
|
@@ -4587,10 +5973,10 @@ const PRAXIS_FILES_UPLOAD_COMPONENT_METADATA = {
|
|
|
4587
5973
|
description: 'Disparado quando os detalhes do item sao fechados.',
|
|
4588
5974
|
},
|
|
4589
5975
|
{
|
|
4590
|
-
name: '
|
|
4591
|
-
type: '
|
|
5976
|
+
name: 'pendingStateChange',
|
|
5977
|
+
type: 'PendingFilesState',
|
|
4592
5978
|
label: 'Arquivos pendentes',
|
|
4593
|
-
description: 'Disparado quando
|
|
5979
|
+
description: 'Disparado quando o estado de selecao pendente muda.',
|
|
4594
5980
|
},
|
|
4595
5981
|
{
|
|
4596
5982
|
name: 'proximityChange',
|
|
@@ -4728,7 +6114,7 @@ const FILES_UPLOAD_AI_CAPABILITIES = {
|
|
|
4728
6114
|
notes: [
|
|
4729
6115
|
'limits.maxFileSizeBytes and limits.maxBulkSizeBytes are bytes (e.g., 10485760 for 10MB).',
|
|
4730
6116
|
'options.allowedExtensions is enforced by backend validation; ui.accept is only a client hint.',
|
|
4731
|
-
'Use
|
|
6117
|
+
'Use options.defaultConflictPolicy to control name collisions on upload.',
|
|
4732
6118
|
],
|
|
4733
6119
|
capabilities: [
|
|
4734
6120
|
{ path: 'strategy', category: 'strategy', valueKind: 'enum', allowedValues: ENUMS.strategy, description: 'Upload strategy.' },
|
|
@@ -4759,11 +6145,10 @@ const FILES_UPLOAD_AI_CAPABILITIES = {
|
|
|
4759
6145
|
{ path: 'limits.maxFileSizeBytes', category: 'limits', valueKind: 'number', description: 'Max file size (bytes).' },
|
|
4760
6146
|
{ path: 'limits.maxFilesPerBulk', category: 'limits', valueKind: 'number', description: 'Max files per bulk upload.' },
|
|
4761
6147
|
{ path: 'limits.maxBulkSizeBytes', category: 'limits', valueKind: 'number', description: 'Max bulk size (bytes).' },
|
|
4762
|
-
{ path: 'limits.defaultConflictPolicy', category: 'limits', valueKind: 'enum', allowedValues: ENUMS.conflictPolicy, description: 'Default conflict policy.' },
|
|
4763
|
-
{ path: 'limits.failFast', category: 'limits', valueKind: 'boolean', description: 'Abort on first error.' },
|
|
4764
|
-
{ path: 'limits.strictValidation', category: 'limits', valueKind: 'boolean', description: 'Strict client validation.' },
|
|
4765
|
-
{ path: 'limits.maxUploadSizeMb', category: 'limits', valueKind: 'number', description: 'Max upload size in MB.' },
|
|
4766
6148
|
{ path: 'options', category: 'options', valueKind: 'object', description: 'Backend options sent as JSON.' },
|
|
6149
|
+
{ path: 'options.defaultConflictPolicy', category: 'options', valueKind: 'enum', allowedValues: ENUMS.conflictPolicy, description: 'Default conflict policy.' },
|
|
6150
|
+
{ path: 'options.strictValidation', category: 'options', valueKind: 'boolean', description: 'Strict backend validation.' },
|
|
6151
|
+
{ path: 'options.maxUploadSizeMb', category: 'options', valueKind: 'number', description: 'Max upload size in MB.' },
|
|
4767
6152
|
{ path: 'options.allowedExtensions', category: 'options', valueKind: 'array', description: 'Allowed file extensions.' },
|
|
4768
6153
|
{ path: 'options.allowedExtensions[]', category: 'options', valueKind: 'string', description: 'Single extension (e.g., pdf).' },
|
|
4769
6154
|
{ path: 'options.acceptMimeTypes', category: 'options', valueKind: 'array', description: 'Allowed mime types.' },
|
|
@@ -4771,6 +6156,7 @@ const FILES_UPLOAD_AI_CAPABILITIES = {
|
|
|
4771
6156
|
{ path: 'options.targetDirectory', category: 'options', valueKind: 'string', description: 'Target directory on backend.' },
|
|
4772
6157
|
{ path: 'options.enableVirusScanning', category: 'options', valueKind: 'boolean', description: 'Enable virus scanning.' },
|
|
4773
6158
|
{ path: 'bulk', category: 'bulk', valueKind: 'object', description: 'Bulk upload tuning.' },
|
|
6159
|
+
{ path: 'bulk.failFast', category: 'bulk', valueKind: 'boolean', description: 'Abort on first error.' },
|
|
4774
6160
|
{ path: 'bulk.parallelUploads', category: 'bulk', valueKind: 'number', description: 'Parallel uploads count.' },
|
|
4775
6161
|
{ path: 'bulk.retryCount', category: 'bulk', valueKind: 'number', description: 'Retry count.' },
|
|
4776
6162
|
{ path: 'bulk.retryBackoffMs', category: 'bulk', valueKind: 'number', description: 'Retry backoff (ms).' },
|
|
@@ -4799,5 +6185,5 @@ const FILES_UPLOAD_AI_CAPABILITIES = {
|
|
|
4799
6185
|
* Generated bundle index. Do not edit.
|
|
4800
6186
|
*/
|
|
4801
6187
|
|
|
4802
|
-
export { BulkUploadResultStatus, ErrorCode, ErrorMapperService, FILES_UPLOAD_AI_CAPABILITIES, FILES_UPLOAD_ERROR_MESSAGES, FILES_UPLOAD_PT_BR, FILES_UPLOAD_TEXTS, FilesApiClient, PRAXIS_FILES_UPLOAD_COMPONENT_METADATA, PdxFilesUploadFieldComponent, PraxisFilesUpload, PraxisFilesUploadConfigEditor, PresignedUploaderService, ScanStatus, TRANSLATE_LIKE, acceptValidator, maxBulkSizeValidator, maxFileSizeValidator, maxFilesPerBulkValidator, providePraxisFilesUploadMetadata };
|
|
6188
|
+
export { BulkUploadResultStatus, ErrorCode, ErrorMapperService, FILES_UPLOAD_AI_CAPABILITIES, FILES_UPLOAD_EN_US, FILES_UPLOAD_ERROR_MESSAGES, FILES_UPLOAD_PT_BR, FILES_UPLOAD_TEXTS, FilesApiClient, PRAXIS_FILES_UPLOAD_COMPONENT_METADATA, PdxFilesUploadFieldComponent, PraxisFilesUpload, PraxisFilesUploadConfigEditor, PresignedUploaderService, ScanStatus, TRANSLATE_LIKE, acceptValidator, createPraxisFilesUploadI18nConfig, getPrimaryErrorItem, isErrorEnvelope, maxBulkSizeValidator, maxFileSizeValidator, maxFilesPerBulkValidator, providePraxisFilesUploadI18n, providePraxisFilesUploadMetadata, resolvePraxisFilesUploadText };
|
|
4803
6189
|
//# sourceMappingURL=praxisui-files-upload.mjs.map
|