@eqproject/eqp-attachments 3.0.4 → 3.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +246 -184
- package/esm2022/lib/eqp-attachments.component.mjs +333 -151
- package/esm2022/lib/eqp-attachments.module.mjs +5 -13
- package/esm2022/lib/interfaces/IAttachment.mjs +1 -1
- package/fesm2022/eqproject-eqp-attachments.mjs +338 -164
- package/fesm2022/eqproject-eqp-attachments.mjs.map +1 -1
- package/lib/eqp-attachments.component.d.ts +62 -22
- package/lib/eqp-attachments.module.d.ts +1 -3
- package/lib/interfaces/IAttachment.d.ts +2 -0
- package/package.json +3 -6
|
@@ -2,12 +2,10 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { Injectable, EventEmitter, ViewChild, Output, Input, Component, NgModule } from '@angular/core';
|
|
3
3
|
import * as i2 from '@angular/forms';
|
|
4
4
|
import { Validators, FormsModule, ReactiveFormsModule } from '@angular/forms';
|
|
5
|
-
import { TooltipPositionType, TypeColumn } from '@eqproject/eqp-common';
|
|
6
5
|
import imageCompression from 'browser-image-compression';
|
|
7
6
|
import * as i14 from 'ngx-image-cropper';
|
|
8
|
-
import { base64ToFile, ImageCropperComponent
|
|
7
|
+
import { base64ToFile, ImageCropperComponent } from 'ngx-image-cropper';
|
|
9
8
|
import Swal from 'sweetalert2';
|
|
10
|
-
import { Observable, from, concatMap } from 'rxjs';
|
|
11
9
|
import * as i1 from '@angular/material/dialog';
|
|
12
10
|
import { MatDialogModule } from '@angular/material/dialog';
|
|
13
11
|
import * as i3 from '@angular/platform-browser';
|
|
@@ -20,18 +18,14 @@ import * as i8 from '@angular/material/form-field';
|
|
|
20
18
|
import { MatFormFieldModule } from '@angular/material/form-field';
|
|
21
19
|
import * as i9 from '@angular/material/menu';
|
|
22
20
|
import { MatMenuModule } from '@angular/material/menu';
|
|
23
|
-
import * as i10 from '@angular/material/
|
|
24
|
-
import {
|
|
21
|
+
import * as i10 from '@angular/material/button-toggle';
|
|
22
|
+
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
25
23
|
import * as i11 from '@angular/material/icon';
|
|
26
24
|
import { MatIconModule } from '@angular/material/icon';
|
|
27
25
|
import * as i12 from '@angular/material/tooltip';
|
|
28
26
|
import { MatTooltipModule } from '@angular/material/tooltip';
|
|
29
27
|
import * as i13 from '@angular/common';
|
|
30
28
|
import { CommonModule } from '@angular/common';
|
|
31
|
-
import * as i15 from '@eqproject/eqp-table';
|
|
32
|
-
import { EqpTableModule } from '@eqproject/eqp-table';
|
|
33
|
-
import * as i16 from 'ngx-file-drop';
|
|
34
|
-
import { NgxFileDropModule } from 'ngx-file-drop';
|
|
35
29
|
import { MatCheckboxModule } from '@angular/material/checkbox';
|
|
36
30
|
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
|
37
31
|
import { MatDatepickerModule } from '@angular/material/datepicker';
|
|
@@ -43,10 +37,10 @@ import { MatSidenavModule } from '@angular/material/sidenav';
|
|
|
43
37
|
import { MatToolbarModule } from '@angular/material/toolbar';
|
|
44
38
|
import { MatListModule } from '@angular/material/list';
|
|
45
39
|
import { MatGridListModule } from '@angular/material/grid-list';
|
|
40
|
+
import { MatCardModule } from '@angular/material/card';
|
|
46
41
|
import { MatStepperModule } from '@angular/material/stepper';
|
|
47
42
|
import { MatTabsModule } from '@angular/material/tabs';
|
|
48
43
|
import { MatExpansionModule } from '@angular/material/expansion';
|
|
49
|
-
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
50
44
|
import { MatChipsModule } from '@angular/material/chips';
|
|
51
45
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
|
52
46
|
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
|
@@ -274,14 +268,14 @@ class EqpAttachmentsComponent {
|
|
|
274
268
|
* Se TRUE mostra il titolo nell'header nel caso in cui "multipleAttachment" è TRUE ("Elenco allegati" di default).
|
|
275
269
|
*/
|
|
276
270
|
showHeader = true;
|
|
277
|
-
/**
|
|
278
|
-
* Titolo da visualizzare se il parametro "showHeader" è TRUE. Di devault viene visualizzato "Elenco allegati".
|
|
279
|
-
*/
|
|
280
|
-
headerTitle = "Elenco allegati";
|
|
281
271
|
/**
|
|
282
272
|
* Sorgente dati da visualizzare. Nel caso si vuole gestire un singolo allegato va passato in ogni caso come Array.
|
|
283
273
|
*/
|
|
284
274
|
attachmentsList = null;
|
|
275
|
+
/**
|
|
276
|
+
* Nel caso si vuole gestire un solo elemento senza passarlo come array, lo passo come singolo allegato e gestisco nella libreria l'array.
|
|
277
|
+
*/
|
|
278
|
+
singleAttachment = null;
|
|
285
279
|
/**
|
|
286
280
|
* Se TRUE non mostra la MatCard (nel caso in cui "multipleAttachment" è TRUE).
|
|
287
281
|
*/
|
|
@@ -296,10 +290,6 @@ class EqpAttachmentsComponent {
|
|
|
296
290
|
* SOLO se si gestiscono allegati multipli, quindi se l'input 'multipleAttachment' assume il valore TRUE, altrimenti è sempre disabilitata.
|
|
297
291
|
*/
|
|
298
292
|
loadMultipleFiles = false;
|
|
299
|
-
/**
|
|
300
|
-
* Configurazione delle colonne della eqp-table per la visualizzazione degli allegati (caso "multipleAttachment" è TRUE).
|
|
301
|
-
*/
|
|
302
|
-
attachmentsColumns = null;
|
|
303
293
|
/**
|
|
304
294
|
* Imposta il messaggio da visualizzare nel caso in cui la tabella degli allegati (nel caso in cui "multipleAttachment" è TRUE) è vuota.
|
|
305
295
|
*/
|
|
@@ -392,10 +382,13 @@ class EqpAttachmentsComponent {
|
|
|
392
382
|
* Classe custom da assegnare al dialog del crop immagini
|
|
393
383
|
*/
|
|
394
384
|
cropDialogClass;
|
|
385
|
+
maxFileSizeMB = 100; // Default max size of 100 MB
|
|
386
|
+
cardSize = 'small'; // Default
|
|
387
|
+
customCardWidthPx = 200; // Larghezza custom in px
|
|
388
|
+
customCardHeightPx = 180; // Altezza custom in px
|
|
395
389
|
/**
|
|
396
390
|
* Input per definire le label da usare nel componente
|
|
397
391
|
*/
|
|
398
|
-
downloadTooltipPosition = TooltipPositionType.Below;
|
|
399
392
|
openLinkLabel = "Apri link";
|
|
400
393
|
addButtonLabel = "Aggiungi";
|
|
401
394
|
downloadLabel = "Download";
|
|
@@ -409,7 +402,6 @@ class EqpAttachmentsComponent {
|
|
|
409
402
|
exitLabel = "Esci";
|
|
410
403
|
uploadWithDropboxLabel = "Carica con Dropbox";
|
|
411
404
|
cropLabel = "Scegli le dimensioni dell'immagine";
|
|
412
|
-
eqpTableSearchText = "Cerca";
|
|
413
405
|
deleteDialogTitle = null;
|
|
414
406
|
deleteDialogMessage = "Sei sicuro di voler cancellare quest'allegato?";
|
|
415
407
|
noImageSelectedErrorMessage = "Non è possibile selezionare un file che non sia un'immagine.";
|
|
@@ -420,6 +412,21 @@ class EqpAttachmentsComponent {
|
|
|
420
412
|
flipVerticalLabel = "Capovolgi verticalmente";
|
|
421
413
|
rotateRightLabel = "Ruota a destra";
|
|
422
414
|
rotateLeftLabel = "Ruota a sinistra";
|
|
415
|
+
uploadTitle = 'Upload file';
|
|
416
|
+
uploadSubtitle = 'Drag & drop files o click';
|
|
417
|
+
dropHereLabel = 'Rilascia i file qui';
|
|
418
|
+
supportedFormatsLabel = 'Formati supportati: JPEG, PNG, PDF (Max 100MB)';
|
|
419
|
+
browseFilesLabel = 'Cerca i file';
|
|
420
|
+
uploadSummaryLabel = 'Lista allegati';
|
|
421
|
+
filesLabel = 'Files';
|
|
422
|
+
totalSizeLabel = 'Dimensione totale';
|
|
423
|
+
emptyStateLabel = 'Non sono presenti file caricati';
|
|
424
|
+
addedSuccessfullyLabel = 'file(s) caricati con successo.';
|
|
425
|
+
removedLabel = 'File rimosso';
|
|
426
|
+
// @Input() removeLabel = 'Rimuovi file';
|
|
427
|
+
chooseView = true;
|
|
428
|
+
showSummary = false;
|
|
429
|
+
viewMode = 'table';
|
|
423
430
|
//#endregion
|
|
424
431
|
//#region @Output del componente
|
|
425
432
|
/**
|
|
@@ -453,6 +460,7 @@ class EqpAttachmentsComponent {
|
|
|
453
460
|
dialogAddMultipleAttachment;
|
|
454
461
|
dialogRefCropImage;
|
|
455
462
|
dialogCropImage;
|
|
463
|
+
addingLinkTemplate;
|
|
456
464
|
//#endregion
|
|
457
465
|
//#region Proprietà per gestione ridimensionamento file di tipo image
|
|
458
466
|
imageChangedEvent = "";
|
|
@@ -468,11 +476,51 @@ class EqpAttachmentsComponent {
|
|
|
468
476
|
originalHeight;
|
|
469
477
|
customWidth;
|
|
470
478
|
customHeight;
|
|
471
|
-
attachmentTable;
|
|
472
479
|
inlinePreviewTemplate;
|
|
473
480
|
dialogPreview;
|
|
474
481
|
imageFile;
|
|
475
482
|
addingLinkMode = false;
|
|
483
|
+
//#region Sezione nuova refactoring
|
|
484
|
+
// Stato drag & drop e toast
|
|
485
|
+
dragOver = false;
|
|
486
|
+
toast = {
|
|
487
|
+
visible: false,
|
|
488
|
+
type: 'success',
|
|
489
|
+
text: '',
|
|
490
|
+
timeoutId: 0
|
|
491
|
+
};
|
|
492
|
+
progressPercent = 0;
|
|
493
|
+
totalSizeBytes = 0;
|
|
494
|
+
get totalSizeFormatted() { return this.formatFileSize(this.totalSizeBytes); }
|
|
495
|
+
// Utility per formattare bytes (2 decimali)
|
|
496
|
+
formatFileSize(bytes) {
|
|
497
|
+
if (!bytes || bytes <= 0)
|
|
498
|
+
return '0 Bytes';
|
|
499
|
+
const k = 1024;
|
|
500
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
501
|
+
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
502
|
+
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
|
|
503
|
+
}
|
|
504
|
+
// Mostra toast con auto‑hide e reset progress bar CSS
|
|
505
|
+
showToast(message, type = 'success', durationMs = 3000) {
|
|
506
|
+
if (this.toast.visible && this.toast.text === message) {
|
|
507
|
+
return;
|
|
508
|
+
}
|
|
509
|
+
// Cancella il timeout precedente se esiste
|
|
510
|
+
if (this.toast.timeoutId) {
|
|
511
|
+
clearTimeout(this.toast.timeoutId);
|
|
512
|
+
}
|
|
513
|
+
// Calcola una durata dinamica
|
|
514
|
+
const duration = durationMs || Math.max(4000, message.length * 100);
|
|
515
|
+
this.toast.text = message;
|
|
516
|
+
this.toast.type = type;
|
|
517
|
+
this.toast.visible = true;
|
|
518
|
+
// Imposta il nuovo timeout per nascondere automaticamente il toast
|
|
519
|
+
this.toast.timeoutId = setTimeout(() => {
|
|
520
|
+
this.toast.visible = false;
|
|
521
|
+
}, duration);
|
|
522
|
+
}
|
|
523
|
+
#endregion;
|
|
476
524
|
constructor(dialog, formBuilder, sanitizer, http, cd, eqpAttachmentService) {
|
|
477
525
|
this.dialog = dialog;
|
|
478
526
|
this.formBuilder = formBuilder;
|
|
@@ -481,7 +529,9 @@ class EqpAttachmentsComponent {
|
|
|
481
529
|
this.cd = cd;
|
|
482
530
|
this.eqpAttachmentService = eqpAttachmentService;
|
|
483
531
|
}
|
|
484
|
-
|
|
532
|
+
ngOnInit() {
|
|
533
|
+
// Inizializza metriche e progress in base allo stato iniziale
|
|
534
|
+
this.recomputeTotalsAndProgress();
|
|
485
535
|
//Se è stata richiesta la gestione delle sole immagini allora imposta il filtro per le estensioni possibili da caricare
|
|
486
536
|
if (!this.acceptedFileTypes)
|
|
487
537
|
if (this.allowOnlyImages == true)
|
|
@@ -495,20 +545,48 @@ class EqpAttachmentsComponent {
|
|
|
495
545
|
EqpAttachmentDialogService.Warning('Almeno uno degli AttachmentType selezionati nel parametro "allowedTypes" non esiste.');
|
|
496
546
|
this.allowedTypes = [AttachmentType.FILE, AttachmentType.LINK, AttachmentType.DROPBOX];
|
|
497
547
|
}
|
|
498
|
-
//Se è stata richiesta la gestione multipla degli allegati allora configura l'eqp-table
|
|
499
|
-
if (this.multipleAttachment == true && (!this.attachmentsColumns || this.attachmentsColumns.length == 0)) {
|
|
500
|
-
this.configureColumns();
|
|
501
|
-
}
|
|
502
548
|
if (this.attachmentsList == null)
|
|
503
549
|
this.attachmentsList = new Array();
|
|
550
|
+
// Se è stato passato un singolo allegato lo aggiungo alla lista
|
|
551
|
+
if (this.singleAttachment != null && this.attachmentsList.length == 0) {
|
|
552
|
+
this.attachmentsList.push(this.singleAttachment);
|
|
553
|
+
}
|
|
504
554
|
this.checkAttachmentImage();
|
|
505
555
|
if (this.allowedTypes.includes(3)) {
|
|
506
556
|
this.eqpAttachmentService.loadDropboxScript();
|
|
507
557
|
}
|
|
508
558
|
}
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
559
|
+
setViewMode(mode) {
|
|
560
|
+
this.viewMode = mode;
|
|
561
|
+
}
|
|
562
|
+
// Ricalcola la somma pesata sugli allegati presenti
|
|
563
|
+
recomputeTotalsAndProgress() {
|
|
564
|
+
this.totalSizeBytes = (this.attachmentsList || [])
|
|
565
|
+
.filter(a => !!a)
|
|
566
|
+
.reduce((sum, a) => sum + this.bytesFromBase64(a.FileDataBase64 || a.FileThumbnailBase64 || ''), 0);
|
|
567
|
+
// Progress fittizio: 100% se presenti file, 0 se vuoto
|
|
568
|
+
this.progressPercent = (this.attachmentsList && this.attachmentsList.length > 0) ? 100 : 0;
|
|
569
|
+
}
|
|
570
|
+
ngOnDestroy() {
|
|
571
|
+
if (this.toast.timeoutId)
|
|
572
|
+
clearTimeout(this.toast.timeoutId);
|
|
573
|
+
}
|
|
574
|
+
// Calcola i byte da una stringa Base64 pura (senza prefisso data:...)
|
|
575
|
+
bytesFromBase64(base64) {
|
|
576
|
+
if (!base64)
|
|
577
|
+
return 0;
|
|
578
|
+
// Rimuovi eventuale prefisso data URL
|
|
579
|
+
const commaIdx = base64.indexOf(',');
|
|
580
|
+
const b64 = commaIdx >= 0 ? base64.substring(commaIdx + 1) : base64;
|
|
581
|
+
// Conta padding
|
|
582
|
+
let padding = 0;
|
|
583
|
+
if (b64.endsWith('=='))
|
|
584
|
+
padding = 2;
|
|
585
|
+
else if (b64.endsWith('='))
|
|
586
|
+
padding = 1;
|
|
587
|
+
// Formula esatta: (3 * (len / 4)) - padding
|
|
588
|
+
const len = b64.length;
|
|
589
|
+
return Math.max(0, (3 * Math.floor(len / 4)) - padding);
|
|
512
590
|
}
|
|
513
591
|
checkAttachmentImage() {
|
|
514
592
|
this.attachmentsList.forEach((a) => {
|
|
@@ -516,67 +594,6 @@ class EqpAttachmentsComponent {
|
|
|
516
594
|
});
|
|
517
595
|
}
|
|
518
596
|
//#region Gestione elenco allegati
|
|
519
|
-
/**
|
|
520
|
-
* Configura le colonne per l'eqp-table nel caso in cui il parametro "multipleAttachments" è TRUE.
|
|
521
|
-
*/
|
|
522
|
-
configureColumns() {
|
|
523
|
-
this.attachmentsColumns = [];
|
|
524
|
-
if (this.disableAction != true) {
|
|
525
|
-
this.attachmentsColumns.push({
|
|
526
|
-
key: "action",
|
|
527
|
-
display: "",
|
|
528
|
-
type: TypeColumn.MenuAction,
|
|
529
|
-
buttonMenuIcon: "more_vert",
|
|
530
|
-
styles: { flex: "0 0 6%" },
|
|
531
|
-
actions: [
|
|
532
|
-
{ name: this.deleteLabel, icon: "delete", fn: (element, index, col) => this.deleteAttachment(element) }
|
|
533
|
-
]
|
|
534
|
-
});
|
|
535
|
-
}
|
|
536
|
-
let downloadColumn = {
|
|
537
|
-
key: "attachment",
|
|
538
|
-
display: "",
|
|
539
|
-
type: TypeColumn.SimpleAction,
|
|
540
|
-
styles: { flex: "0 0 6%" },
|
|
541
|
-
actions: [
|
|
542
|
-
{
|
|
543
|
-
name: "",
|
|
544
|
-
fontawesome: true,
|
|
545
|
-
icon: (element) => {
|
|
546
|
-
return this.showInlinePreview
|
|
547
|
-
? element.AttachmentType == AttachmentType.FILE
|
|
548
|
-
? "fas fa-cloud-download-alt"
|
|
549
|
-
: "fas fa-external-link-alt"
|
|
550
|
-
: this.getAttachmentIcon(element);
|
|
551
|
-
},
|
|
552
|
-
fn: (element, col, elementIndex) => this.viewAttachment(element),
|
|
553
|
-
tooltip: {
|
|
554
|
-
tooltipText: (element) => {
|
|
555
|
-
return element.AttachmentType == AttachmentType.FILE ? this.downloadLabel : this.openLinkLabel;
|
|
556
|
-
},
|
|
557
|
-
tooltipPosition: this.downloadTooltipPosition
|
|
558
|
-
}
|
|
559
|
-
}
|
|
560
|
-
]
|
|
561
|
-
};
|
|
562
|
-
let inlinePreviewColumn = {
|
|
563
|
-
key: "InlinePreview",
|
|
564
|
-
display: this.previewLabel,
|
|
565
|
-
type: TypeColumn.ExternalTemplate,
|
|
566
|
-
externalTemplate: this.inlinePreviewTemplate,
|
|
567
|
-
styles: { flex: "0 0 10%" }
|
|
568
|
-
};
|
|
569
|
-
let fileNameColumn = { key: "FileName", display: this.fileNameLabel };
|
|
570
|
-
if (this.showInlinePreview) {
|
|
571
|
-
this.attachmentsColumns.push(inlinePreviewColumn);
|
|
572
|
-
this.attachmentsColumns.push(fileNameColumn);
|
|
573
|
-
this.attachmentsColumns.push(downloadColumn);
|
|
574
|
-
}
|
|
575
|
-
else {
|
|
576
|
-
this.attachmentsColumns.push(downloadColumn);
|
|
577
|
-
this.attachmentsColumns.push(fileNameColumn);
|
|
578
|
-
}
|
|
579
|
-
}
|
|
580
597
|
/**
|
|
581
598
|
* Elimina un allegato eliminando anche il file presente nello storage di archiviazione utilizzato (AWS o cartella progetto)
|
|
582
599
|
* @param element IAttachmentDTO da cancellare
|
|
@@ -593,9 +610,21 @@ class EqpAttachmentsComponent {
|
|
|
593
610
|
removeAttachmentFromList(attachmentIndex) {
|
|
594
611
|
this.onDeleteAttachment.emit(this.attachmentsList[attachmentIndex]);
|
|
595
612
|
this.attachmentsList.splice(attachmentIndex, 1);
|
|
596
|
-
if (this.attachmentTable)
|
|
597
|
-
this.attachmentTable.reloadDatatable();
|
|
598
613
|
this.localEditedAttachments.emit(this.attachmentsList);
|
|
614
|
+
this.recomputeTotalsAndProgress();
|
|
615
|
+
this.showToast(this.removedLabel || 'File removed', 'success');
|
|
616
|
+
}
|
|
617
|
+
simulateProgress() {
|
|
618
|
+
this.progressPercent = 0;
|
|
619
|
+
setTimeout(() => {
|
|
620
|
+
this.progressPercent = 30;
|
|
621
|
+
setTimeout(() => {
|
|
622
|
+
this.progressPercent = 60;
|
|
623
|
+
setTimeout(() => {
|
|
624
|
+
this.progressPercent = 100;
|
|
625
|
+
}, 200);
|
|
626
|
+
}, 200);
|
|
627
|
+
}, 100);
|
|
599
628
|
}
|
|
600
629
|
/**
|
|
601
630
|
* Scarica l'allegato o apre il link
|
|
@@ -684,7 +713,7 @@ class EqpAttachmentsComponent {
|
|
|
684
713
|
}
|
|
685
714
|
}
|
|
686
715
|
confirmAddAttachment() {
|
|
687
|
-
if (this.newAttachment.IsImage) {
|
|
716
|
+
if (this.newAttachment.IsImage && this.imageCropper) {
|
|
688
717
|
this.newAttachment.FileDataBase64 = this.imageCropper.crop().base64.split(";base64,")[1];
|
|
689
718
|
}
|
|
690
719
|
if (this.loadMultipleFiles != true) {
|
|
@@ -701,13 +730,17 @@ class EqpAttachmentsComponent {
|
|
|
701
730
|
this.attachmentsList = new Array();
|
|
702
731
|
this.attachmentsList = this.attachmentsList.concat(this.newMultipleAttachments);
|
|
703
732
|
}
|
|
704
|
-
if (this.attachmentTable)
|
|
705
|
-
this.attachmentTable.reloadDatatable();
|
|
733
|
+
// if (this.attachmentTable) this.attachmentTable.reloadDatatable();
|
|
706
734
|
this.localEditedAttachments.emit(this.attachmentsList);
|
|
707
735
|
if (this.newAttachment.IsImage) {
|
|
708
736
|
this.dialogRefCropImage.close();
|
|
709
737
|
this.restoreOriginalDimensions();
|
|
710
738
|
}
|
|
739
|
+
if (this.newAttachment.AttachmentType === AttachmentType.LINK) {
|
|
740
|
+
this.newAttachment = {};
|
|
741
|
+
if (this.newAttachmentForm)
|
|
742
|
+
this.newAttachmentForm.reset();
|
|
743
|
+
}
|
|
711
744
|
}
|
|
712
745
|
/**
|
|
713
746
|
* Apre il dialog per l'anteprima dell'allegato selezionato.
|
|
@@ -744,6 +777,9 @@ class EqpAttachmentsComponent {
|
|
|
744
777
|
EqpAttachmentDialogService.Info("Impossibile aprire l'anteprima dell'allegato, file mancante.");
|
|
745
778
|
return;
|
|
746
779
|
}
|
|
780
|
+
else if (this.selectedAttachment.FileContentType === 'application/pdf' && this.selectedAttachment.FileDataBase64) {
|
|
781
|
+
this.selectedAttachment.TrustedUrl = this.sanitizer.bypassSecurityTrustResourceUrl(`data:application/pdf;base64,${this.selectedAttachment.FileDataBase64}`);
|
|
782
|
+
}
|
|
747
783
|
else if (!this.selectedAttachment.IsImage) {
|
|
748
784
|
if (this.selectedAttachment.FilePath && this.productionBaseUrl) {
|
|
749
785
|
this.selectedAttachment.TrustedUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://docs.google.com/gview?url=" +
|
|
@@ -775,13 +811,22 @@ class EqpAttachmentsComponent {
|
|
|
775
811
|
* immagine per il croppie (in caso di caricamento multiplo le funzionalità del croppie sono disabilitate).
|
|
776
812
|
*/
|
|
777
813
|
async onFileAdded(event, isFileDropped = false) {
|
|
814
|
+
if (this.isDisabled) {
|
|
815
|
+
EqpAttachmentDialogService.Info("Caricamento allegati non disponibile.");
|
|
816
|
+
event.preventDefault();
|
|
817
|
+
return;
|
|
818
|
+
}
|
|
778
819
|
this.showCropImage = false;
|
|
779
|
-
let filesOnInput = isFileDropped ? event : event.target.files;
|
|
820
|
+
// let filesOnInput = isFileDropped ? event : event.target.files;
|
|
821
|
+
const filesOnInput = isFileDropped
|
|
822
|
+
? event
|
|
823
|
+
: Array.from(event.target?.files || []);
|
|
824
|
+
const { validFiles, oversizedFiles } = this.validationFile(filesOnInput, isFileDropped);
|
|
780
825
|
//Se è stato richiesto il caricamento SINGOLO oppure se il caricamento è MULTIPLO ma è stato selezionato un solo file
|
|
781
826
|
//allora verifica se il file è un immagine (per mostrare il CROPPIE)
|
|
782
|
-
if ([...
|
|
783
|
-
this.selectedFile =
|
|
784
|
-
this.selectedFiles =
|
|
827
|
+
if ([...validFiles].length == 1 || this.loadMultipleFiles != true) {
|
|
828
|
+
this.selectedFile = validFiles[0];
|
|
829
|
+
this.selectedFiles = validFiles;
|
|
785
830
|
if (!this.selectedFile)
|
|
786
831
|
return;
|
|
787
832
|
//Memorizza i dati per l'allegato
|
|
@@ -796,7 +841,7 @@ class EqpAttachmentsComponent {
|
|
|
796
841
|
//Verifica se il file caricato è un'immagine oppure no. Se è un immagine, prima di caricarla mostra il croppie per il resize.
|
|
797
842
|
//Se non è un immagine allora genera il Base64
|
|
798
843
|
if (this.newAttachment.IsImage == true) {
|
|
799
|
-
this.getImageDimensions(
|
|
844
|
+
this.getImageDimensions(validFiles[0]);
|
|
800
845
|
//Mostra il croppie e disabilita la form finchè non termina la modifica dell'immagine
|
|
801
846
|
this.newAttachmentForm.disable();
|
|
802
847
|
this.newAttachmentForm.controls["customWidth"].enable();
|
|
@@ -808,7 +853,8 @@ class EqpAttachmentsComponent {
|
|
|
808
853
|
hasBackdrop: true,
|
|
809
854
|
width: "60%",
|
|
810
855
|
maxHeight: "80%",
|
|
811
|
-
maxWidth: "70vh"
|
|
856
|
+
maxWidth: "70vh",
|
|
857
|
+
panelClass: 'eqp-attachments-dialog'
|
|
812
858
|
});
|
|
813
859
|
}
|
|
814
860
|
else {
|
|
@@ -820,10 +866,11 @@ class EqpAttachmentsComponent {
|
|
|
820
866
|
}
|
|
821
867
|
}
|
|
822
868
|
else {
|
|
823
|
-
this.selectedFiles =
|
|
869
|
+
this.selectedFiles = validFiles;
|
|
824
870
|
if (!this.selectedFiles || this.selectedFiles.length == 0)
|
|
825
871
|
return;
|
|
826
|
-
this.newMultipleAttachments
|
|
872
|
+
if (!this.newMultipleAttachments)
|
|
873
|
+
this.newMultipleAttachments = [];
|
|
827
874
|
for (let i = 0; i < this.selectedFiles.length; i++) {
|
|
828
875
|
let newAttachment = await this.createAttachmentFromUploadedFile(this.selectedFiles[i], true, true);
|
|
829
876
|
//Se è stata richiesta la gestione delle sole immagini ma per errore è stato selezionato un file che non è un immagine
|
|
@@ -834,10 +881,50 @@ class EqpAttachmentsComponent {
|
|
|
834
881
|
}
|
|
835
882
|
this.confirmAddAttachment();
|
|
836
883
|
}
|
|
884
|
+
this.recomputeTotalsAndProgress();
|
|
885
|
+
this.simulateProgress();
|
|
886
|
+
// Caso 1: Successo Parziale (alcuni file validi, altri no)
|
|
887
|
+
if (oversizedFiles.length > 0 && validFiles.length > 0) {
|
|
888
|
+
const fileNames = oversizedFiles.join(', ');
|
|
889
|
+
this.showToast(`${validFiles.length} file aggiunti. ${oversizedFiles.length} non caricati perché troppo grandi (${fileNames}).`, 'error');
|
|
890
|
+
}
|
|
891
|
+
// Caso 2: Nessun file valido
|
|
892
|
+
else if (oversizedFiles.length > 0 && validFiles.length === 0) {
|
|
893
|
+
}
|
|
894
|
+
// Caso 3: Tutti i file validi
|
|
895
|
+
else if (oversizedFiles.length === 0 && validFiles.length > 0) {
|
|
896
|
+
this.showToast(`${validFiles.length} ${this.addedSuccessfullyLabel || 'file(s) aggiunti con successo.'}`, 'success');
|
|
897
|
+
}
|
|
837
898
|
//Resetto il valore del file input in modo da scatenare il change anche se si dovesse caricare lo stesso file
|
|
838
899
|
if (!isFileDropped)
|
|
839
900
|
event.target.value = "";
|
|
840
901
|
}
|
|
902
|
+
validationFile(filesOnInput, isFileDropped) {
|
|
903
|
+
const validFiles = [];
|
|
904
|
+
const oversizedFiles = [];
|
|
905
|
+
for (const file of filesOnInput) {
|
|
906
|
+
const fileSizeMB = file.size / 1024 / 1024;
|
|
907
|
+
if (fileSizeMB > this.maxFileSizeMB) {
|
|
908
|
+
oversizedFiles.push(file.name);
|
|
909
|
+
}
|
|
910
|
+
else {
|
|
911
|
+
validFiles.push(file);
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
// Show a single error message for all oversized files
|
|
915
|
+
if (oversizedFiles.length > 0) {
|
|
916
|
+
const fileNames = oversizedFiles.join(', ');
|
|
917
|
+
this.showToast(`File(s) troppo grandi: ${fileNames}. Limite: ${this.maxFileSizeMB}MB`, 'error');
|
|
918
|
+
}
|
|
919
|
+
// Abort if no files are valid
|
|
920
|
+
if (validFiles.length === 0) {
|
|
921
|
+
if (!isFileDropped) {
|
|
922
|
+
event.target.value = '';
|
|
923
|
+
}
|
|
924
|
+
return;
|
|
925
|
+
}
|
|
926
|
+
return { validFiles, oversizedFiles };
|
|
927
|
+
}
|
|
841
928
|
/**
|
|
842
929
|
* A partire dal FILE ricevuto in input ricostruisce l'oggetto IAttachmentDTO e lo restituisce.
|
|
843
930
|
* Se il parametro getBase64 viene passato a TRUE allora, sempre a partire dal file,genera il base64 e
|
|
@@ -1055,35 +1142,14 @@ class EqpAttachmentsComponent {
|
|
|
1055
1142
|
this.restoreOriginalDimensions();
|
|
1056
1143
|
}
|
|
1057
1144
|
//#endregion
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
const
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
const fileEntry = files[i].fileEntry;
|
|
1066
|
-
const observable = new Observable((observer) => {
|
|
1067
|
-
fileEntry.file((file) => {
|
|
1068
|
-
filesDropped.push(file);
|
|
1069
|
-
observer.next(file);
|
|
1070
|
-
observer.complete();
|
|
1071
|
-
});
|
|
1072
|
-
});
|
|
1073
|
-
observables.push(observable);
|
|
1074
|
-
}
|
|
1075
|
-
else {
|
|
1076
|
-
const fileEntry = files[i].fileEntry;
|
|
1077
|
-
console.log(files[i].relativePath, fileEntry);
|
|
1078
|
-
}
|
|
1145
|
+
fileDropped(event) {
|
|
1146
|
+
event.preventDefault();
|
|
1147
|
+
event.stopPropagation();
|
|
1148
|
+
this.dragOver = false;
|
|
1149
|
+
const files = event.dataTransfer?.files;
|
|
1150
|
+
if (files && files.length > 0) {
|
|
1151
|
+
this.onFileAdded({ target: { files: files } });
|
|
1079
1152
|
}
|
|
1080
|
-
from(observables)
|
|
1081
|
-
.pipe(concatMap((observable) => observable))
|
|
1082
|
-
.subscribe({
|
|
1083
|
-
complete: () => {
|
|
1084
|
-
this.onFileAdded(filesDropped, true);
|
|
1085
|
-
}
|
|
1086
|
-
});
|
|
1087
1153
|
}
|
|
1088
1154
|
// Se il caricamento del file dropbox va a buon fine, la funzione di callback restituisce un array di oggetti.
|
|
1089
1155
|
// Viene poi fatta una XMLHttpRequest con responseType 'blob' per convertire il primo elemento della response in un Blob.
|
|
@@ -1118,33 +1184,119 @@ class EqpAttachmentsComponent {
|
|
|
1118
1184
|
fileInput.click();
|
|
1119
1185
|
}
|
|
1120
1186
|
// Metodo per visualizzare la form di aggiunta di un link
|
|
1187
|
+
// switchToAddingLinkMode() {
|
|
1188
|
+
// this.addingLinkMode = true;
|
|
1189
|
+
// this.newAttachment = {} as IAttachmentDTO;
|
|
1190
|
+
// this.newAttachment.IsImage = false;
|
|
1191
|
+
// this.newAttachment.AttachmentType = this.attachmentType.LINK;
|
|
1192
|
+
// this.newMultipleAttachments = new Array<IAttachmentDTO>();
|
|
1193
|
+
// this.newMultipleAttachments.push(this.newAttachment);
|
|
1194
|
+
// this.createAttachmentForm();
|
|
1195
|
+
// }
|
|
1196
|
+
/**
|
|
1197
|
+
* Apre il dialogo per l'inserimento del link.
|
|
1198
|
+
*/
|
|
1121
1199
|
switchToAddingLinkMode() {
|
|
1122
|
-
|
|
1123
|
-
this.
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
this.
|
|
1200
|
+
// 1. Crea il form per il dialogo
|
|
1201
|
+
this.newAttachmentForm = this.formBuilder.group({
|
|
1202
|
+
fileName: [''],
|
|
1203
|
+
filePath: ['', [Validators.required, Validators.pattern('https?://.+')]]
|
|
1204
|
+
});
|
|
1205
|
+
// 2. Apri il dialogo
|
|
1206
|
+
const dialogRef = this.dialog.open(this.addingLinkTemplate, {
|
|
1207
|
+
width: '500px',
|
|
1208
|
+
panelClass: 'eqp-attachments-dialog'
|
|
1209
|
+
});
|
|
1210
|
+
// 3. Gestisci il risultato alla chiusura
|
|
1211
|
+
dialogRef.afterClosed().subscribe(result => {
|
|
1212
|
+
if (result) {
|
|
1213
|
+
// A. Crea l'oggetto per il nuovo link dai dati del form
|
|
1214
|
+
const newLinkObject = {
|
|
1215
|
+
ID: 0,
|
|
1216
|
+
AttachmentType: AttachmentType.LINK,
|
|
1217
|
+
FileName: result.fileName,
|
|
1218
|
+
FilePath: result.filePath,
|
|
1219
|
+
IsImage: false
|
|
1220
|
+
};
|
|
1221
|
+
// B. Prepara l'oggetto `newAttachment` che `confirmAddAttachment` si aspetta
|
|
1222
|
+
this.newAttachment = newLinkObject;
|
|
1223
|
+
this.newMultipleAttachments = [newLinkObject];
|
|
1224
|
+
// C. Chiama la tua funzione di conferma centrale!
|
|
1225
|
+
this.confirmAddAttachment();
|
|
1226
|
+
}
|
|
1227
|
+
});
|
|
1228
|
+
}
|
|
1229
|
+
// Metodo per ottenere le classi CSS dinamiche
|
|
1230
|
+
getCardClass(att) {
|
|
1231
|
+
return {
|
|
1232
|
+
'file-preview': true,
|
|
1233
|
+
'uploading': !!att.isUploading,
|
|
1234
|
+
'card-small': this.cardSize === 'small',
|
|
1235
|
+
'card-medium': this.cardSize === 'medium',
|
|
1236
|
+
'card-large': this.cardSize === 'large',
|
|
1237
|
+
// Nessuna classe specifica per 'custom', useremo style.cssText
|
|
1238
|
+
};
|
|
1239
|
+
}
|
|
1240
|
+
// Questa funzione ora si applica al contenitore, non alla singola card.
|
|
1241
|
+
getPreviewsContainerStyle() {
|
|
1242
|
+
let minWidth = '200px'; // Dimensione di default per 'medium'
|
|
1243
|
+
if (this.cardSize === 'small') {
|
|
1244
|
+
minWidth = '140px';
|
|
1245
|
+
}
|
|
1246
|
+
else if (this.cardSize === 'large') {
|
|
1247
|
+
minWidth = '280px';
|
|
1248
|
+
}
|
|
1249
|
+
else if (this.cardSize === 'custom') {
|
|
1250
|
+
minWidth = `${this.customCardWidthPx}px`;
|
|
1251
|
+
}
|
|
1252
|
+
return { '--card-min-width': minWidth };
|
|
1253
|
+
}
|
|
1254
|
+
handlePrimaryAction(attachment) {
|
|
1255
|
+
// Se il file può essere visualizzato in anteprima, apri il dialog
|
|
1256
|
+
if (this.canBePreviewed(attachment)) {
|
|
1257
|
+
this.openPreviewDialog(attachment);
|
|
1258
|
+
}
|
|
1259
|
+
// Altrimenti, esegui l'azione di default (download per file, apri per link)
|
|
1260
|
+
else {
|
|
1261
|
+
this.viewAttachment(attachment);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
/**
|
|
1265
|
+
* Determines if an attachment can be previewed in the dialog.
|
|
1266
|
+
* @param att The attachment to check.
|
|
1267
|
+
* @returns `true` if a preview is available, otherwise `false`.
|
|
1268
|
+
*/
|
|
1269
|
+
canBePreviewed(att) {
|
|
1270
|
+
if (!att) {
|
|
1271
|
+
return false;
|
|
1272
|
+
}
|
|
1273
|
+
// Case 1: It's an image with Base64 data.
|
|
1274
|
+
const isPreviewableImage = att.IsImage && !!att.FileDataBase64;
|
|
1275
|
+
// Case 2: It's a PDF with Base64 data.
|
|
1276
|
+
const isPreviewablePdf = att.FileContentType === 'application/pdf' && !!att.FileDataBase64;
|
|
1277
|
+
// Case 3: It's a remote document with a URL (for Google Viewer).
|
|
1278
|
+
const isRemoteDocument = !att.IsImage && !!att.FilePath && !!this.productionBaseUrl;
|
|
1279
|
+
// A preview is possible if any of these conditions are true.
|
|
1280
|
+
return isPreviewableImage || isPreviewablePdf || isRemoteDocument;
|
|
1129
1281
|
}
|
|
1130
1282
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EqpAttachmentsComponent, deps: [{ token: i1.MatDialog }, { token: i2.FormBuilder }, { token: i3.DomSanitizer }, { token: i4.HttpClient }, { token: i0.ChangeDetectorRef }, { token: EqpAttachmentService }], target: i0.ɵɵFactoryTarget.Component });
|
|
1131
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "17.3.12", type: EqpAttachmentsComponent, selector: "eqp-attachments", inputs: { disableAction: "disableAction", showHeader: "showHeader", headerTitle: "headerTitle", attachmentsList: "attachmentsList", showMatCard: "showMatCard", multipleAttachment: "multipleAttachment", loadMultipleFiles: "loadMultipleFiles", attachmentsColumns: "attachmentsColumns", emptyTableMessage: "emptyTableMessage", allowOnlyImages: "allowOnlyImages", acceptedFileTypes: "acceptedFileTypes", isDisabled: "isDisabled", showInlinePreview: "showInlinePreview", getAttachmentEndpoint: "getAttachmentEndpoint", productionBaseUrl: "productionBaseUrl", compressionOptions: "compressionOptions", allowedTypes: "allowedTypes", isEqpTableMultiLanguage: "isEqpTableMultiLanguage", tablePaginatorVisible: "tablePaginatorVisible", isTableSearcheable: "isTableSearcheable", tablePaginatorSize: "tablePaginatorSize", separatedUploadButtons: "separatedUploadButtons", showPreview: "showPreview", singleAttachmentDragAndDrop: "singleAttachmentDragAndDrop", cropOptions: "cropOptions", cropDialogClass: "cropDialogClass", downloadTooltipPosition: "downloadTooltipPosition", openLinkLabel: "openLinkLabel", addButtonLabel: "addButtonLabel", downloadLabel: "downloadLabel", deleteLabel: "deleteLabel", fileNameLabel: "fileNameLabel", previewLabel: "previewLabel", uploadFileLabel: "uploadFileLabel", confirmLabel: "confirmLabel", abortLabel: "abortLabel", saveLabel: "saveLabel", exitLabel: "exitLabel", uploadWithDropboxLabel: "uploadWithDropboxLabel", cropLabel: "cropLabel", eqpTableSearchText: "eqpTableSearchText", deleteDialogTitle: "deleteDialogTitle", deleteDialogMessage: "deleteDialogMessage", noImageSelectedErrorMessage: "noImageSelectedErrorMessage", wrongTypeSelectedErrorMessage: "wrongTypeSelectedErrorMessage", videoPreviewErrorMessage: "videoPreviewErrorMessage", audioPreviewErrorMessage: ["videoPreviewErrorMessage", "audioPreviewErrorMessage"], flipHorinzontalLabel: "flipHorinzontalLabel", flipVerticalLabel: "flipVerticalLabel", rotateRightLabel: "rotateRightLabel", rotateLeftLabel: "rotateLeftLabel" }, outputs: { localEditedAttachments: "localEditedAttachments", abortAddAttachment: "abortAddAttachment", downloadAttachment: "downloadAttachment", onDeleteAttachment: "onDeleteAttachment" }, viewQueries: [{ propertyName: "dialogAddAttachment", first: true, predicate: ["dialogAddAttachment"], descendants: true, static: true }, { propertyName: "dialogAddMultipleAttachment", first: true, predicate: ["dialogAddMultipleAttachment"], descendants: true, static: true }, { propertyName: "dialogCropImage", first: true, predicate: ["dialogCropImage"], descendants: true, static: true }, { propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }, { propertyName: "imageInput", first: true, predicate: ["imageInput"], descendants: true }, { propertyName: "attachmentTable", first: true, predicate: ["attachmentTable"], descendants: true }, { propertyName: "inlinePreviewTemplate", first: true, predicate: ["inlinePreviewTemplate"], descendants: true, static: true }, { propertyName: "dialogPreview", first: true, predicate: ["dialogPreview"], descendants: true, static: true }], ngImport: i0, template: "<!-- Se richiesta la gestione singola mostra il pulsante di caricamento di un singolo file -->\r\n<div *ngIf=\"multipleAttachment != true\">\r\n <div *ngIf=\"!singleAttachmentDragAndDrop\">\r\n <!-- Template del button per l'aggiunta di un allegato -->\r\n <div *ngIf=\"!addingLinkMode\" class=\"text-center\">\r\n <ng-container *ngTemplateOutlet=\"addAttachmentButton\"></ng-container>\r\n </div>\r\n <!-- Template della form per l'aggiunta di un link -->\r\n <div *ngIf=\"addingLinkMode\" class=\"text-center\">\r\n <ng-container *ngTemplateOutlet=\"addingLinkTemplate\"></ng-container>\r\n </div>\r\n </div>\r\n <div *ngIf=\"singleAttachmentDragAndDrop\">\r\n <input\r\n #fileInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <ngx-file-drop\r\n (onFileDrop)=\"fileDropped($event)\"\r\n (click)=\"onSelectFile($event, fileInput)\"\r\n *ngIf=\"\r\n allowedTypes &&\r\n allowedTypes.includes(1) &&\r\n (!attachmentsList || attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \">\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"!addingLinkMode\">\r\n <i class=\"fa-solid fa-cloud-upload-alt fa-3x mt-3\"></i>\r\n Trascina i file qui\r\n <div class=\"btn-group mt-1\" role=\"group\">\r\n <button type=\"button\" class=\"btn btn-light border-end\" (click)=\"fileInput.click()\">Scegli un file</button>\r\n <div class=\"btn-group\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-light border-start dropdown-toggle\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\"></button>\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n mat-menu-item\r\n (click)=\"fileInput.click()\"\r\n class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n mat-menu-item\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n mat-menu-item\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n </div>\r\n </div>\r\n </ng-template>\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"addingLinkMode\">\r\n <ng-container *ngTemplateOutlet=\"addingLinkTemplate\"></ng-container>\r\n </ng-template>\r\n </ngx-file-drop>\r\n </div>\r\n <div class=\"text-center\">\r\n <button\r\n class=\"mb-2 me-2 eqp-attachments-download-btn\"\r\n (click)=\"viewAttachment(attachmentsList[0])\"\r\n type=\"button\"\r\n mat-raised-button\r\n *ngIf=\"attachmentsList && attachmentsList.length > 0 && attachmentsList[0]\"\r\n color=\"primary\">\r\n <mat-icon *ngIf=\"attachmentsList[0].AttachmentType == AttachmentType.FILE\">download</mat-icon>\r\n <mat-icon *ngIf=\"attachmentsList[0].AttachmentType != AttachmentType.FILE\">open_in_new</mat-icon>\r\n {{ attachmentsList[0].AttachmentType == AttachmentType.FILE ? downloadLabel : openLinkLabel }}\r\n </button>\r\n <button\r\n class=\"mb-2 me-2 eqp-attachments-preview-btn\"\r\n (click)=\"openPreviewDialog(attachmentsList[0])\"\r\n type=\"button\"\r\n mat-raised-button\r\n color=\"primary\"\r\n *ngIf=\"\r\n showPreview &&\r\n attachmentsList &&\r\n attachmentsList.length > 0 &&\r\n attachmentsList[0] &&\r\n (!attachmentsList[0].FileContentType ||\r\n (!attachmentsList[0].FileContentType.startsWith('video') &&\r\n !attachmentsList[0].FileContentType.startsWith('audio'))) &&\r\n attachmentsList[0].IsImage == true\r\n \">\r\n <mat-icon>visibility</mat-icon> {{ previewLabel }}\r\n </button>\r\n <button\r\n class=\"mb-2 eqp-attachments-delete-btn\"\r\n (click)=\"deleteAttachment(attachmentsList[0])\"\r\n type=\"button\"\r\n mat-raised-button\r\n *ngIf=\"attachmentsList && attachmentsList.length > 0 && attachmentsList[0]\"\r\n [disabled]=\"isDisabled\">\r\n <mat-icon>delete</mat-icon> {{ deleteLabel }}\r\n </button>\r\n </div>\r\n <div\r\n class=\"row\"\r\n style=\"margin-top: 10px\"\r\n *ngIf=\"\r\n attachmentsList.length > 0 &&\r\n attachmentsList[0] &&\r\n attachmentsList[0].FileDataBase64 &&\r\n attachmentsList[0].IsImage == true\r\n \">\r\n <div class=\"col-sm-12 d-flex justify-content-center\">\r\n <div class=\"single-attachment-inline-preview-container\">\r\n <img src=\"data:image/png;base64,{{ attachmentsList[0].FileDataBase64 }}\" />\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row\" *ngIf=\"attachmentsList.length > 0 && attachmentsList[0] && attachmentsList[0].IsImage != true\">\r\n <div class=\"col-sm-12\">\r\n <mat-form-field>\r\n <mat-label>{{ fileNameLabel }}</mat-label>\r\n <input readonly matInput [(ngModel)]=\"attachmentsList[0].FileName\" />\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div *ngIf=\"multipleAttachment == true\">\r\n <input\r\n #fileInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <ngx-file-drop (onFileDrop)=\"fileDropped($event)\" (click)=\"onSelectFile($event, fileInput)\">\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"!addingLinkMode\">\r\n <i class=\"fa-solid fa-cloud-upload-alt fa-3x mt-3\"></i>\r\n Trascina i file qui\r\n <div class=\"btn-group mt-1\" role=\"group\">\r\n <button type=\"button\" class=\"btn btn-light border-end\" (click)=\"fileInput.click()\">Scegli un file</button>\r\n <div class=\"btn-group\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-light border-start dropdown-toggle\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\"></button>\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n mat-menu-item\r\n (click)=\"fileInput.click()\"\r\n class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n mat-menu-item\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n mat-menu-item\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n </div>\r\n </div>\r\n </ng-template>\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"addingLinkMode\">\r\n <ng-container *ngTemplateOutlet=\"addingLinkTemplate\"></ng-container>\r\n </ng-template>\r\n </ngx-file-drop>\r\n <div class=\"mt-2\">\r\n <eqp-table\r\n #attachmentTable\r\n [createMatCard]=\"false\"\r\n #table\r\n [columns]=\"attachmentsColumns\"\r\n [isMultiLanguage]=\"isEqpTableMultiLanguage\"\r\n [data]=\"attachmentsList\"\r\n [paginatorVisible]=\"tablePaginatorVisible\"\r\n [matPaginatorSize]=\"tablePaginatorSize\"\r\n [emptyTableMessage]=\"emptyTableMessage\"\r\n [searchText]=\"eqpTableSearchText\"\r\n [isTableSearcheable]=\"isTableSearcheable\">\r\n </eqp-table>\r\n </div>\r\n</div>\r\n\r\n<ng-template #dialogCropImage>\r\n <!-- Richiama il template per le funzionalit\u00E0 del CROPPIE -->\r\n <div style=\"overflow-x: hidden\" [ngClass]=\"cropDialogClass\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"croppieTemplate\"\r\n [ngTemplateOutletContext]=\"{ form: newAttachmentForm }\"\r\n *ngIf=\"showCropImage == true\"></ng-container>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #inlinePreviewTemplate let-row=\"row\">\r\n <div\r\n class=\"inline-preview-container\"\r\n *ngIf=\"row.AttachmentType != AttachmentType.LINK && row.IsImage\"\r\n (click)=\"openPreviewDialog(row)\">\r\n <img src=\"data:image/png;base64,{{ row.FileThumbnailBase64 ? row.FileThumbnailBase64 : row.FileDataBase64 }}\" />\r\n </div>\r\n <div\r\n class=\"inline-preview-container\"\r\n *ngIf=\"row.AttachmentType != AttachmentType.LINK && !row.IsImage\"\r\n (click)=\"openPreviewDialog(row)\">\r\n <i [ngClass]=\"getAttachmentIcon(row)\"></i>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #dialogPreview>\r\n <mat-card class=\"example-card\" *ngIf=\"selectedAttachment\">\r\n <mat-card-content>\r\n <div class=\"row\">\r\n <div class=\"header-title-standard\">\r\n {{ previewLabel }} {{ selectedAttachment?.AttachmentType == attachmentType.FILE ? \"File\" : \"Link\" }}\r\n <button\r\n type=\"button\"\r\n class=\"btn-close closeButton\"\r\n mat-dialog-close\r\n (click)=\"selectedAttachment = null\"></button>\r\n </div>\r\n </div>\r\n <div class=\"row mt-2\">\r\n <!-- ANTEPRIMA IMMAGINE -->\r\n <div class=\"col-12 text-center preview-container\" *ngIf=\"selectedAttachment.IsImage\">\r\n <img\r\n class=\"image-preview\"\r\n src=\"data:image/png;base64,{{\r\n selectedAttachment.FileDataBase64\r\n ? selectedAttachment.FileDataBase64\r\n : selectedAttachment.FileThumbnailBase64\r\n }}\" />\r\n </div>\r\n\r\n <!-- ANTEPRIMA LINK -->\r\n <div class=\"col-12 preview-container\" *ngIf=\"!selectedAttachment.IsImage\">\r\n <iframe\r\n class=\"link-preview\"\r\n [src]=\"selectedAttachment.TrustedUrl\"\r\n [title]=\"selectedAttachment.FileName\"></iframe>\r\n </div>\r\n </div>\r\n <div class=\"row mt-3\">\r\n <div class=\"col-sm-12 text-center\">\r\n <button\r\n mat-mini-fab\r\n color=\"primary\"\r\n matTooltip=\"Download\"\r\n (click)=\"viewAttachment(selectedAttachment)\"\r\n *ngIf=\"selectedAttachment.AttachmentType != AttachmentType.LINK\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n </mat-card>\r\n</ng-template>\r\n\r\n<!-- TEMPLATE PER IL PULSANTE DI AGGIUNTA NUOVO ALLEGATO -->\r\n<ng-template #addAttachmentButton>\r\n <!--\r\n Pulsanti per l'aggiunta di un file o un link. Ne viene visualizzato uno se:\r\n - gli allowedTypes sono stati specificati, nell'array ne \u00E8 presente uno solo, quello inserito \u00E8 AttachmentType.FILE (o AttachmentType.LINK)\r\n e sono nella gestione di pi\u00F9 allegati (multipleAttachment == true)\r\n OPPURE\r\n - gli allowedTypes sono stati specificati, nell'array ne \u00E8 presente uno solo, quello inserito \u00E8 AttachmentType.FILE (o AttachmentType.LINK)\r\n e sono nella gestione di un singolo allegato (multipleAttachment == true) e non ne \u00E8 ancora stato selezionato uno (ovvero attachmentsList non esiste o non ha elementi)\r\n -->\r\n <input\r\n #fileInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <button\r\n class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n *ngIf=\"\r\n allowedTypes &&\r\n allowedTypes.length == 1 &&\r\n (multipleAttachment == true ||\r\n !attachmentsList ||\r\n attachmentsList.length == 0 ||\r\n (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \"\r\n (click)=\"addFile(allowedTypes[0], fileInput)\"\r\n [disabled]=\"isDisabled\">\r\n <!-- Per l'aggiunta dei file mostro un'icona diversa dall'aggiunta dei link -->\r\n <mat-icon *ngIf=\"allowedTypes[0] == 1\">cloud_upload</mat-icon>\r\n <i class=\"fas fa-link\" *ngIf=\"allowedTypes[0] == 2\"></i>\r\n <i class=\"fa-brands fa-dropbox\" *ngIf=\"allowedTypes[0] == 3\"></i>\r\n <span style=\"margin-left: 10px\">\r\n {{\r\n allowedTypes[0] == 1\r\n ? addButtonLabel + \" file\"\r\n : allowedTypes[0] == 2\r\n ? addButtonLabel + \" link\"\r\n : uploadWithDropboxLabel\r\n }}</span\r\n >\r\n </button>\r\n\r\n <!-- Pulsante per aprire il menu per la scelta del tipo di Attachment da creare -->\r\n <button\r\n class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\"\r\n [disabled]=\"isDisabled\"\r\n *ngIf=\"\r\n !separatedUploadButtons &&\r\n allowedTypes &&\r\n allowedTypes.length > 1 &&\r\n (multipleAttachment == true ||\r\n !attachmentsList ||\r\n attachmentsList.length == 0 ||\r\n (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \">\r\n <mat-icon *ngIf=\"multipleAttachment != true\">cloud_upload</mat-icon>\r\n <mat-icon *ngIf=\"multipleAttachment == true\">add</mat-icon>\r\n <span style=\"margin-left: 0px\"> {{ addButtonLabel }} </span>\r\n </button>\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <input\r\n #imageInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n mat-menu-item\r\n (click)=\"imageInput.click()\"\r\n class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n mat-menu-item\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n mat-menu-item\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n\r\n <div\r\n *ngIf=\"\r\n separatedUploadButtons &&\r\n allowedTypes &&\r\n allowedTypes.length > 1 &&\r\n (multipleAttachment == true ||\r\n !attachmentsList ||\r\n attachmentsList.length == 0 ||\r\n (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \">\r\n <div class=\"btn-group\">\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n (click)=\"imageInput.click()\"\r\n class=\"btn btn-secondary eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"secondary\"\r\n type=\"button\">\r\n <i class=\"fa-solid fa-cloud-upload\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <input\r\n #imageInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"btn btn-secondary eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"secondary\"\r\n type=\"button\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"btn btn-secondary eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"secondary\"\r\n type=\"button\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #croppieTemplate>\r\n <div class=\"row m-3\">\r\n <h4>{{ cropLabel }}</h4>\r\n </div>\r\n <div class=\"row m-2 crop-large\">\r\n <div class=\"col-md-12 d-flex align-items-center justify-content-center\">\r\n <button\r\n [matTooltip]=\"rotateLeftLabel\"\r\n *ngIf=\"cropOptions.includes(1)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"rotateLeft()\">\r\n <mat-icon style=\"vertical-align: middle\">rotate_left</mat-icon>\r\n </button>\r\n <button\r\n [matTooltip]=\"rotateRightLabel\"\r\n *ngIf=\"cropOptions.includes(1)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"rotateRight()\">\r\n <mat-icon style=\"vertical-align: middle\">rotate_right</mat-icon>\r\n </button>\r\n <button\r\n [matTooltip]=\"flipHorinzontalLabel\"\r\n *ngIf=\"cropOptions.includes(2)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"flipHorizontal()\">\r\n <mat-icon style=\"vertical-align: middle\">flip_horizontal</mat-icon>\r\n </button>\r\n <button\r\n [matTooltip]=\"flipVerticalLabel\"\r\n *ngIf=\"cropOptions.includes(2)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"flipVertical()\">\r\n <div style=\"transform: rotate(90deg)\"><mat-icon style=\"vertical-align: middle\">flip_vertical</mat-icon></div>\r\n </button>\r\n <button\r\n [matTooltip]=\"'Reset'\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"restoreOriginalDimensions()\">\r\n <mat-icon style=\"vertical-align: middle\">replay</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n <div class=\"row m-2 crop-small\">\r\n <div class=\"col-md-12 d-flex align-items-center justify-content-center\">\r\n <mat-icon *ngIf=\"cropOptions.includes(1)\" style=\"font-size: 27px; vertical-align: middle\" (click)=\"rotateLeft()\"\r\n >rotate_left</mat-icon\r\n >\r\n <mat-icon\r\n class=\"ms-3\"\r\n *ngIf=\"cropOptions.includes(1)\"\r\n style=\"font-size: 27px; vertical-align: middle\"\r\n (click)=\"rotateRight()\"\r\n >rotate_right</mat-icon\r\n >\r\n <mat-icon\r\n class=\"ms-3\"\r\n *ngIf=\"cropOptions.includes(2)\"\r\n style=\"font-size: 27px; vertical-align: middle\"\r\n (click)=\"flipHorizontal()\"\r\n >flip_horizontal</mat-icon\r\n >\r\n <div class=\"ms-3\" style=\"transform: rotate(90deg)\">\r\n <mat-icon\r\n *ngIf=\"cropOptions.includes(2)\"\r\n style=\"font-size: 27px; vertical-align: middle\"\r\n (click)=\"flipVertical()\"\r\n >flip_vertical</mat-icon\r\n >\r\n </div>\r\n <mat-icon class=\"ms-3\" (click)=\"restoreOriginalDimensions()\" style=\"font-size: 27px; vertical-align: middle\"\r\n >replay</mat-icon\r\n >\r\n </div>\r\n </div>\r\n <div class=\"row justify-content-center\">\r\n <div class=\"col-12 d-flex align-items-center justify-content-center\">\r\n <div class=\"crop-container\">\r\n <image-cropper\r\n [imageFile]=\"selectedFile\"\r\n [maintainAspectRatio]=\"false\"\r\n [autoCrop]=\"false\"\r\n [containWithinAspectRatio]=\"false\"\r\n [aspectRatio]=\"4 / 3\"\r\n [cropperMinWidth]=\"128\"\r\n [onlyScaleDown]=\"true\"\r\n [roundCropper]=\"false\"\r\n [canvasRotation]=\"0\"\r\n [transform]=\"transform\"\r\n [alignImage]=\"'left'\"\r\n format=\"png\"\r\n (imageCropped)=\"imageCropped($event)\"\r\n [resizeToWidth]=\"customWidth\"\r\n [resizeToHeight]=\"customHeight\"\r\n [canvasRotation]=\"canvasRotation\"\r\n [output]=\"'base64'\">\r\n </image-cropper>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row justify-content-center mt-2 mb-2 crop-large\">\r\n <div class=\"col-12 d-flex align-items-center justify-content-center\">\r\n <button\r\n class=\"btn btn-primary mat-raised-button eqp-attachments-confirm-btn me-2\"\r\n type=\"button\"\r\n (click)=\"confirmAddAttachment()\">\r\n {{ confirmLabel }}\r\n </button>\r\n <button class=\"btn mat-raised-button eqp-attachments-abort-btn\" type=\"button\" (click)=\"abortFile()\">\r\n {{ abortLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n <div class=\"row justify-content-center mt-2 mb-2 crop-small\">\r\n <div class=\"col-6 d-flex align-items-center justify-content-center\" style=\"font-size: 20px\">\r\n <i class=\"fa fa-times\" (click)=\"abortFile()\"></i>\r\n </div>\r\n <div class=\"col-6 d-flex align-items-center justify-content-center\" style=\"font-size: 20px\">\r\n <i class=\"fa fa-check\" (click)=\"confirmAddAttachment()\"></i>\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<!-- TEMPLATE PER FORM DI AGGIUNTA DI UN LINK -->\r\n<ng-template #addingLinkTemplate>\r\n <span class=\"mb-1\" style=\"font-size: 20px\"><i class=\"fa fa-link\"></i>Inserisci l'URL</span>\r\n <form [formGroup]=\"newAttachmentForm\" *ngIf=\"newAttachmentForm\">\r\n <div class=\"row mb-2\" style=\"height: 80px\">\r\n <div class=\"col-6 d-grid gap-2 mx-auto\">\r\n <div class=\"input-group mb-1\">\r\n <input\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"{{ fileNameLabel }}\"\r\n formControlName=\"name\"\r\n [(ngModel)]=\"newAttachment.FileName\" />\r\n </div>\r\n </div>\r\n <div class=\"col-12 d-grid gap-2\">\r\n <div class=\"input-group\">\r\n <input\r\n required\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"Link\"\r\n formControlName=\"path\"\r\n [(ngModel)]=\"newAttachment.FilePath\" />\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-6 d-grid gap-2\">\r\n <button\r\n class=\"btn btn-secondary mat-raised-button\"\r\n (click)=\"selectedAttachment = null; addingLinkMode = false\"\r\n type=\"button\">\r\n {{ exitLabel }}\r\n </button>\r\n </div>\r\n <div class=\"col-6 d-grid gap-2\">\r\n <button\r\n class=\"btn btn-primary mat-raised-button\"\r\n type=\"submit\"\r\n (click)=\"confirmAddAttachment(); selectedAttachment = null; addingLinkMode = false\">\r\n {{ saveLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n </form>\r\n</ng-template>\r\n", styles: ["ngx-file-drop ::ng-deep .ngx-file-drop__drop-zone{min-height:30vh;border-radius:5px!important;background-color:#e4e6ea!important;cursor:pointer;font-size:17px!important;border:dotted!important;padding-top:7vh}ngx-file-drop ::ng-deep .ngx-file-drop__drop-zone .ngx-file-drop__content{display:flex;flex-direction:column;align-items:center;justify-content:center;color:#73777a!important;margin:auto}.custom-height .mat-form-field-wrapper{height:20px}.eqp-attachments-header-title{font-weight:700;font-size:19px;line-height:24px;margin-bottom:auto}.single-attachment-inline-preview-container{max-height:400px;max-width:400px;display:flex;align-items:center}.single-attachment-inline-preview-container img{max-width:100%;max-height:120px}.inline-preview-container{max-height:100px;max-width:100px;display:flex;align-items:center;justify-content:center;width:100%;cursor:pointer}.inline-preview-container img{max-width:100%;max-height:100px}.inline-preview-container i{font-size:25px;margin:auto}.preview-container{max-height:60vh;max-width:100%}.preview-container .image-preview{max-width:100%;max-height:100%}.preview-container .link-preview{width:70vw;height:55vh}.closeButton{float:right}@media (max-width: 768px){.crop-large{display:none}}@media (min-width: 768px){.crop-small{display:none}}@media (max-width: 768px){.crop-container{max-width:55%}}@media (min-width: 768px){.crop-container{max-width:35%}}\n"], dependencies: [{ kind: "component", type: i6.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i6.MatMiniFabButton, selector: "button[mat-mini-fab]", exportAs: ["matButton"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i8.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatLabel, selector: "mat-label" }, { kind: "component", type: i9.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: i9.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i10.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "directive", type: i10.MatCardContent, selector: "mat-card-content" }, { kind: "component", type: i11.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i13.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i13.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i13.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i14.ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "cropperFrameAriaLabel", "output", "format", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "autoCrop", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "cropper", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange"] }, { kind: "component", type: i15.EqpTableComponent, selector: "eqp-table" }, { kind: "component", type: i16.NgxFileDropComponent, selector: "ngx-file-drop", inputs: ["accept", "directory", "multiple", "dropZoneLabel", "dropZoneClassName", "useDragEnter", "contentClassName", "showBrowseBtn", "browseBtnClassName", "browseBtnLabel", "disabled"], outputs: ["onFileDrop", "onFileOver", "onFileLeave"] }, { kind: "directive", type: i16.NgxFileDropContentTemplateDirective, selector: "[ngx-file-drop-content-tmp]" }] });
|
|
1283
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EqpAttachmentsComponent, selector: "eqp-attachments", inputs: { disableAction: "disableAction", showHeader: "showHeader", attachmentsList: "attachmentsList", singleAttachment: "singleAttachment", showMatCard: "showMatCard", multipleAttachment: "multipleAttachment", loadMultipleFiles: "loadMultipleFiles", emptyTableMessage: "emptyTableMessage", allowOnlyImages: "allowOnlyImages", acceptedFileTypes: "acceptedFileTypes", isDisabled: "isDisabled", showInlinePreview: "showInlinePreview", getAttachmentEndpoint: "getAttachmentEndpoint", productionBaseUrl: "productionBaseUrl", compressionOptions: "compressionOptions", allowedTypes: "allowedTypes", isEqpTableMultiLanguage: "isEqpTableMultiLanguage", tablePaginatorVisible: "tablePaginatorVisible", isTableSearcheable: "isTableSearcheable", tablePaginatorSize: "tablePaginatorSize", separatedUploadButtons: "separatedUploadButtons", showPreview: "showPreview", singleAttachmentDragAndDrop: "singleAttachmentDragAndDrop", cropOptions: "cropOptions", cropDialogClass: "cropDialogClass", maxFileSizeMB: "maxFileSizeMB", cardSize: "cardSize", customCardWidthPx: "customCardWidthPx", customCardHeightPx: "customCardHeightPx", openLinkLabel: "openLinkLabel", addButtonLabel: "addButtonLabel", downloadLabel: "downloadLabel", deleteLabel: "deleteLabel", fileNameLabel: "fileNameLabel", previewLabel: "previewLabel", uploadFileLabel: "uploadFileLabel", confirmLabel: "confirmLabel", abortLabel: "abortLabel", saveLabel: "saveLabel", exitLabel: "exitLabel", uploadWithDropboxLabel: "uploadWithDropboxLabel", cropLabel: "cropLabel", deleteDialogTitle: "deleteDialogTitle", deleteDialogMessage: "deleteDialogMessage", noImageSelectedErrorMessage: "noImageSelectedErrorMessage", wrongTypeSelectedErrorMessage: "wrongTypeSelectedErrorMessage", videoPreviewErrorMessage: "videoPreviewErrorMessage", audioPreviewErrorMessage: ["videoPreviewErrorMessage", "audioPreviewErrorMessage"], flipHorinzontalLabel: "flipHorinzontalLabel", flipVerticalLabel: "flipVerticalLabel", rotateRightLabel: "rotateRightLabel", rotateLeftLabel: "rotateLeftLabel", uploadTitle: "uploadTitle", uploadSubtitle: "uploadSubtitle", dropHereLabel: "dropHereLabel", supportedFormatsLabel: "supportedFormatsLabel", browseFilesLabel: "browseFilesLabel", uploadSummaryLabel: "uploadSummaryLabel", filesLabel: "filesLabel", totalSizeLabel: "totalSizeLabel", emptyStateLabel: "emptyStateLabel", addedSuccessfullyLabel: "addedSuccessfullyLabel", removedLabel: "removedLabel", chooseView: "chooseView", showSummary: "showSummary", viewMode: "viewMode" }, outputs: { localEditedAttachments: "localEditedAttachments", abortAddAttachment: "abortAddAttachment", downloadAttachment: "downloadAttachment", onDeleteAttachment: "onDeleteAttachment" }, viewQueries: [{ propertyName: "dialogAddAttachment", first: true, predicate: ["dialogAddAttachment"], descendants: true, static: true }, { propertyName: "dialogAddMultipleAttachment", first: true, predicate: ["dialogAddMultipleAttachment"], descendants: true, static: true }, { propertyName: "dialogCropImage", first: true, predicate: ["dialogCropImage"], descendants: true, static: true }, { propertyName: "addingLinkTemplate", first: true, predicate: ["addingLinkTemplate"], descendants: true }, { propertyName: "imageCropper", first: true, predicate: ImageCropperComponent, descendants: true }, { propertyName: "imageInput", first: true, predicate: ["imageInput"], descendants: true }, { propertyName: "inlinePreviewTemplate", first: true, predicate: ["inlinePreviewTemplate"], descendants: true, static: true }, { propertyName: "dialogPreview", first: true, predicate: ["dialogPreview"], descendants: true, static: true }], ngImport: i0, template: "<!-- Se richiesta la gestione singola mostra il pulsante di caricamento di un singolo file -->\r\n<div class=\"header\">\r\n <h1>{{ uploadTitle }}</h1>\r\n @if(chooseView == true){\r\n <mat-button-toggle-group [value]=\"viewMode\" (change)=\"setViewMode($event.value)\"\r\n aria-label=\"Modalit\u00E0 di visualizzazione\">\r\n <mat-button-toggle value=\"card\"><mat-icon>grid_view</mat-icon></mat-button-toggle>\r\n <mat-button-toggle value=\"table\"><mat-icon>view_list</mat-icon></mat-button-toggle>\r\n </mat-button-toggle-group>\r\n }\r\n</div>\r\n\r\n<!-- Gestione singolo -->\r\n@if (multipleAttachment != true) {\r\n@if (!singleAttachmentDragAndDrop) {\r\n@if (!addingLinkMode) {\r\n<div class=\"text-center\">\r\n <ng-container [ngTemplateOutlet]=\"addAttachmentButton\"></ng-container>\r\n</div>\r\n} @else {\r\n<div class=\"text-center\">\r\n <ng-container [ngTemplateOutlet]=\"addingLinkTemplate\"></ng-container>\r\n</div>\r\n}\r\n} @else {\r\n<input #fileInput id=\"file_attachment\" name=\"file_attachment\" type=\"file\" hidden (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n\r\n@if (allowedTypes && allowedTypes.includes(1) && (!attachmentsList || attachmentsList.length == 0 ||\r\n(attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n<div class=\"dropbox\" [class.dragover]=\"dragOver\" (dragover)=\"dragOver = true; $event.preventDefault()\"\r\n (dragleave)=\"dragOver = false\" (drop)=\"dragOver = false; fileDropped($event)\"\r\n (click)=\"onSelectFile($event, fileInput)\" role=\"button\" tabindex=\"0\" (keydown.enter)=\"fileInput.click()\"\r\n (keydown.space)=\"fileInput.click()\">\r\n <div class=\"dropbox-icon\">\uD83D\uDCC1</div>\r\n <div class=\"dropbox-text\">{{ dropHereLabel }}</div>\r\n <div class=\"dropbox-subtext\">\r\n {{ supportedFormatsLabel }}\r\n </div>\r\n <button class=\"browse-btn\" type=\"button\" (click)=\"$event.stopPropagation(); fileInput.click()\">\r\n {{ browseFilesLabel }}\r\n </button>\r\n <div class=\"secondary-action\">\r\n @if (allowedTypes.includes(2) || allowedTypes.includes(3)) {\r\n @if (allowedTypes.includes(2) && allowedTypes.includes(3)) {\r\n <a class=\"secondary-action-link\" [matMenuTriggerFor]=\"isDisabled ? null : linkMenu\"\r\n [class.disabled-link]=\"isDisabled\" (click)=\"$event.stopPropagation()\">\r\n o aggiungi da web\r\n </a>\r\n <mat-menu #linkMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"switchToAddingLinkMode()\">\r\n <mat-icon>link</mat-icon>\r\n <span>Aggiungi da link</span>\r\n </button>\r\n <button mat-menu-item (click)=\"chooseDropboxFile()\">\r\n <mat-icon>cloud_queue</mat-icon>\r\n <span>Carica da Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n }\r\n\r\n @else if (allowedTypes.includes(2)) {\r\n <a [class.disabled-link]=\"isDisabled\" class=\"secondary-action-link\"\r\n (click)=\"$event.stopPropagation(); switchToAddingLinkMode()\">\r\n aggiungi un link\r\n </a>\r\n }\r\n\r\n @else if (allowedTypes.includes(3)) {\r\n <a [class.disabled-link]=\"isDisabled\" class=\"secondary-action-link\"\r\n (click)=\"$event.stopPropagation(); chooseDropboxFile()\">\r\n carica da Dropbox\r\n </a>\r\n }\r\n }\r\n </div>\r\n</div>\r\n}\r\n}\r\n\r\n<!-- Azioni singolo elemento (come prima) -->\r\n<div class=\"text-center\">\r\n @if (attachmentsList && attachmentsList.length > 0 && attachmentsList[0]) {\r\n <button class=\"mb-2 me-2 eqp-attachments-download-btn\" (click)=\"viewAttachment(attachmentsList[0])\" type=\"button\"\r\n mat-raised-button color=\"primary\">\r\n @if (attachmentsList[0].AttachmentType == AttachmentType.FILE) {\r\n <mat-icon>download</mat-icon>\r\n } @else {\r\n <mat-icon>open_in_new</mat-icon>\r\n }\r\n {{ attachmentsList[0].AttachmentType == AttachmentType.FILE ? downloadLabel : openLinkLabel }}\r\n </button>\r\n\r\n @if (showPreview && (!attachmentsList[0].FileContentType || (!attachmentsList[0].FileContentType.startsWith('video')\r\n && !attachmentsList[0].FileContentType.startsWith('audio'))) && attachmentsList[0].IsImage == true) {\r\n <button class=\"mb-2 me-2 eqp-attachments-preview-btn\" (click)=\"openPreviewDialog(attachmentsList[0])\" type=\"button\"\r\n mat-raised-button color=\"primary\">\r\n <mat-icon>visibility</mat-icon> {{ previewLabel }}\r\n </button>\r\n }\r\n\r\n <button [disabled]=\"disableAction\" class=\"mb-2 eqp-attachments-delete-btn\"\r\n (click)=\"deleteAttachment(attachmentsList[0])\" type=\"button\" mat-raised-button [disabled]=\"isDisabled\">\r\n <mat-icon>delete</mat-icon> {{ deleteLabel }}\r\n </button>\r\n }\r\n</div>\r\n}\r\n\r\n<!-- Gestione multipla -->\r\n@if (multipleAttachment == true) {\r\n<input #fileInput id=\"file_attachment_multi\" name=\"file_attachment_multi\" type=\"file\" hidden\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n\r\n<div class=\"dropbox\" [class.dragover]=\"dragOver\" (dragover)=\"dragOver = true; $event.preventDefault()\"\r\n (dragleave)=\"dragOver = false\" (drop)=\"dragOver = false; fileDropped($event)\"\r\n (click)=\"onSelectFile($event, fileInput)\" role=\"button\" tabindex=\"0\" (keydown.enter)=\"fileInput.click()\"\r\n (keydown.space)=\"fileInput.click()\">\r\n <div class=\"dropbox-icon\">\uD83D\uDCC1</div>\r\n <div class=\"dropbox-text\">{{ dropHereLabel }}</div>\r\n <div class=\"dropbox-subtext\">\r\n {{ supportedFormatsLabel }}\r\n </div>\r\n <button class=\"browse-btn\" type=\"button\" (click)=\"$event.stopPropagation(); fileInput.click()\">\r\n {{ browseFilesLabel }}\r\n </button>\r\n @if (allowedTypes.includes(2)) {\r\n <div class=\"secondary-action-link\">\r\n o <a (click)=\"$event.stopPropagation(); switchToAddingLinkMode()\">aggiungi un link</a>\r\n </div>\r\n }\r\n</div>\r\n}\r\n\r\n@if (attachmentsList?.length > 0 && showSummary) {\r\n<div class=\"upload-stats\">\r\n\r\n <div class=\"stats-info\">\r\n <div class=\"stat-item\">\r\n <div class=\"stat-value\">{{ attachmentsList?.length }}</div>\r\n <div class=\"stat-label\">{{ filesLabel }}</div>\r\n </div>\r\n <div class=\"stat-item\">\r\n <div class=\"stat-value\">{{ totalSizeFormatted }}</div>\r\n <div class=\"stat-label\">{{ totalSizeLabel }}</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"progress-bar\">\r\n <div class=\"progress-fill\" [style.width.%]=\"progressPercent\"></div>\r\n </div>\r\n\r\n</div>\r\n} @else if(attachmentsList?.length == 0){\r\n<div class=\"empty-state\">{{ emptyStateLabel }}</div>\r\n}\r\n\r\n<!-- Griglia anteprime card (vale per singolo e multiplo) -->\r\n@if (viewMode === 'card') {\r\n\r\n<div class=\"file-previews\" [ngStyle]=\"getPreviewsContainerStyle()\">\r\n @for (att of attachmentsList; track att.ID) {\r\n <div [ngClass]=\"getCardClass(att)\">\r\n <div class=\"preview-img-container\" (click)=\"!att.isUploading && handlePrimaryAction(att)\">\r\n\r\n @if (att.IsImage) {\r\n <img class=\"preview-img\"\r\n [src]=\"'data:' + att.FileContentType + ';base64,' + (att.FileThumbnailBase64 || att.FileDataBase64)\"\r\n [alt]=\"att.FileName\" /> } @else {\r\n <div class=\"file-icon\"><i [ngClass]=\"getAttachmentIcon(att)\"></i></div>\r\n }\r\n\r\n <div class=\"preview-action-overlay\">\r\n @if (att.IsImage && canBePreviewed(att)) {\r\n } @else if (att.FileContentType === 'application/pdf' && canBePreviewed(att)) {\r\n <mat-icon>open_in_new</mat-icon>\r\n } @else if (att.AttachmentType === AttachmentType.FILE) {\r\n <mat-icon>download</mat-icon>\r\n } @else {\r\n <mat-icon>open_in_new</mat-icon>\r\n }\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"file-info\">\r\n <div class=\"file-name\" [title]=\"att.FileName\">{{ att.FileName }}</div>\r\n </div>\r\n\r\n @if(!disableAction){\r\n <button mat-icon-button class=\"remove-btn\" type=\"button\" aria-label=\"Rimuovi allegato\"\r\n (click)=\"deleteAttachment(att)\" [disabled]=\"att.isUploading\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n\r\n @if (att.isUploading) {\r\n <div class=\"upload-spinner\"></div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n} @else if(viewMode === 'table' && attachmentsList?.length > 0) {\r\n\r\n<div class=\"table-view-container\">\r\n <div class=\"table-header\">\r\n <div class=\"table-cell name-col\">File</div>\r\n <div class=\"table-cell actions-col\">Azioni</div>\r\n </div>\r\n\r\n @for (att of attachmentsList; track att.ID) {\r\n <div class=\"table-row\">\r\n <div class=\"table-cell name-col\">\r\n <i class=\"file-icon-small\" [ngClass]=\"getAttachmentIcon(att)\"></i>\r\n <div class=\"file-info-text\">\r\n <span class=\"file-name-table\">{{ att.FileName }}</span>\r\n <span class=\"file-type-table\">{{ att.FileExtension || 'Link' }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"table-cell actions-col\">\r\n <button mat-icon-button (click)=\"viewAttachment(att)\" matTooltip=\"Visualizza/Scarica\">\r\n <mat-icon>{{ att.AttachmentType === attachmentType.FILE ? 'download' : 'open_in_new' }}</mat-icon>\r\n </button>\r\n <button mat-icon-button [matMenuTriggerFor]=\"actionsMenu\" matTooltip=\"Altre opzioni\">\r\n <mat-icon>more_vert</mat-icon>\r\n </button>\r\n <mat-menu #actionsMenu=\"matMenu\">\r\n <button mat-menu-item\r\n (click)=\"openPreviewDialog(att)\"><mat-icon>visibility</mat-icon><span>Anteprima</span></button>\r\n <button [disabled]=\"disableAction\" mat-menu-item (click)=\"deleteAttachment(att)\" matTooltip=\"Elimina\">\r\n <mat-icon color=\"warn\">delete</mat-icon>\r\n <span>Elimina</span>\r\n </button>\r\n </mat-menu>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n}\r\n\r\n<!-- Notifica toast -->\r\n<div class=\"upload-notification\" [class.show]=\"toast?.visible\" [class.success]=\"toast?.type === 'success'\"\r\n [class.error]=\"toast?.type === 'error'\">\r\n <span>{{ toast?.text }}</span>\r\n <div class=\"notification-progress\"></div>\r\n</div>\r\n\r\n\r\n<ng-template #dialogCropImage>\r\n <div style=\"overflow-x: hidden\" [ngClass]=\"cropDialogClass\">\r\n @if (showCropImage == true) {\r\n <ng-container [ngTemplateOutlet]=\"croppieTemplate\" [ngTemplateOutletContext]=\"{ form: newAttachmentForm }\">\r\n </ng-container>\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n\r\n<ng-template #inlinePreviewTemplate let-row=\"row\">\r\n @if (row.AttachmentType != AttachmentType.LINK && row.IsImage) {\r\n <div class=\"inline-preview-container\" (click)=\"openPreviewDialog(row)\">\r\n <img [src]=\"'data:image/png;base64,' + (row.FileThumbnailBase64 ? row.FileThumbnailBase64 : row.FileDataBase64)\" />\r\n </div>\r\n } @else if (row.AttachmentType != AttachmentType.LINK && !row.IsImage) {\r\n <div class=\"inline-preview-container\" (click)=\"openPreviewDialog(row)\">\r\n <i [ngClass]=\"getAttachmentIcon(row)\"></i>\r\n </div>\r\n }\r\n</ng-template>\r\n\r\n\r\n<ng-template #dialogPreview>\r\n @if (selectedAttachment) {\r\n <h2 mat-dialog-title class=\"dialog-header\">\r\n <span>\r\n {{ previewLabel }} {{ selectedAttachment!.AttachmentType === attachmentType.FILE ? \"File\" : \"Link\" }}\r\n </span>\r\n <button mat-icon-button mat-dialog-close aria-label=\"Chiudi anteprima\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </h2>\r\n\r\n <mat-dialog-content class=\"dialog-content\">\r\n @if (selectedAttachment!.IsImage) {\r\n <img class=\"preview-image\"\r\n [src]=\"'data:image/png;base64,' + (selectedAttachment!.FileDataBase64 || selectedAttachment!.FileThumbnailBase64)\"\r\n alt=\"Anteprima allegato\" />\r\n } @else {\r\n <iframe class=\"preview-iframe\" [src]=\"selectedAttachment!.TrustedUrl\" [title]=\"selectedAttachment!.FileName\">\r\n </iframe>\r\n }\r\n </mat-dialog-content>\r\n\r\n <mat-dialog-actions [align]=\"'center'\">\r\n @if (selectedAttachment!.AttachmentType !== AttachmentType.LINK) {\r\n <button mat-raised-button color=\"primary\" (click)=\"viewAttachment(selectedAttachment!)\">\r\n <mat-icon>download</mat-icon>\r\n <span>{{ downloadLabel }}</span>\r\n </button>\r\n }\r\n </mat-dialog-actions>\r\n }\r\n</ng-template>\r\n\r\n\r\n<!-- TEMPLATE PER IL PULSANTE DI AGGIUNTA NUOVO ALLEGATO -->\r\n<ng-template #addAttachmentButton>\r\n <input #fileInput style=\"display: none\" id=\"file_attachment\" name=\"file_attachment\" type=\"file\"\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n\r\n @if (allowedTypes && allowedTypes.length == 1 && (multipleAttachment == true || !attachmentsList ||\r\n attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n <button class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\" mat-raised-button color=\"primary\" type=\"button\"\r\n (click)=\"addFile(allowedTypes[0], fileInput)\" [disabled]=\"isDisabled\">\r\n @if (allowedTypes[0] == 1) { <mat-icon>cloud_upload</mat-icon> }\r\n @if (allowedTypes[0] == 2) { <i class=\"fas fa-link\"></i> }\r\n @if (allowedTypes[0] == 3) { <i class=\"fa-brands fa-dropbox\"></i> }\r\n <span style=\"margin-left: 10px\">\r\n {{ allowedTypes[0] == 1 ? (addButtonLabel + \" file\") : allowedTypes[0] == 2 ? (addButtonLabel + \" link\") :\r\n uploadWithDropboxLabel }}\r\n </span>\r\n </button>\r\n }\r\n\r\n @if (!separatedUploadButtons && allowedTypes && allowedTypes.length > 1 && (multipleAttachment == true ||\r\n !attachmentsList || attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n <button class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\" mat-raised-button color=\"primary\" type=\"button\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\" [disabled]=\"isDisabled\">\r\n @if (multipleAttachment != true) { <mat-icon>cloud_upload</mat-icon> } @else { <mat-icon>add</mat-icon> }\r\n <span style=\"margin-left: 0px\">{{ addButtonLabel }}</span>\r\n </button>\r\n\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <input #imageInput style=\"display: none\" id=\"file_attachment\" name=\"file_attachment\" type=\"file\"\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n @if (allowedTypes.includes(1)) {\r\n <button mat-menu-item (click)=\"imageInput.click()\" class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n }\r\n @if (allowedTypes.includes(2)) {\r\n <button mat-menu-item (click)=\"switchToAddingLinkMode()\" class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n }\r\n @if (allowedTypes.includes(3)) {\r\n <button mat-menu-item (click)=\"chooseDropboxFile()\" class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n }\r\n </mat-menu>\r\n }\r\n\r\n @if (separatedUploadButtons && allowedTypes && allowedTypes.length > 1 && (multipleAttachment == true ||\r\n !attachmentsList || attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n <div class=\"btn-group\">\r\n @if (allowedTypes.includes(1)) {\r\n <button (click)=\"imageInput.click()\" class=\"btn btn-secondary eqp-attachments-add-btn\" mat-raised-button\r\n color=\"secondary\" type=\"button\">\r\n <i class=\"fa-solid fa-cloud-upload\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n }\r\n <input #imageInput style=\"display: none\" id=\"file_attachment\" name=\"file_attachment\" type=\"file\"\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n @if (allowedTypes.includes(2)) {\r\n <button (click)=\"switchToAddingLinkMode()\" class=\"btn btn-secondary eqp-attachments-add-btn\" mat-raised-button\r\n color=\"secondary\" type=\"button\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n }\r\n @if (allowedTypes.includes(3)) {\r\n <button (click)=\"chooseDropboxFile()\" class=\"btn btn-secondary eqp-attachments-add-btn\" mat-raised-button\r\n color=\"secondary\" type=\"button\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n\r\n\r\n<ng-template #croppieTemplate>\r\n <!-- Header compatto -->\r\n <h2 class=\"dialog-header m-3\">\r\n <span>{{ cropLabel }}</span>\r\n <button mat-icon-button type=\"button\" aria-label=\"Chiudi\" (click)=\"abortFile()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </h2>\r\n\r\n <!-- Contenuto scrollabile con griglia responsive -->\r\n <div class=\"dialog-content\">\r\n <!-- Toolbar controlli (large) -->\r\n <div class=\"controls-row crop-large\">\r\n @if (cropOptions.includes(1)) {\r\n <button [matTooltip]=\"rotateLeftLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"rotateLeft()\">\r\n <mat-icon>rotate_left</mat-icon>\r\n </button>\r\n <button [matTooltip]=\"rotateRightLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"rotateRight()\">\r\n <mat-icon>rotate_right</mat-icon>\r\n </button>\r\n }\r\n @if (cropOptions.includes(2)) {\r\n <button [matTooltip]=\"flipHorinzontalLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"flipHorizontal()\">\r\n <mat-icon>flip_horizontal</mat-icon>\r\n </button>\r\n <button [matTooltip]=\"flipVerticalLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"flipVertical()\">\r\n <mat-icon>flip_vertical</mat-icon>\r\n </button>\r\n }\r\n <button [matTooltip]=\"'Reset'\" class=\"btn btn-primary mat-raised-button\" (click)=\"restoreOriginalDimensions()\">\r\n <mat-icon>replay</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Toolbar controlli (small) -->\r\n <div class=\"controls-row crop-small\">\r\n @if (cropOptions.includes(1)) {\r\n <mat-icon class=\"control-icon\" (click)=\"rotateLeft()\">rotate_left</mat-icon>\r\n <mat-icon class=\"control-icon\" (click)=\"rotateRight()\">rotate_right</mat-icon>\r\n }\r\n @if (cropOptions.includes(2)) {\r\n <mat-icon class=\"control-icon\" (click)=\"flipHorizontal()\">flip_horizontal</mat-icon>\r\n <mat-icon class=\"control-icon\" (click)=\"flipVertical()\">flip_vertical</mat-icon>\r\n }\r\n <mat-icon class=\"control-icon\" (click)=\"restoreOriginalDimensions()\">replay</mat-icon>\r\n </div>\r\n\r\n <!-- Area crop con contenimento -->\r\n <div class=\"crop-area\">\r\n <div class=\"crop-container\">\r\n <image-cropper [imageFile]=\"selectedFile\" [maintainAspectRatio]=\"false\" [autoCrop]=\"false\"\r\n [containWithinAspectRatio]=\"false\" [aspectRatio]=\"4 / 3\" [cropperMinWidth]=\"128\" [onlyScaleDown]=\"true\"\r\n [roundCropper]=\"false\" [canvasRotation]=\"0\" [transform]=\"transform\" [alignImage]=\"'left'\" format=\"png\"\r\n (imageCropped)=\"imageCropped($event)\" [resizeToWidth]=\"customWidth\" [resizeToHeight]=\"customHeight\"\r\n [canvasRotation]=\"canvasRotation\" [output]=\"'base64'\">\r\n </image-cropper>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Azioni -->\r\n <div class=\"dialog-actions crop-large\">\r\n <button class=\"btn btn-primary mat-raised-button eqp-attachments-confirm-btn\" type=\"button\"\r\n (click)=\"confirmAddAttachment()\">\r\n {{ confirmLabel }}\r\n </button>\r\n <button class=\"btn mat-raised-button eqp-attachments-abort-btn\" type=\"button\" (click)=\"abortFile()\">\r\n {{ abortLabel }}\r\n </button>\r\n </div>\r\n\r\n <div class=\"dialog-actions crop-small\">\r\n <button class=\"icon-action\" type=\"button\" (click)=\"abortFile()\" aria-label=\"Abort\">\r\n <i class=\"fa fa-times\"></i>\r\n </button>\r\n <button class=\"icon-action\" type=\"button\" (click)=\"confirmAddAttachment()\" aria-label=\"Confirm\">\r\n <i class=\"fa fa-check\"></i>\r\n </button>\r\n </div>\r\n</ng-template>\r\n\r\n\r\n\r\n<!-- TEMPLATE PER FORM DI AGGIUNTA DI UN LINK -->\r\n<ng-template #addingLinkTemplate>\r\n <h2 mat-dialog-title>Aggiungi un link</h2>\r\n <mat-dialog-content class=\"add-link-dialog-content\">\r\n <form [formGroup]=\"newAttachmentForm\" class=\"add-link-form\">\r\n <mat-form-field appearance=\"outline\">\r\n <mat-label>URL *</mat-label>\r\n <input matInput formControlName=\"filePath\" placeholder=\"https://...\" required>\r\n @if (newAttachmentForm.get('filePath')?.hasError('pattern')) {\r\n <mat-error>L'URL non sembra valido.</mat-error>\r\n }\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\">\r\n <mat-label>Nome (opzionale)</mat-label>\r\n <input matInput formControlName=\"fileName\" placeholder=\"Es. Documento Progetto\">\r\n </mat-form-field>\r\n </form>\r\n </mat-dialog-content>\r\n <mat-dialog-actions align=\"end\">\r\n <button mat-button mat-dialog-close>Annulla</button>\r\n <button mat-raised-button color=\"primary\" [mat-dialog-close]=\"newAttachmentForm.value\"\r\n [disabled]=\"newAttachmentForm.invalid\">\r\n Aggiungi\r\n </button>\r\n </mat-dialog-actions>\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{--primary-color: #6a5af9;--primary-color-dark: #5441f8;--success-color: #1ce593;--error-color: #ff5b5b;--background-light: #f7f9fc;--background-card: rgba(255, 255, 255, .7);--text-color: #1e293b;--text-color-light: #64748b;--border-color: rgba(203, 213, 225, .5);--shadow-color: rgba(99, 102, 241, .2);--border-radius: 16px;--transition-speed: .3s}@keyframes fadeIn{0%{opacity:0;transform:translateY(15px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%{box-shadow:0 0 0 0 var(--primary-color)}70%{box-shadow:0 0 0 10px #6a5af900}to{box-shadow:0 0 #6a5af900}}.container{width:100%;max-width:700px;margin:2rem auto;font-family:Inter,sans-serif}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid var(--border-color)}.header h1{color:var(--text-color);font-size:1.8rem;font-weight:700;margin:0}.dropbox{width:100%;border:2px dashed var(--border-color);border-radius:var(--border-radius);padding:2.5rem;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;cursor:pointer;background-color:var(--background-card);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);transition:all var(--transition-speed) ease}.dropbox:hover{transform:translateY(-5px);box-shadow:0 10px 25px var(--shadow-color);border-color:var(--primary-color)}.dropbox.dragover{border-style:solid;border-color:var(--primary-color);transform:scale(1.02);box-shadow:0 0 25px var(--shadow-color);animation:pulse 1.5s infinite}.dropbox .dropbox-icon{font-size:3.5rem;color:var(--primary-color)}.dropbox .dropbox-text{font-size:1.1rem;font-weight:600;color:var(--text-color);margin-top:1rem}.dropbox .dropbox-subtext{color:var(--text-color-light);margin-top:.5rem}.dropbox .browse-btn{margin-top:1.5rem;padding:.75rem 1.5rem;background-color:var(--primary-color);color:#fff;border:none;border-radius:10px;font-weight:600;transition:all var(--transition-speed) ease}.dropbox .browse-btn:hover{background-color:var(--primary-color-dark);transform:translateY(-2px)}.file-previews{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:1.5rem;margin-top:2rem}.file-previews{display:grid;gap:16px;padding:16px;grid-template-columns:repeat(auto-fill,minmax(var(--card-min-width, 200px),1fr))}.file-preview{background-color:var(--background-card);border-radius:var(--border-radius);box-shadow:0 4px 15px #0000000d;border:1px solid var(--border-color);overflow:hidden;display:flex;flex-direction:column;position:relative;opacity:0;animation:fadeIn .5s ease-out forwards;transition:transform var(--transition-speed) ease,box-shadow var(--transition-speed) ease}.file-preview.uploading{cursor:wait}.file-preview.uploading .preview-img-container:after{content:\"\";position:absolute;inset:0;background:#ffffffb3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);pointer-events:none}.file-preview .preview-action-overlay{position:absolute;inset:0;display:flex;justify-content:center;align-items:center;background-color:#0006;color:#fff;opacity:1;transition:opacity .3s ease}.file-preview .preview-action-overlay .mat-icon{font-size:36px;width:36px;height:36px}.file-preview .remove-btn{position:absolute;top:8px;right:8px;z-index:2;background-color:#0000004d;color:#fff;width:32px;height:32px;display:flex;align-items:center;justify-content:center;padding:0;transition:all var(--transition-speed) ease}.file-preview .remove-btn:hover{background-color:#ff5b5b}.file-preview .remove-btn .mat-icon{font-size:18px}@media (hover: hover){.file-preview .remove-btn .file-preview .preview-action-overlay{opacity:0}.file-preview .remove-btn .file-preview:hover .preview-action-overlay,.file-preview .remove-btn .file-preview:hover .remove-btn{opacity:1}.file-preview .remove-btn .file-preview .remove-btn{opacity:0}}@media (hover: hover) and (min-width: 769px){.file-preview .remove-btn{opacity:0;transform:scale(.8)}.file-preview:hover{transform:translateY(-5px);box-shadow:0 10px 25px var(--shadow-color)}.file-preview:hover .remove-btn{opacity:1;transform:scale(1)}}.file-preview .preview-img-container{aspect-ratio:4/3;display:flex;align-items:center;justify-content:center;position:relative;cursor:pointer;background-color:#f0f2f5}.file-preview .preview-img{width:100%;height:100%;object-fit:cover}.file-preview .file-icon{font-size:4rem;color:var(--primary-color)}.file-preview .file-info{padding:12px;border-top:1px solid var(--border-color);flex-grow:1;display:flex;align-items:center}.file-preview .file-name{font-weight:600;font-size:.9rem;color:var(--text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-preview .upload-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:40px;height:40px;border:4px solid rgba(0,0,0,.1);border-left-color:var(--primary-color);border-radius:50%;animation:spin 1s linear infinite;pointer-events:none}.file-preview.card-small{width:140px}.file-preview.card-small .preview-img-container{height:80px}.file-preview.card-small .file-info{padding:8px}.file-preview.card-small .file-name{font-size:.8rem}.file-preview.card-small .file-icon{font-size:3rem}.file-preview.card-small .remove-btn{width:28px;height:28px}.file-preview.card-small .remove-btn .mat-icon{font-size:16px}.file-preview.card-large{width:280px}.file-preview.card-large .preview-img-container{height:180px}.file-preview.card-large .file-info{padding:16px}.file-preview.card-large .file-name{font-size:1rem}.file-preview.card-large .file-icon{font-size:5rem}.file-preview.card-large .remove-btn{width:36px;height:36px}.file-preview.card-large .remove-btn .mat-icon{font-size:20px}.file-preview[style*=--eqp-card-width]{width:var(--eqp-card-width)}.file-preview[style*=--eqp-card-width] .preview-img-container{height:calc(var(--eqp-card-width) * .65)}@keyframes spin{to{transform:translate(-50%,-50%) rotate(360deg)}}.upload-stats{background-color:var(--background-card);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid var(--border-color);border-radius:var(--border-radius);padding:1.5rem;margin-top:2rem;animation:fadeIn .5s ease-out}.upload-stats .stats-info{display:flex;justify-content:space-around}.upload-stats .progress-bar{height:10px;background-color:var(--border-color);border-radius:5px;overflow:hidden;margin-top:1rem}.upload-stats .progress-fill{height:100%;background:linear-gradient(90deg,var(--primary-color),var(--success-color));transition:width var(--transition-speed) ease-out;border-radius:5px}.upload-notification{position:fixed;bottom:20px;left:50%;transform:translate(-50%,100px);padding:1rem 1.5rem;border-radius:var(--border-radius);color:#fff;font-weight:600;box-shadow:0 10px 30px #0003;transition:transform var(--transition-speed) cubic-bezier(.175,.885,.32,1.275)}.upload-notification.show{transform:translate(-50%)}.upload-notification.success{background-color:var(--success-color)}.upload-notification.error{background-color:var(--error-color)}.dialog-header{display:flex;justify-content:space-between;align-items:center;width:100%}.dialog-content{display:flex;justify-content:center;align-items:center;padding:16px 24px;overflow:auto}.preview-image{max-width:100%;max-height:70vh;height:auto;object-fit:contain}.preview-iframe{width:80vw;height:75vh;border:none;max-width:1200px}.dialog-header{display:flex;align-items:center;justify-content:space-between}.dialog-content{display:flex;flex-direction:column;gap:12px;max-height:60vh;overflow:auto;padding:0 16px 8px}.controls-row{display:flex;align-items:center;justify-content:center;gap:8px}.control-icon{font-size:28px;cursor:pointer}.crop-area{display:flex;justify-content:center}.crop-container{width:100%;max-width:720px;aspect-ratio:4/3;border-radius:8px;background:#f9f9f9;overflow:hidden}image-cropper,image-cropper canvas,image-cropper img{max-width:100%!important;max-height:100%!important}.dialog-actions{display:flex;justify-content:center;gap:12px;padding:12px 16px}.crop-large{display:flex}.crop-small{display:none}@media (max-width: 600px){.dialog-content{max-height:55vh}.crop-container{max-width:100%;aspect-ratio:auto}.crop-large{display:none}.crop-small{display:flex;justify-content:center;gap:14px}.control-icon{font-size:24px}}.stats-header{display:flex;justify-content:space-between;align-items:center;width:100%}.table-view-container{margin-top:1.5rem;background-color:var(--background-card);border-radius:var(--border-radius);border:1px solid var(--border-color);overflow:hidden;animation:fadeIn .5s ease-out}.table-header,.table-row{display:flex;align-items:center;padding:0 1rem;transition:background-color var(--transition-speed) ease}.table-header{background-color:#f8f9fa;font-weight:600;color:var(--text-color-light);font-size:.8rem;text-transform:uppercase;border-bottom:2px solid var(--border-color)}.table-row{border-bottom:1px solid var(--border-color)}.table-row:last-child{border-bottom:none}.table-row:hover{background-color:#00000005}.table-cell{padding:1rem .5rem;display:flex;align-items:center;gap:1rem}.name-col{flex:4}.size-col,.date-col{flex:2}.actions-col{flex:1;justify-content:flex-end;min-width:150px}.file-icon-small{font-size:1.5rem;color:var(--primary-color)}.file-info-text{display:flex;flex-direction:column}.file-name-table{font-weight:600;color:var(--text-color)}.file-type-table{font-size:.8rem;color:var(--text-color-light)}@media (max-width: 768px){.date-col{display:none}.name-col{flex:3}.size-col{flex:2}.actions-col{flex:2;min-width:120px}}.secondary-action-link{color:var(--primary-color);text-decoration:none;border-bottom:1px solid var(--primary-color);cursor:pointer;font-weight:500}.secondary-action-link:hover{color:var(--primary-color-dark);border-bottom-color:var(--primary-color-dark)}h2[mat-dialog-title]+mat-dialog-content.add-link-dialog-content{padding-top:20px}mat-dialog-content.add-link-dialog-content{padding-left:24px;padding-right:24px;padding-bottom:20px}.add-link-form{display:flex;flex-direction:column;gap:16px}:host ::ng-deep .eqp-attachments-dialog .mat-mdc-dialog-container{--mdc-dialog-subhead-size: 1.25rem;--mdc-dialog-subhead-line-height: 1.5;--mdc-dialog-subhead-weight: 600;--mdc-dialog-supporting-text-size: 1rem}.empty-state{display:flex;flex-direction:column;justify-content:center;align-items:center;padding:1.5rem 1rem;margin-top:1.5rem;border:2px dashed var(--border-color);border-radius:var(--border-radius);background-color:#f8f9fa;text-align:center;color:var(--text-color-light);animation:fadeIn .5s ease-out}.empty-state:before{content:\"\\1f4ed\";font-size:2.5rem;margin-bottom:1rem;opacity:.7}.secondary-action-link.disabled-link{color:#adb5bd;cursor:not-allowed;border-bottom-color:transparent}.secondary-action-link.disabled-link:hover{color:#adb5bd;border-bottom-color:transparent}\n"], dependencies: [{ kind: "component", type: i6.MatButton, selector: " button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button] ", exportAs: ["matButton"] }, { kind: "component", type: i6.MatIconButton, selector: "button[mat-icon-button]", exportAs: ["matButton"] }, { kind: "directive", type: i7.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly"], exportAs: ["matInput"] }, { kind: "component", type: i8.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i8.MatLabel, selector: "mat-label" }, { kind: "directive", type: i8.MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "component", type: i9.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: i9.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i9.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "directive", type: i10.MatButtonToggleGroup, selector: "mat-button-toggle-group", inputs: ["appearance", "name", "vertical", "value", "multiple", "disabled", "hideSingleSelectionIndicator", "hideMultipleSelectionIndicator"], outputs: ["valueChange", "change"], exportAs: ["matButtonToggleGroup"] }, { kind: "component", type: i10.MatButtonToggle, selector: "mat-button-toggle", inputs: ["aria-label", "aria-labelledby", "id", "name", "value", "tabIndex", "disableRipple", "appearance", "checked", "disabled"], outputs: ["change"], exportAs: ["matButtonToggle"] }, { kind: "component", type: i11.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i1.MatDialogClose, selector: "[mat-dialog-close], [matDialogClose]", inputs: ["aria-label", "type", "mat-dialog-close", "matDialogClose"], exportAs: ["matDialogClose"] }, { kind: "directive", type: i1.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i1.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i1.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "directive", type: i12.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "directive", type: i2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i2.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: i2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i13.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i13.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i13.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: i14.ImageCropperComponent, selector: "image-cropper", inputs: ["imageChangedEvent", "imageURL", "imageBase64", "imageFile", "imageAltText", "options", "cropperFrameAriaLabel", "output", "format", "autoCrop", "cropper", "transform", "maintainAspectRatio", "aspectRatio", "resetCropOnAspectRatioChange", "resizeToWidth", "resizeToHeight", "cropperMinWidth", "cropperMinHeight", "cropperMaxHeight", "cropperMaxWidth", "cropperStaticWidth", "cropperStaticHeight", "canvasRotation", "initialStepSize", "roundCropper", "onlyScaleDown", "imageQuality", "backgroundColor", "containWithinAspectRatio", "hideResizeSquares", "allowMoveImage", "checkImageType", "alignImage", "disabled", "hidden"], outputs: ["imageCropped", "startCropImage", "imageLoaded", "cropperReady", "loadImageFailed", "transformChange", "cropperChange"] }] });
|
|
1132
1284
|
}
|
|
1133
1285
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EqpAttachmentsComponent, decorators: [{
|
|
1134
1286
|
type: Component,
|
|
1135
|
-
args: [{ selector: "eqp-attachments", template: "<!-- Se richiesta la gestione singola mostra il pulsante di caricamento di un singolo file -->\r\n<div *ngIf=\"multipleAttachment != true\">\r\n <div *ngIf=\"!singleAttachmentDragAndDrop\">\r\n <!-- Template del button per l'aggiunta di un allegato -->\r\n <div *ngIf=\"!addingLinkMode\" class=\"text-center\">\r\n <ng-container *ngTemplateOutlet=\"addAttachmentButton\"></ng-container>\r\n </div>\r\n <!-- Template della form per l'aggiunta di un link -->\r\n <div *ngIf=\"addingLinkMode\" class=\"text-center\">\r\n <ng-container *ngTemplateOutlet=\"addingLinkTemplate\"></ng-container>\r\n </div>\r\n </div>\r\n <div *ngIf=\"singleAttachmentDragAndDrop\">\r\n <input\r\n #fileInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <ngx-file-drop\r\n (onFileDrop)=\"fileDropped($event)\"\r\n (click)=\"onSelectFile($event, fileInput)\"\r\n *ngIf=\"\r\n allowedTypes &&\r\n allowedTypes.includes(1) &&\r\n (!attachmentsList || attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \">\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"!addingLinkMode\">\r\n <i class=\"fa-solid fa-cloud-upload-alt fa-3x mt-3\"></i>\r\n Trascina i file qui\r\n <div class=\"btn-group mt-1\" role=\"group\">\r\n <button type=\"button\" class=\"btn btn-light border-end\" (click)=\"fileInput.click()\">Scegli un file</button>\r\n <div class=\"btn-group\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-light border-start dropdown-toggle\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\"></button>\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n mat-menu-item\r\n (click)=\"fileInput.click()\"\r\n class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n mat-menu-item\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n mat-menu-item\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n </div>\r\n </div>\r\n </ng-template>\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"addingLinkMode\">\r\n <ng-container *ngTemplateOutlet=\"addingLinkTemplate\"></ng-container>\r\n </ng-template>\r\n </ngx-file-drop>\r\n </div>\r\n <div class=\"text-center\">\r\n <button\r\n class=\"mb-2 me-2 eqp-attachments-download-btn\"\r\n (click)=\"viewAttachment(attachmentsList[0])\"\r\n type=\"button\"\r\n mat-raised-button\r\n *ngIf=\"attachmentsList && attachmentsList.length > 0 && attachmentsList[0]\"\r\n color=\"primary\">\r\n <mat-icon *ngIf=\"attachmentsList[0].AttachmentType == AttachmentType.FILE\">download</mat-icon>\r\n <mat-icon *ngIf=\"attachmentsList[0].AttachmentType != AttachmentType.FILE\">open_in_new</mat-icon>\r\n {{ attachmentsList[0].AttachmentType == AttachmentType.FILE ? downloadLabel : openLinkLabel }}\r\n </button>\r\n <button\r\n class=\"mb-2 me-2 eqp-attachments-preview-btn\"\r\n (click)=\"openPreviewDialog(attachmentsList[0])\"\r\n type=\"button\"\r\n mat-raised-button\r\n color=\"primary\"\r\n *ngIf=\"\r\n showPreview &&\r\n attachmentsList &&\r\n attachmentsList.length > 0 &&\r\n attachmentsList[0] &&\r\n (!attachmentsList[0].FileContentType ||\r\n (!attachmentsList[0].FileContentType.startsWith('video') &&\r\n !attachmentsList[0].FileContentType.startsWith('audio'))) &&\r\n attachmentsList[0].IsImage == true\r\n \">\r\n <mat-icon>visibility</mat-icon> {{ previewLabel }}\r\n </button>\r\n <button\r\n class=\"mb-2 eqp-attachments-delete-btn\"\r\n (click)=\"deleteAttachment(attachmentsList[0])\"\r\n type=\"button\"\r\n mat-raised-button\r\n *ngIf=\"attachmentsList && attachmentsList.length > 0 && attachmentsList[0]\"\r\n [disabled]=\"isDisabled\">\r\n <mat-icon>delete</mat-icon> {{ deleteLabel }}\r\n </button>\r\n </div>\r\n <div\r\n class=\"row\"\r\n style=\"margin-top: 10px\"\r\n *ngIf=\"\r\n attachmentsList.length > 0 &&\r\n attachmentsList[0] &&\r\n attachmentsList[0].FileDataBase64 &&\r\n attachmentsList[0].IsImage == true\r\n \">\r\n <div class=\"col-sm-12 d-flex justify-content-center\">\r\n <div class=\"single-attachment-inline-preview-container\">\r\n <img src=\"data:image/png;base64,{{ attachmentsList[0].FileDataBase64 }}\" />\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row\" *ngIf=\"attachmentsList.length > 0 && attachmentsList[0] && attachmentsList[0].IsImage != true\">\r\n <div class=\"col-sm-12\">\r\n <mat-form-field>\r\n <mat-label>{{ fileNameLabel }}</mat-label>\r\n <input readonly matInput [(ngModel)]=\"attachmentsList[0].FileName\" />\r\n </mat-form-field>\r\n </div>\r\n </div>\r\n</div>\r\n\r\n<div *ngIf=\"multipleAttachment == true\">\r\n <input\r\n #fileInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <ngx-file-drop (onFileDrop)=\"fileDropped($event)\" (click)=\"onSelectFile($event, fileInput)\">\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"!addingLinkMode\">\r\n <i class=\"fa-solid fa-cloud-upload-alt fa-3x mt-3\"></i>\r\n Trascina i file qui\r\n <div class=\"btn-group mt-1\" role=\"group\">\r\n <button type=\"button\" class=\"btn btn-light border-end\" (click)=\"fileInput.click()\">Scegli un file</button>\r\n <div class=\"btn-group\" role=\"group\">\r\n <button\r\n type=\"button\"\r\n class=\"btn btn-light border-start dropdown-toggle\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\"></button>\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n mat-menu-item\r\n (click)=\"fileInput.click()\"\r\n class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n mat-menu-item\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n mat-menu-item\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n </div>\r\n </div>\r\n </ng-template>\r\n <ng-template ngx-file-drop-content-tmp *ngIf=\"addingLinkMode\">\r\n <ng-container *ngTemplateOutlet=\"addingLinkTemplate\"></ng-container>\r\n </ng-template>\r\n </ngx-file-drop>\r\n <div class=\"mt-2\">\r\n <eqp-table\r\n #attachmentTable\r\n [createMatCard]=\"false\"\r\n #table\r\n [columns]=\"attachmentsColumns\"\r\n [isMultiLanguage]=\"isEqpTableMultiLanguage\"\r\n [data]=\"attachmentsList\"\r\n [paginatorVisible]=\"tablePaginatorVisible\"\r\n [matPaginatorSize]=\"tablePaginatorSize\"\r\n [emptyTableMessage]=\"emptyTableMessage\"\r\n [searchText]=\"eqpTableSearchText\"\r\n [isTableSearcheable]=\"isTableSearcheable\">\r\n </eqp-table>\r\n </div>\r\n</div>\r\n\r\n<ng-template #dialogCropImage>\r\n <!-- Richiama il template per le funzionalit\u00E0 del CROPPIE -->\r\n <div style=\"overflow-x: hidden\" [ngClass]=\"cropDialogClass\">\r\n <ng-container\r\n [ngTemplateOutlet]=\"croppieTemplate\"\r\n [ngTemplateOutletContext]=\"{ form: newAttachmentForm }\"\r\n *ngIf=\"showCropImage == true\"></ng-container>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #inlinePreviewTemplate let-row=\"row\">\r\n <div\r\n class=\"inline-preview-container\"\r\n *ngIf=\"row.AttachmentType != AttachmentType.LINK && row.IsImage\"\r\n (click)=\"openPreviewDialog(row)\">\r\n <img src=\"data:image/png;base64,{{ row.FileThumbnailBase64 ? row.FileThumbnailBase64 : row.FileDataBase64 }}\" />\r\n </div>\r\n <div\r\n class=\"inline-preview-container\"\r\n *ngIf=\"row.AttachmentType != AttachmentType.LINK && !row.IsImage\"\r\n (click)=\"openPreviewDialog(row)\">\r\n <i [ngClass]=\"getAttachmentIcon(row)\"></i>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #dialogPreview>\r\n <mat-card class=\"example-card\" *ngIf=\"selectedAttachment\">\r\n <mat-card-content>\r\n <div class=\"row\">\r\n <div class=\"header-title-standard\">\r\n {{ previewLabel }} {{ selectedAttachment?.AttachmentType == attachmentType.FILE ? \"File\" : \"Link\" }}\r\n <button\r\n type=\"button\"\r\n class=\"btn-close closeButton\"\r\n mat-dialog-close\r\n (click)=\"selectedAttachment = null\"></button>\r\n </div>\r\n </div>\r\n <div class=\"row mt-2\">\r\n <!-- ANTEPRIMA IMMAGINE -->\r\n <div class=\"col-12 text-center preview-container\" *ngIf=\"selectedAttachment.IsImage\">\r\n <img\r\n class=\"image-preview\"\r\n src=\"data:image/png;base64,{{\r\n selectedAttachment.FileDataBase64\r\n ? selectedAttachment.FileDataBase64\r\n : selectedAttachment.FileThumbnailBase64\r\n }}\" />\r\n </div>\r\n\r\n <!-- ANTEPRIMA LINK -->\r\n <div class=\"col-12 preview-container\" *ngIf=\"!selectedAttachment.IsImage\">\r\n <iframe\r\n class=\"link-preview\"\r\n [src]=\"selectedAttachment.TrustedUrl\"\r\n [title]=\"selectedAttachment.FileName\"></iframe>\r\n </div>\r\n </div>\r\n <div class=\"row mt-3\">\r\n <div class=\"col-sm-12 text-center\">\r\n <button\r\n mat-mini-fab\r\n color=\"primary\"\r\n matTooltip=\"Download\"\r\n (click)=\"viewAttachment(selectedAttachment)\"\r\n *ngIf=\"selectedAttachment.AttachmentType != AttachmentType.LINK\">\r\n <mat-icon>download</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n </mat-card-content>\r\n </mat-card>\r\n</ng-template>\r\n\r\n<!-- TEMPLATE PER IL PULSANTE DI AGGIUNTA NUOVO ALLEGATO -->\r\n<ng-template #addAttachmentButton>\r\n <!--\r\n Pulsanti per l'aggiunta di un file o un link. Ne viene visualizzato uno se:\r\n - gli allowedTypes sono stati specificati, nell'array ne \u00E8 presente uno solo, quello inserito \u00E8 AttachmentType.FILE (o AttachmentType.LINK)\r\n e sono nella gestione di pi\u00F9 allegati (multipleAttachment == true)\r\n OPPURE\r\n - gli allowedTypes sono stati specificati, nell'array ne \u00E8 presente uno solo, quello inserito \u00E8 AttachmentType.FILE (o AttachmentType.LINK)\r\n e sono nella gestione di un singolo allegato (multipleAttachment == true) e non ne \u00E8 ancora stato selezionato uno (ovvero attachmentsList non esiste o non ha elementi)\r\n -->\r\n <input\r\n #fileInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <button\r\n class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n *ngIf=\"\r\n allowedTypes &&\r\n allowedTypes.length == 1 &&\r\n (multipleAttachment == true ||\r\n !attachmentsList ||\r\n attachmentsList.length == 0 ||\r\n (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \"\r\n (click)=\"addFile(allowedTypes[0], fileInput)\"\r\n [disabled]=\"isDisabled\">\r\n <!-- Per l'aggiunta dei file mostro un'icona diversa dall'aggiunta dei link -->\r\n <mat-icon *ngIf=\"allowedTypes[0] == 1\">cloud_upload</mat-icon>\r\n <i class=\"fas fa-link\" *ngIf=\"allowedTypes[0] == 2\"></i>\r\n <i class=\"fa-brands fa-dropbox\" *ngIf=\"allowedTypes[0] == 3\"></i>\r\n <span style=\"margin-left: 10px\">\r\n {{\r\n allowedTypes[0] == 1\r\n ? addButtonLabel + \" file\"\r\n : allowedTypes[0] == 2\r\n ? addButtonLabel + \" link\"\r\n : uploadWithDropboxLabel\r\n }}</span\r\n >\r\n </button>\r\n\r\n <!-- Pulsante per aprire il menu per la scelta del tipo di Attachment da creare -->\r\n <button\r\n class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"primary\"\r\n type=\"button\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\"\r\n [disabled]=\"isDisabled\"\r\n *ngIf=\"\r\n !separatedUploadButtons &&\r\n allowedTypes &&\r\n allowedTypes.length > 1 &&\r\n (multipleAttachment == true ||\r\n !attachmentsList ||\r\n attachmentsList.length == 0 ||\r\n (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \">\r\n <mat-icon *ngIf=\"multipleAttachment != true\">cloud_upload</mat-icon>\r\n <mat-icon *ngIf=\"multipleAttachment == true\">add</mat-icon>\r\n <span style=\"margin-left: 0px\"> {{ addButtonLabel }} </span>\r\n </button>\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <input\r\n #imageInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n mat-menu-item\r\n (click)=\"imageInput.click()\"\r\n class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n mat-menu-item\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n mat-menu-item\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n\r\n <div\r\n *ngIf=\"\r\n separatedUploadButtons &&\r\n allowedTypes &&\r\n allowedTypes.length > 1 &&\r\n (multipleAttachment == true ||\r\n !attachmentsList ||\r\n attachmentsList.length == 0 ||\r\n (attachmentsList.length > 0 && !attachmentsList[0]))\r\n \">\r\n <div class=\"btn-group\">\r\n <button\r\n *ngIf=\"allowedTypes.includes(1)\"\r\n (click)=\"imageInput.click()\"\r\n class=\"btn btn-secondary eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"secondary\"\r\n type=\"button\">\r\n <i class=\"fa-solid fa-cloud-upload\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n <input\r\n #imageInput\r\n style=\"display: none\"\r\n id=\"file_attachment\"\r\n name=\"file_attachment\"\r\n type=\"file\"\r\n (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\"\r\n [multiple]=\"loadMultipleFiles\" />\r\n <button\r\n *ngIf=\"allowedTypes.includes(2)\"\r\n (click)=\"switchToAddingLinkMode()\"\r\n class=\"btn btn-secondary eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"secondary\"\r\n type=\"button\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n <button\r\n *ngIf=\"allowedTypes.includes(3)\"\r\n (click)=\"chooseDropboxFile()\"\r\n class=\"btn btn-secondary eqp-attachments-add-btn\"\r\n mat-raised-button\r\n color=\"secondary\"\r\n type=\"button\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<ng-template #croppieTemplate>\r\n <div class=\"row m-3\">\r\n <h4>{{ cropLabel }}</h4>\r\n </div>\r\n <div class=\"row m-2 crop-large\">\r\n <div class=\"col-md-12 d-flex align-items-center justify-content-center\">\r\n <button\r\n [matTooltip]=\"rotateLeftLabel\"\r\n *ngIf=\"cropOptions.includes(1)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"rotateLeft()\">\r\n <mat-icon style=\"vertical-align: middle\">rotate_left</mat-icon>\r\n </button>\r\n <button\r\n [matTooltip]=\"rotateRightLabel\"\r\n *ngIf=\"cropOptions.includes(1)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"rotateRight()\">\r\n <mat-icon style=\"vertical-align: middle\">rotate_right</mat-icon>\r\n </button>\r\n <button\r\n [matTooltip]=\"flipHorinzontalLabel\"\r\n *ngIf=\"cropOptions.includes(2)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"flipHorizontal()\">\r\n <mat-icon style=\"vertical-align: middle\">flip_horizontal</mat-icon>\r\n </button>\r\n <button\r\n [matTooltip]=\"flipVerticalLabel\"\r\n *ngIf=\"cropOptions.includes(2)\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"flipVertical()\">\r\n <div style=\"transform: rotate(90deg)\"><mat-icon style=\"vertical-align: middle\">flip_vertical</mat-icon></div>\r\n </button>\r\n <button\r\n [matTooltip]=\"'Reset'\"\r\n class=\"btn btn-primary mat-raised-button ms-2\"\r\n (click)=\"restoreOriginalDimensions()\">\r\n <mat-icon style=\"vertical-align: middle\">replay</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n <div class=\"row m-2 crop-small\">\r\n <div class=\"col-md-12 d-flex align-items-center justify-content-center\">\r\n <mat-icon *ngIf=\"cropOptions.includes(1)\" style=\"font-size: 27px; vertical-align: middle\" (click)=\"rotateLeft()\"\r\n >rotate_left</mat-icon\r\n >\r\n <mat-icon\r\n class=\"ms-3\"\r\n *ngIf=\"cropOptions.includes(1)\"\r\n style=\"font-size: 27px; vertical-align: middle\"\r\n (click)=\"rotateRight()\"\r\n >rotate_right</mat-icon\r\n >\r\n <mat-icon\r\n class=\"ms-3\"\r\n *ngIf=\"cropOptions.includes(2)\"\r\n style=\"font-size: 27px; vertical-align: middle\"\r\n (click)=\"flipHorizontal()\"\r\n >flip_horizontal</mat-icon\r\n >\r\n <div class=\"ms-3\" style=\"transform: rotate(90deg)\">\r\n <mat-icon\r\n *ngIf=\"cropOptions.includes(2)\"\r\n style=\"font-size: 27px; vertical-align: middle\"\r\n (click)=\"flipVertical()\"\r\n >flip_vertical</mat-icon\r\n >\r\n </div>\r\n <mat-icon class=\"ms-3\" (click)=\"restoreOriginalDimensions()\" style=\"font-size: 27px; vertical-align: middle\"\r\n >replay</mat-icon\r\n >\r\n </div>\r\n </div>\r\n <div class=\"row justify-content-center\">\r\n <div class=\"col-12 d-flex align-items-center justify-content-center\">\r\n <div class=\"crop-container\">\r\n <image-cropper\r\n [imageFile]=\"selectedFile\"\r\n [maintainAspectRatio]=\"false\"\r\n [autoCrop]=\"false\"\r\n [containWithinAspectRatio]=\"false\"\r\n [aspectRatio]=\"4 / 3\"\r\n [cropperMinWidth]=\"128\"\r\n [onlyScaleDown]=\"true\"\r\n [roundCropper]=\"false\"\r\n [canvasRotation]=\"0\"\r\n [transform]=\"transform\"\r\n [alignImage]=\"'left'\"\r\n format=\"png\"\r\n (imageCropped)=\"imageCropped($event)\"\r\n [resizeToWidth]=\"customWidth\"\r\n [resizeToHeight]=\"customHeight\"\r\n [canvasRotation]=\"canvasRotation\"\r\n [output]=\"'base64'\">\r\n </image-cropper>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row justify-content-center mt-2 mb-2 crop-large\">\r\n <div class=\"col-12 d-flex align-items-center justify-content-center\">\r\n <button\r\n class=\"btn btn-primary mat-raised-button eqp-attachments-confirm-btn me-2\"\r\n type=\"button\"\r\n (click)=\"confirmAddAttachment()\">\r\n {{ confirmLabel }}\r\n </button>\r\n <button class=\"btn mat-raised-button eqp-attachments-abort-btn\" type=\"button\" (click)=\"abortFile()\">\r\n {{ abortLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n <div class=\"row justify-content-center mt-2 mb-2 crop-small\">\r\n <div class=\"col-6 d-flex align-items-center justify-content-center\" style=\"font-size: 20px\">\r\n <i class=\"fa fa-times\" (click)=\"abortFile()\"></i>\r\n </div>\r\n <div class=\"col-6 d-flex align-items-center justify-content-center\" style=\"font-size: 20px\">\r\n <i class=\"fa fa-check\" (click)=\"confirmAddAttachment()\"></i>\r\n </div>\r\n </div>\r\n</ng-template>\r\n\r\n<!-- TEMPLATE PER FORM DI AGGIUNTA DI UN LINK -->\r\n<ng-template #addingLinkTemplate>\r\n <span class=\"mb-1\" style=\"font-size: 20px\"><i class=\"fa fa-link\"></i>Inserisci l'URL</span>\r\n <form [formGroup]=\"newAttachmentForm\" *ngIf=\"newAttachmentForm\">\r\n <div class=\"row mb-2\" style=\"height: 80px\">\r\n <div class=\"col-6 d-grid gap-2 mx-auto\">\r\n <div class=\"input-group mb-1\">\r\n <input\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"{{ fileNameLabel }}\"\r\n formControlName=\"name\"\r\n [(ngModel)]=\"newAttachment.FileName\" />\r\n </div>\r\n </div>\r\n <div class=\"col-12 d-grid gap-2\">\r\n <div class=\"input-group\">\r\n <input\r\n required\r\n type=\"text\"\r\n class=\"form-control\"\r\n placeholder=\"Link\"\r\n formControlName=\"path\"\r\n [(ngModel)]=\"newAttachment.FilePath\" />\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"row\">\r\n <div class=\"col-6 d-grid gap-2\">\r\n <button\r\n class=\"btn btn-secondary mat-raised-button\"\r\n (click)=\"selectedAttachment = null; addingLinkMode = false\"\r\n type=\"button\">\r\n {{ exitLabel }}\r\n </button>\r\n </div>\r\n <div class=\"col-6 d-grid gap-2\">\r\n <button\r\n class=\"btn btn-primary mat-raised-button\"\r\n type=\"submit\"\r\n (click)=\"confirmAddAttachment(); selectedAttachment = null; addingLinkMode = false\">\r\n {{ saveLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n </form>\r\n</ng-template>\r\n", styles: ["ngx-file-drop ::ng-deep .ngx-file-drop__drop-zone{min-height:30vh;border-radius:5px!important;background-color:#e4e6ea!important;cursor:pointer;font-size:17px!important;border:dotted!important;padding-top:7vh}ngx-file-drop ::ng-deep .ngx-file-drop__drop-zone .ngx-file-drop__content{display:flex;flex-direction:column;align-items:center;justify-content:center;color:#73777a!important;margin:auto}.custom-height .mat-form-field-wrapper{height:20px}.eqp-attachments-header-title{font-weight:700;font-size:19px;line-height:24px;margin-bottom:auto}.single-attachment-inline-preview-container{max-height:400px;max-width:400px;display:flex;align-items:center}.single-attachment-inline-preview-container img{max-width:100%;max-height:120px}.inline-preview-container{max-height:100px;max-width:100px;display:flex;align-items:center;justify-content:center;width:100%;cursor:pointer}.inline-preview-container img{max-width:100%;max-height:100px}.inline-preview-container i{font-size:25px;margin:auto}.preview-container{max-height:60vh;max-width:100%}.preview-container .image-preview{max-width:100%;max-height:100%}.preview-container .link-preview{width:70vw;height:55vh}.closeButton{float:right}@media (max-width: 768px){.crop-large{display:none}}@media (min-width: 768px){.crop-small{display:none}}@media (max-width: 768px){.crop-container{max-width:55%}}@media (min-width: 768px){.crop-container{max-width:35%}}\n"] }]
|
|
1287
|
+
args: [{ selector: "eqp-attachments", template: "<!-- Se richiesta la gestione singola mostra il pulsante di caricamento di un singolo file -->\r\n<div class=\"header\">\r\n <h1>{{ uploadTitle }}</h1>\r\n @if(chooseView == true){\r\n <mat-button-toggle-group [value]=\"viewMode\" (change)=\"setViewMode($event.value)\"\r\n aria-label=\"Modalit\u00E0 di visualizzazione\">\r\n <mat-button-toggle value=\"card\"><mat-icon>grid_view</mat-icon></mat-button-toggle>\r\n <mat-button-toggle value=\"table\"><mat-icon>view_list</mat-icon></mat-button-toggle>\r\n </mat-button-toggle-group>\r\n }\r\n</div>\r\n\r\n<!-- Gestione singolo -->\r\n@if (multipleAttachment != true) {\r\n@if (!singleAttachmentDragAndDrop) {\r\n@if (!addingLinkMode) {\r\n<div class=\"text-center\">\r\n <ng-container [ngTemplateOutlet]=\"addAttachmentButton\"></ng-container>\r\n</div>\r\n} @else {\r\n<div class=\"text-center\">\r\n <ng-container [ngTemplateOutlet]=\"addingLinkTemplate\"></ng-container>\r\n</div>\r\n}\r\n} @else {\r\n<input #fileInput id=\"file_attachment\" name=\"file_attachment\" type=\"file\" hidden (change)=\"onFileAdded($event)\"\r\n [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n\r\n@if (allowedTypes && allowedTypes.includes(1) && (!attachmentsList || attachmentsList.length == 0 ||\r\n(attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n<div class=\"dropbox\" [class.dragover]=\"dragOver\" (dragover)=\"dragOver = true; $event.preventDefault()\"\r\n (dragleave)=\"dragOver = false\" (drop)=\"dragOver = false; fileDropped($event)\"\r\n (click)=\"onSelectFile($event, fileInput)\" role=\"button\" tabindex=\"0\" (keydown.enter)=\"fileInput.click()\"\r\n (keydown.space)=\"fileInput.click()\">\r\n <div class=\"dropbox-icon\">\uD83D\uDCC1</div>\r\n <div class=\"dropbox-text\">{{ dropHereLabel }}</div>\r\n <div class=\"dropbox-subtext\">\r\n {{ supportedFormatsLabel }}\r\n </div>\r\n <button class=\"browse-btn\" type=\"button\" (click)=\"$event.stopPropagation(); fileInput.click()\">\r\n {{ browseFilesLabel }}\r\n </button>\r\n <div class=\"secondary-action\">\r\n @if (allowedTypes.includes(2) || allowedTypes.includes(3)) {\r\n @if (allowedTypes.includes(2) && allowedTypes.includes(3)) {\r\n <a class=\"secondary-action-link\" [matMenuTriggerFor]=\"isDisabled ? null : linkMenu\"\r\n [class.disabled-link]=\"isDisabled\" (click)=\"$event.stopPropagation()\">\r\n o aggiungi da web\r\n </a>\r\n <mat-menu #linkMenu=\"matMenu\">\r\n <button mat-menu-item (click)=\"switchToAddingLinkMode()\">\r\n <mat-icon>link</mat-icon>\r\n <span>Aggiungi da link</span>\r\n </button>\r\n <button mat-menu-item (click)=\"chooseDropboxFile()\">\r\n <mat-icon>cloud_queue</mat-icon>\r\n <span>Carica da Dropbox</span>\r\n </button>\r\n </mat-menu>\r\n }\r\n\r\n @else if (allowedTypes.includes(2)) {\r\n <a [class.disabled-link]=\"isDisabled\" class=\"secondary-action-link\"\r\n (click)=\"$event.stopPropagation(); switchToAddingLinkMode()\">\r\n aggiungi un link\r\n </a>\r\n }\r\n\r\n @else if (allowedTypes.includes(3)) {\r\n <a [class.disabled-link]=\"isDisabled\" class=\"secondary-action-link\"\r\n (click)=\"$event.stopPropagation(); chooseDropboxFile()\">\r\n carica da Dropbox\r\n </a>\r\n }\r\n }\r\n </div>\r\n</div>\r\n}\r\n}\r\n\r\n<!-- Azioni singolo elemento (come prima) -->\r\n<div class=\"text-center\">\r\n @if (attachmentsList && attachmentsList.length > 0 && attachmentsList[0]) {\r\n <button class=\"mb-2 me-2 eqp-attachments-download-btn\" (click)=\"viewAttachment(attachmentsList[0])\" type=\"button\"\r\n mat-raised-button color=\"primary\">\r\n @if (attachmentsList[0].AttachmentType == AttachmentType.FILE) {\r\n <mat-icon>download</mat-icon>\r\n } @else {\r\n <mat-icon>open_in_new</mat-icon>\r\n }\r\n {{ attachmentsList[0].AttachmentType == AttachmentType.FILE ? downloadLabel : openLinkLabel }}\r\n </button>\r\n\r\n @if (showPreview && (!attachmentsList[0].FileContentType || (!attachmentsList[0].FileContentType.startsWith('video')\r\n && !attachmentsList[0].FileContentType.startsWith('audio'))) && attachmentsList[0].IsImage == true) {\r\n <button class=\"mb-2 me-2 eqp-attachments-preview-btn\" (click)=\"openPreviewDialog(attachmentsList[0])\" type=\"button\"\r\n mat-raised-button color=\"primary\">\r\n <mat-icon>visibility</mat-icon> {{ previewLabel }}\r\n </button>\r\n }\r\n\r\n <button [disabled]=\"disableAction\" class=\"mb-2 eqp-attachments-delete-btn\"\r\n (click)=\"deleteAttachment(attachmentsList[0])\" type=\"button\" mat-raised-button [disabled]=\"isDisabled\">\r\n <mat-icon>delete</mat-icon> {{ deleteLabel }}\r\n </button>\r\n }\r\n</div>\r\n}\r\n\r\n<!-- Gestione multipla -->\r\n@if (multipleAttachment == true) {\r\n<input #fileInput id=\"file_attachment_multi\" name=\"file_attachment_multi\" type=\"file\" hidden\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n\r\n<div class=\"dropbox\" [class.dragover]=\"dragOver\" (dragover)=\"dragOver = true; $event.preventDefault()\"\r\n (dragleave)=\"dragOver = false\" (drop)=\"dragOver = false; fileDropped($event)\"\r\n (click)=\"onSelectFile($event, fileInput)\" role=\"button\" tabindex=\"0\" (keydown.enter)=\"fileInput.click()\"\r\n (keydown.space)=\"fileInput.click()\">\r\n <div class=\"dropbox-icon\">\uD83D\uDCC1</div>\r\n <div class=\"dropbox-text\">{{ dropHereLabel }}</div>\r\n <div class=\"dropbox-subtext\">\r\n {{ supportedFormatsLabel }}\r\n </div>\r\n <button class=\"browse-btn\" type=\"button\" (click)=\"$event.stopPropagation(); fileInput.click()\">\r\n {{ browseFilesLabel }}\r\n </button>\r\n @if (allowedTypes.includes(2)) {\r\n <div class=\"secondary-action-link\">\r\n o <a (click)=\"$event.stopPropagation(); switchToAddingLinkMode()\">aggiungi un link</a>\r\n </div>\r\n }\r\n</div>\r\n}\r\n\r\n@if (attachmentsList?.length > 0 && showSummary) {\r\n<div class=\"upload-stats\">\r\n\r\n <div class=\"stats-info\">\r\n <div class=\"stat-item\">\r\n <div class=\"stat-value\">{{ attachmentsList?.length }}</div>\r\n <div class=\"stat-label\">{{ filesLabel }}</div>\r\n </div>\r\n <div class=\"stat-item\">\r\n <div class=\"stat-value\">{{ totalSizeFormatted }}</div>\r\n <div class=\"stat-label\">{{ totalSizeLabel }}</div>\r\n </div>\r\n </div>\r\n\r\n <div class=\"progress-bar\">\r\n <div class=\"progress-fill\" [style.width.%]=\"progressPercent\"></div>\r\n </div>\r\n\r\n</div>\r\n} @else if(attachmentsList?.length == 0){\r\n<div class=\"empty-state\">{{ emptyStateLabel }}</div>\r\n}\r\n\r\n<!-- Griglia anteprime card (vale per singolo e multiplo) -->\r\n@if (viewMode === 'card') {\r\n\r\n<div class=\"file-previews\" [ngStyle]=\"getPreviewsContainerStyle()\">\r\n @for (att of attachmentsList; track att.ID) {\r\n <div [ngClass]=\"getCardClass(att)\">\r\n <div class=\"preview-img-container\" (click)=\"!att.isUploading && handlePrimaryAction(att)\">\r\n\r\n @if (att.IsImage) {\r\n <img class=\"preview-img\"\r\n [src]=\"'data:' + att.FileContentType + ';base64,' + (att.FileThumbnailBase64 || att.FileDataBase64)\"\r\n [alt]=\"att.FileName\" /> } @else {\r\n <div class=\"file-icon\"><i [ngClass]=\"getAttachmentIcon(att)\"></i></div>\r\n }\r\n\r\n <div class=\"preview-action-overlay\">\r\n @if (att.IsImage && canBePreviewed(att)) {\r\n } @else if (att.FileContentType === 'application/pdf' && canBePreviewed(att)) {\r\n <mat-icon>open_in_new</mat-icon>\r\n } @else if (att.AttachmentType === AttachmentType.FILE) {\r\n <mat-icon>download</mat-icon>\r\n } @else {\r\n <mat-icon>open_in_new</mat-icon>\r\n }\r\n </div>\r\n\r\n </div>\r\n\r\n <div class=\"file-info\">\r\n <div class=\"file-name\" [title]=\"att.FileName\">{{ att.FileName }}</div>\r\n </div>\r\n\r\n @if(!disableAction){\r\n <button mat-icon-button class=\"remove-btn\" type=\"button\" aria-label=\"Rimuovi allegato\"\r\n (click)=\"deleteAttachment(att)\" [disabled]=\"att.isUploading\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n }\r\n\r\n @if (att.isUploading) {\r\n <div class=\"upload-spinner\"></div>\r\n }\r\n </div>\r\n }\r\n</div>\r\n\r\n} @else if(viewMode === 'table' && attachmentsList?.length > 0) {\r\n\r\n<div class=\"table-view-container\">\r\n <div class=\"table-header\">\r\n <div class=\"table-cell name-col\">File</div>\r\n <div class=\"table-cell actions-col\">Azioni</div>\r\n </div>\r\n\r\n @for (att of attachmentsList; track att.ID) {\r\n <div class=\"table-row\">\r\n <div class=\"table-cell name-col\">\r\n <i class=\"file-icon-small\" [ngClass]=\"getAttachmentIcon(att)\"></i>\r\n <div class=\"file-info-text\">\r\n <span class=\"file-name-table\">{{ att.FileName }}</span>\r\n <span class=\"file-type-table\">{{ att.FileExtension || 'Link' }}</span>\r\n </div>\r\n </div>\r\n\r\n <div class=\"table-cell actions-col\">\r\n <button mat-icon-button (click)=\"viewAttachment(att)\" matTooltip=\"Visualizza/Scarica\">\r\n <mat-icon>{{ att.AttachmentType === attachmentType.FILE ? 'download' : 'open_in_new' }}</mat-icon>\r\n </button>\r\n <button mat-icon-button [matMenuTriggerFor]=\"actionsMenu\" matTooltip=\"Altre opzioni\">\r\n <mat-icon>more_vert</mat-icon>\r\n </button>\r\n <mat-menu #actionsMenu=\"matMenu\">\r\n <button mat-menu-item\r\n (click)=\"openPreviewDialog(att)\"><mat-icon>visibility</mat-icon><span>Anteprima</span></button>\r\n <button [disabled]=\"disableAction\" mat-menu-item (click)=\"deleteAttachment(att)\" matTooltip=\"Elimina\">\r\n <mat-icon color=\"warn\">delete</mat-icon>\r\n <span>Elimina</span>\r\n </button>\r\n </mat-menu>\r\n </div>\r\n </div>\r\n }\r\n</div>\r\n}\r\n\r\n<!-- Notifica toast -->\r\n<div class=\"upload-notification\" [class.show]=\"toast?.visible\" [class.success]=\"toast?.type === 'success'\"\r\n [class.error]=\"toast?.type === 'error'\">\r\n <span>{{ toast?.text }}</span>\r\n <div class=\"notification-progress\"></div>\r\n</div>\r\n\r\n\r\n<ng-template #dialogCropImage>\r\n <div style=\"overflow-x: hidden\" [ngClass]=\"cropDialogClass\">\r\n @if (showCropImage == true) {\r\n <ng-container [ngTemplateOutlet]=\"croppieTemplate\" [ngTemplateOutletContext]=\"{ form: newAttachmentForm }\">\r\n </ng-container>\r\n }\r\n </div>\r\n</ng-template>\r\n\r\n\r\n<ng-template #inlinePreviewTemplate let-row=\"row\">\r\n @if (row.AttachmentType != AttachmentType.LINK && row.IsImage) {\r\n <div class=\"inline-preview-container\" (click)=\"openPreviewDialog(row)\">\r\n <img [src]=\"'data:image/png;base64,' + (row.FileThumbnailBase64 ? row.FileThumbnailBase64 : row.FileDataBase64)\" />\r\n </div>\r\n } @else if (row.AttachmentType != AttachmentType.LINK && !row.IsImage) {\r\n <div class=\"inline-preview-container\" (click)=\"openPreviewDialog(row)\">\r\n <i [ngClass]=\"getAttachmentIcon(row)\"></i>\r\n </div>\r\n }\r\n</ng-template>\r\n\r\n\r\n<ng-template #dialogPreview>\r\n @if (selectedAttachment) {\r\n <h2 mat-dialog-title class=\"dialog-header\">\r\n <span>\r\n {{ previewLabel }} {{ selectedAttachment!.AttachmentType === attachmentType.FILE ? \"File\" : \"Link\" }}\r\n </span>\r\n <button mat-icon-button mat-dialog-close aria-label=\"Chiudi anteprima\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </h2>\r\n\r\n <mat-dialog-content class=\"dialog-content\">\r\n @if (selectedAttachment!.IsImage) {\r\n <img class=\"preview-image\"\r\n [src]=\"'data:image/png;base64,' + (selectedAttachment!.FileDataBase64 || selectedAttachment!.FileThumbnailBase64)\"\r\n alt=\"Anteprima allegato\" />\r\n } @else {\r\n <iframe class=\"preview-iframe\" [src]=\"selectedAttachment!.TrustedUrl\" [title]=\"selectedAttachment!.FileName\">\r\n </iframe>\r\n }\r\n </mat-dialog-content>\r\n\r\n <mat-dialog-actions [align]=\"'center'\">\r\n @if (selectedAttachment!.AttachmentType !== AttachmentType.LINK) {\r\n <button mat-raised-button color=\"primary\" (click)=\"viewAttachment(selectedAttachment!)\">\r\n <mat-icon>download</mat-icon>\r\n <span>{{ downloadLabel }}</span>\r\n </button>\r\n }\r\n </mat-dialog-actions>\r\n }\r\n</ng-template>\r\n\r\n\r\n<!-- TEMPLATE PER IL PULSANTE DI AGGIUNTA NUOVO ALLEGATO -->\r\n<ng-template #addAttachmentButton>\r\n <input #fileInput style=\"display: none\" id=\"file_attachment\" name=\"file_attachment\" type=\"file\"\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n\r\n @if (allowedTypes && allowedTypes.length == 1 && (multipleAttachment == true || !attachmentsList ||\r\n attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n <button class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\" mat-raised-button color=\"primary\" type=\"button\"\r\n (click)=\"addFile(allowedTypes[0], fileInput)\" [disabled]=\"isDisabled\">\r\n @if (allowedTypes[0] == 1) { <mat-icon>cloud_upload</mat-icon> }\r\n @if (allowedTypes[0] == 2) { <i class=\"fas fa-link\"></i> }\r\n @if (allowedTypes[0] == 3) { <i class=\"fa-brands fa-dropbox\"></i> }\r\n <span style=\"margin-left: 10px\">\r\n {{ allowedTypes[0] == 1 ? (addButtonLabel + \" file\") : allowedTypes[0] == 2 ? (addButtonLabel + \" link\") :\r\n uploadWithDropboxLabel }}\r\n </span>\r\n </button>\r\n }\r\n\r\n @if (!separatedUploadButtons && allowedTypes && allowedTypes.length > 1 && (multipleAttachment == true ||\r\n !attachmentsList || attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n <button class=\"btn btn-primary mb-4 me-5 eqp-attachments-add-btn\" mat-raised-button color=\"primary\" type=\"button\"\r\n [matMenuTriggerFor]=\"attachmentTypeMenu\" [disabled]=\"isDisabled\">\r\n @if (multipleAttachment != true) { <mat-icon>cloud_upload</mat-icon> } @else { <mat-icon>add</mat-icon> }\r\n <span style=\"margin-left: 0px\">{{ addButtonLabel }}</span>\r\n </button>\r\n\r\n <mat-menu #attachmentTypeMenu=\"matMenu\">\r\n <input #imageInput style=\"display: none\" id=\"file_attachment\" name=\"file_attachment\" type=\"file\"\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n @if (allowedTypes.includes(1)) {\r\n <button mat-menu-item (click)=\"imageInput.click()\" class=\"eqp-attachments-file-btn\">\r\n <i class=\"fas fa-file\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n }\r\n @if (allowedTypes.includes(2)) {\r\n <button mat-menu-item (click)=\"switchToAddingLinkMode()\" class=\"eqp-attachments-link-btn\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n }\r\n @if (allowedTypes.includes(3)) {\r\n <button mat-menu-item (click)=\"chooseDropboxFile()\" class=\"eqp-attachments-link-btn\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n }\r\n </mat-menu>\r\n }\r\n\r\n @if (separatedUploadButtons && allowedTypes && allowedTypes.length > 1 && (multipleAttachment == true ||\r\n !attachmentsList || attachmentsList.length == 0 || (attachmentsList.length > 0 && !attachmentsList[0]))) {\r\n <div class=\"btn-group\">\r\n @if (allowedTypes.includes(1)) {\r\n <button (click)=\"imageInput.click()\" class=\"btn btn-secondary eqp-attachments-add-btn\" mat-raised-button\r\n color=\"secondary\" type=\"button\">\r\n <i class=\"fa-solid fa-cloud-upload\"></i>\r\n <span style=\"margin-left: 10px\">File</span>\r\n </button>\r\n }\r\n <input #imageInput style=\"display: none\" id=\"file_attachment\" name=\"file_attachment\" type=\"file\"\r\n (change)=\"onFileAdded($event)\" [accept]=\"acceptedFileTypes\" [multiple]=\"loadMultipleFiles\" />\r\n @if (allowedTypes.includes(2)) {\r\n <button (click)=\"switchToAddingLinkMode()\" class=\"btn btn-secondary eqp-attachments-add-btn\" mat-raised-button\r\n color=\"secondary\" type=\"button\">\r\n <i class=\"fas fa-link\"></i>\r\n <span style=\"margin-left: 10px\">Link</span>\r\n </button>\r\n }\r\n @if (allowedTypes.includes(3)) {\r\n <button (click)=\"chooseDropboxFile()\" class=\"btn btn-secondary eqp-attachments-add-btn\" mat-raised-button\r\n color=\"secondary\" type=\"button\">\r\n <i class=\"fa-brands fa-dropbox\"></i>\r\n <span style=\"margin-left: 10px\">Dropbox</span>\r\n </button>\r\n }\r\n </div>\r\n }\r\n</ng-template>\r\n\r\n\r\n<ng-template #croppieTemplate>\r\n <!-- Header compatto -->\r\n <h2 class=\"dialog-header m-3\">\r\n <span>{{ cropLabel }}</span>\r\n <button mat-icon-button type=\"button\" aria-label=\"Chiudi\" (click)=\"abortFile()\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </h2>\r\n\r\n <!-- Contenuto scrollabile con griglia responsive -->\r\n <div class=\"dialog-content\">\r\n <!-- Toolbar controlli (large) -->\r\n <div class=\"controls-row crop-large\">\r\n @if (cropOptions.includes(1)) {\r\n <button [matTooltip]=\"rotateLeftLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"rotateLeft()\">\r\n <mat-icon>rotate_left</mat-icon>\r\n </button>\r\n <button [matTooltip]=\"rotateRightLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"rotateRight()\">\r\n <mat-icon>rotate_right</mat-icon>\r\n </button>\r\n }\r\n @if (cropOptions.includes(2)) {\r\n <button [matTooltip]=\"flipHorinzontalLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"flipHorizontal()\">\r\n <mat-icon>flip_horizontal</mat-icon>\r\n </button>\r\n <button [matTooltip]=\"flipVerticalLabel\" class=\"btn btn-primary mat-raised-button\" (click)=\"flipVertical()\">\r\n <mat-icon>flip_vertical</mat-icon>\r\n </button>\r\n }\r\n <button [matTooltip]=\"'Reset'\" class=\"btn btn-primary mat-raised-button\" (click)=\"restoreOriginalDimensions()\">\r\n <mat-icon>replay</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Toolbar controlli (small) -->\r\n <div class=\"controls-row crop-small\">\r\n @if (cropOptions.includes(1)) {\r\n <mat-icon class=\"control-icon\" (click)=\"rotateLeft()\">rotate_left</mat-icon>\r\n <mat-icon class=\"control-icon\" (click)=\"rotateRight()\">rotate_right</mat-icon>\r\n }\r\n @if (cropOptions.includes(2)) {\r\n <mat-icon class=\"control-icon\" (click)=\"flipHorizontal()\">flip_horizontal</mat-icon>\r\n <mat-icon class=\"control-icon\" (click)=\"flipVertical()\">flip_vertical</mat-icon>\r\n }\r\n <mat-icon class=\"control-icon\" (click)=\"restoreOriginalDimensions()\">replay</mat-icon>\r\n </div>\r\n\r\n <!-- Area crop con contenimento -->\r\n <div class=\"crop-area\">\r\n <div class=\"crop-container\">\r\n <image-cropper [imageFile]=\"selectedFile\" [maintainAspectRatio]=\"false\" [autoCrop]=\"false\"\r\n [containWithinAspectRatio]=\"false\" [aspectRatio]=\"4 / 3\" [cropperMinWidth]=\"128\" [onlyScaleDown]=\"true\"\r\n [roundCropper]=\"false\" [canvasRotation]=\"0\" [transform]=\"transform\" [alignImage]=\"'left'\" format=\"png\"\r\n (imageCropped)=\"imageCropped($event)\" [resizeToWidth]=\"customWidth\" [resizeToHeight]=\"customHeight\"\r\n [canvasRotation]=\"canvasRotation\" [output]=\"'base64'\">\r\n </image-cropper>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Azioni -->\r\n <div class=\"dialog-actions crop-large\">\r\n <button class=\"btn btn-primary mat-raised-button eqp-attachments-confirm-btn\" type=\"button\"\r\n (click)=\"confirmAddAttachment()\">\r\n {{ confirmLabel }}\r\n </button>\r\n <button class=\"btn mat-raised-button eqp-attachments-abort-btn\" type=\"button\" (click)=\"abortFile()\">\r\n {{ abortLabel }}\r\n </button>\r\n </div>\r\n\r\n <div class=\"dialog-actions crop-small\">\r\n <button class=\"icon-action\" type=\"button\" (click)=\"abortFile()\" aria-label=\"Abort\">\r\n <i class=\"fa fa-times\"></i>\r\n </button>\r\n <button class=\"icon-action\" type=\"button\" (click)=\"confirmAddAttachment()\" aria-label=\"Confirm\">\r\n <i class=\"fa fa-check\"></i>\r\n </button>\r\n </div>\r\n</ng-template>\r\n\r\n\r\n\r\n<!-- TEMPLATE PER FORM DI AGGIUNTA DI UN LINK -->\r\n<ng-template #addingLinkTemplate>\r\n <h2 mat-dialog-title>Aggiungi un link</h2>\r\n <mat-dialog-content class=\"add-link-dialog-content\">\r\n <form [formGroup]=\"newAttachmentForm\" class=\"add-link-form\">\r\n <mat-form-field appearance=\"outline\">\r\n <mat-label>URL *</mat-label>\r\n <input matInput formControlName=\"filePath\" placeholder=\"https://...\" required>\r\n @if (newAttachmentForm.get('filePath')?.hasError('pattern')) {\r\n <mat-error>L'URL non sembra valido.</mat-error>\r\n }\r\n </mat-form-field>\r\n <mat-form-field appearance=\"outline\">\r\n <mat-label>Nome (opzionale)</mat-label>\r\n <input matInput formControlName=\"fileName\" placeholder=\"Es. Documento Progetto\">\r\n </mat-form-field>\r\n </form>\r\n </mat-dialog-content>\r\n <mat-dialog-actions align=\"end\">\r\n <button mat-button mat-dialog-close>Annulla</button>\r\n <button mat-raised-button color=\"primary\" [mat-dialog-close]=\"newAttachmentForm.value\"\r\n [disabled]=\"newAttachmentForm.invalid\">\r\n Aggiungi\r\n </button>\r\n </mat-dialog-actions>\r\n</ng-template>", styles: ["@charset \"UTF-8\";:host{--primary-color: #6a5af9;--primary-color-dark: #5441f8;--success-color: #1ce593;--error-color: #ff5b5b;--background-light: #f7f9fc;--background-card: rgba(255, 255, 255, .7);--text-color: #1e293b;--text-color-light: #64748b;--border-color: rgba(203, 213, 225, .5);--shadow-color: rgba(99, 102, 241, .2);--border-radius: 16px;--transition-speed: .3s}@keyframes fadeIn{0%{opacity:0;transform:translateY(15px)}to{opacity:1;transform:translateY(0)}}@keyframes pulse{0%{box-shadow:0 0 0 0 var(--primary-color)}70%{box-shadow:0 0 0 10px #6a5af900}to{box-shadow:0 0 #6a5af900}}.container{width:100%;max-width:700px;margin:2rem auto;font-family:Inter,sans-serif}.header{display:flex;justify-content:space-between;align-items:center;margin-bottom:2rem;padding-bottom:1.5rem;border-bottom:1px solid var(--border-color)}.header h1{color:var(--text-color);font-size:1.8rem;font-weight:700;margin:0}.dropbox{width:100%;border:2px dashed var(--border-color);border-radius:var(--border-radius);padding:2.5rem;display:flex;flex-direction:column;align-items:center;justify-content:center;text-align:center;cursor:pointer;background-color:var(--background-card);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);transition:all var(--transition-speed) ease}.dropbox:hover{transform:translateY(-5px);box-shadow:0 10px 25px var(--shadow-color);border-color:var(--primary-color)}.dropbox.dragover{border-style:solid;border-color:var(--primary-color);transform:scale(1.02);box-shadow:0 0 25px var(--shadow-color);animation:pulse 1.5s infinite}.dropbox .dropbox-icon{font-size:3.5rem;color:var(--primary-color)}.dropbox .dropbox-text{font-size:1.1rem;font-weight:600;color:var(--text-color);margin-top:1rem}.dropbox .dropbox-subtext{color:var(--text-color-light);margin-top:.5rem}.dropbox .browse-btn{margin-top:1.5rem;padding:.75rem 1.5rem;background-color:var(--primary-color);color:#fff;border:none;border-radius:10px;font-weight:600;transition:all var(--transition-speed) ease}.dropbox .browse-btn:hover{background-color:var(--primary-color-dark);transform:translateY(-2px)}.file-previews{display:grid;grid-template-columns:repeat(auto-fill,minmax(250px,1fr));gap:1.5rem;margin-top:2rem}.file-previews{display:grid;gap:16px;padding:16px;grid-template-columns:repeat(auto-fill,minmax(var(--card-min-width, 200px),1fr))}.file-preview{background-color:var(--background-card);border-radius:var(--border-radius);box-shadow:0 4px 15px #0000000d;border:1px solid var(--border-color);overflow:hidden;display:flex;flex-direction:column;position:relative;opacity:0;animation:fadeIn .5s ease-out forwards;transition:transform var(--transition-speed) ease,box-shadow var(--transition-speed) ease}.file-preview.uploading{cursor:wait}.file-preview.uploading .preview-img-container:after{content:\"\";position:absolute;inset:0;background:#ffffffb3;-webkit-backdrop-filter:blur(5px);backdrop-filter:blur(5px);pointer-events:none}.file-preview .preview-action-overlay{position:absolute;inset:0;display:flex;justify-content:center;align-items:center;background-color:#0006;color:#fff;opacity:1;transition:opacity .3s ease}.file-preview .preview-action-overlay .mat-icon{font-size:36px;width:36px;height:36px}.file-preview .remove-btn{position:absolute;top:8px;right:8px;z-index:2;background-color:#0000004d;color:#fff;width:32px;height:32px;display:flex;align-items:center;justify-content:center;padding:0;transition:all var(--transition-speed) ease}.file-preview .remove-btn:hover{background-color:#ff5b5b}.file-preview .remove-btn .mat-icon{font-size:18px}@media (hover: hover){.file-preview .remove-btn .file-preview .preview-action-overlay{opacity:0}.file-preview .remove-btn .file-preview:hover .preview-action-overlay,.file-preview .remove-btn .file-preview:hover .remove-btn{opacity:1}.file-preview .remove-btn .file-preview .remove-btn{opacity:0}}@media (hover: hover) and (min-width: 769px){.file-preview .remove-btn{opacity:0;transform:scale(.8)}.file-preview:hover{transform:translateY(-5px);box-shadow:0 10px 25px var(--shadow-color)}.file-preview:hover .remove-btn{opacity:1;transform:scale(1)}}.file-preview .preview-img-container{aspect-ratio:4/3;display:flex;align-items:center;justify-content:center;position:relative;cursor:pointer;background-color:#f0f2f5}.file-preview .preview-img{width:100%;height:100%;object-fit:cover}.file-preview .file-icon{font-size:4rem;color:var(--primary-color)}.file-preview .file-info{padding:12px;border-top:1px solid var(--border-color);flex-grow:1;display:flex;align-items:center}.file-preview .file-name{font-weight:600;font-size:.9rem;color:var(--text-color);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.file-preview .upload-spinner{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:40px;height:40px;border:4px solid rgba(0,0,0,.1);border-left-color:var(--primary-color);border-radius:50%;animation:spin 1s linear infinite;pointer-events:none}.file-preview.card-small{width:140px}.file-preview.card-small .preview-img-container{height:80px}.file-preview.card-small .file-info{padding:8px}.file-preview.card-small .file-name{font-size:.8rem}.file-preview.card-small .file-icon{font-size:3rem}.file-preview.card-small .remove-btn{width:28px;height:28px}.file-preview.card-small .remove-btn .mat-icon{font-size:16px}.file-preview.card-large{width:280px}.file-preview.card-large .preview-img-container{height:180px}.file-preview.card-large .file-info{padding:16px}.file-preview.card-large .file-name{font-size:1rem}.file-preview.card-large .file-icon{font-size:5rem}.file-preview.card-large .remove-btn{width:36px;height:36px}.file-preview.card-large .remove-btn .mat-icon{font-size:20px}.file-preview[style*=--eqp-card-width]{width:var(--eqp-card-width)}.file-preview[style*=--eqp-card-width] .preview-img-container{height:calc(var(--eqp-card-width) * .65)}@keyframes spin{to{transform:translate(-50%,-50%) rotate(360deg)}}.upload-stats{background-color:var(--background-card);-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);border:1px solid var(--border-color);border-radius:var(--border-radius);padding:1.5rem;margin-top:2rem;animation:fadeIn .5s ease-out}.upload-stats .stats-info{display:flex;justify-content:space-around}.upload-stats .progress-bar{height:10px;background-color:var(--border-color);border-radius:5px;overflow:hidden;margin-top:1rem}.upload-stats .progress-fill{height:100%;background:linear-gradient(90deg,var(--primary-color),var(--success-color));transition:width var(--transition-speed) ease-out;border-radius:5px}.upload-notification{position:fixed;bottom:20px;left:50%;transform:translate(-50%,100px);padding:1rem 1.5rem;border-radius:var(--border-radius);color:#fff;font-weight:600;box-shadow:0 10px 30px #0003;transition:transform var(--transition-speed) cubic-bezier(.175,.885,.32,1.275)}.upload-notification.show{transform:translate(-50%)}.upload-notification.success{background-color:var(--success-color)}.upload-notification.error{background-color:var(--error-color)}.dialog-header{display:flex;justify-content:space-between;align-items:center;width:100%}.dialog-content{display:flex;justify-content:center;align-items:center;padding:16px 24px;overflow:auto}.preview-image{max-width:100%;max-height:70vh;height:auto;object-fit:contain}.preview-iframe{width:80vw;height:75vh;border:none;max-width:1200px}.dialog-header{display:flex;align-items:center;justify-content:space-between}.dialog-content{display:flex;flex-direction:column;gap:12px;max-height:60vh;overflow:auto;padding:0 16px 8px}.controls-row{display:flex;align-items:center;justify-content:center;gap:8px}.control-icon{font-size:28px;cursor:pointer}.crop-area{display:flex;justify-content:center}.crop-container{width:100%;max-width:720px;aspect-ratio:4/3;border-radius:8px;background:#f9f9f9;overflow:hidden}image-cropper,image-cropper canvas,image-cropper img{max-width:100%!important;max-height:100%!important}.dialog-actions{display:flex;justify-content:center;gap:12px;padding:12px 16px}.crop-large{display:flex}.crop-small{display:none}@media (max-width: 600px){.dialog-content{max-height:55vh}.crop-container{max-width:100%;aspect-ratio:auto}.crop-large{display:none}.crop-small{display:flex;justify-content:center;gap:14px}.control-icon{font-size:24px}}.stats-header{display:flex;justify-content:space-between;align-items:center;width:100%}.table-view-container{margin-top:1.5rem;background-color:var(--background-card);border-radius:var(--border-radius);border:1px solid var(--border-color);overflow:hidden;animation:fadeIn .5s ease-out}.table-header,.table-row{display:flex;align-items:center;padding:0 1rem;transition:background-color var(--transition-speed) ease}.table-header{background-color:#f8f9fa;font-weight:600;color:var(--text-color-light);font-size:.8rem;text-transform:uppercase;border-bottom:2px solid var(--border-color)}.table-row{border-bottom:1px solid var(--border-color)}.table-row:last-child{border-bottom:none}.table-row:hover{background-color:#00000005}.table-cell{padding:1rem .5rem;display:flex;align-items:center;gap:1rem}.name-col{flex:4}.size-col,.date-col{flex:2}.actions-col{flex:1;justify-content:flex-end;min-width:150px}.file-icon-small{font-size:1.5rem;color:var(--primary-color)}.file-info-text{display:flex;flex-direction:column}.file-name-table{font-weight:600;color:var(--text-color)}.file-type-table{font-size:.8rem;color:var(--text-color-light)}@media (max-width: 768px){.date-col{display:none}.name-col{flex:3}.size-col{flex:2}.actions-col{flex:2;min-width:120px}}.secondary-action-link{color:var(--primary-color);text-decoration:none;border-bottom:1px solid var(--primary-color);cursor:pointer;font-weight:500}.secondary-action-link:hover{color:var(--primary-color-dark);border-bottom-color:var(--primary-color-dark)}h2[mat-dialog-title]+mat-dialog-content.add-link-dialog-content{padding-top:20px}mat-dialog-content.add-link-dialog-content{padding-left:24px;padding-right:24px;padding-bottom:20px}.add-link-form{display:flex;flex-direction:column;gap:16px}:host ::ng-deep .eqp-attachments-dialog .mat-mdc-dialog-container{--mdc-dialog-subhead-size: 1.25rem;--mdc-dialog-subhead-line-height: 1.5;--mdc-dialog-subhead-weight: 600;--mdc-dialog-supporting-text-size: 1rem}.empty-state{display:flex;flex-direction:column;justify-content:center;align-items:center;padding:1.5rem 1rem;margin-top:1.5rem;border:2px dashed var(--border-color);border-radius:var(--border-radius);background-color:#f8f9fa;text-align:center;color:var(--text-color-light);animation:fadeIn .5s ease-out}.empty-state:before{content:\"\\1f4ed\";font-size:2.5rem;margin-bottom:1rem;opacity:.7}.secondary-action-link.disabled-link{color:#adb5bd;cursor:not-allowed;border-bottom-color:transparent}.secondary-action-link.disabled-link:hover{color:#adb5bd;border-bottom-color:transparent}\n"] }]
|
|
1136
1288
|
}], ctorParameters: () => [{ type: i1.MatDialog }, { type: i2.FormBuilder }, { type: i3.DomSanitizer }, { type: i4.HttpClient }, { type: i0.ChangeDetectorRef }, { type: EqpAttachmentService }], propDecorators: { disableAction: [{
|
|
1137
1289
|
type: Input,
|
|
1138
1290
|
args: ["disableAction"]
|
|
1139
1291
|
}], showHeader: [{
|
|
1140
1292
|
type: Input,
|
|
1141
1293
|
args: ["showHeader"]
|
|
1142
|
-
}], headerTitle: [{
|
|
1143
|
-
type: Input,
|
|
1144
|
-
args: ["headerTitle"]
|
|
1145
1294
|
}], attachmentsList: [{
|
|
1146
1295
|
type: Input,
|
|
1147
1296
|
args: ["attachmentsList"]
|
|
1297
|
+
}], singleAttachment: [{
|
|
1298
|
+
type: Input,
|
|
1299
|
+
args: ["singleAttachment"]
|
|
1148
1300
|
}], showMatCard: [{
|
|
1149
1301
|
type: Input,
|
|
1150
1302
|
args: ["showMatCard"]
|
|
@@ -1154,9 +1306,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1154
1306
|
}], loadMultipleFiles: [{
|
|
1155
1307
|
type: Input,
|
|
1156
1308
|
args: ["loadMultipleFiles"]
|
|
1157
|
-
}], attachmentsColumns: [{
|
|
1158
|
-
type: Input,
|
|
1159
|
-
args: ["attachmentsColumns"]
|
|
1160
1309
|
}], emptyTableMessage: [{
|
|
1161
1310
|
type: Input,
|
|
1162
1311
|
args: ["emptyTableMessage"]
|
|
@@ -1211,9 +1360,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1211
1360
|
}], cropDialogClass: [{
|
|
1212
1361
|
type: Input,
|
|
1213
1362
|
args: ["cropDialogClass"]
|
|
1214
|
-
}],
|
|
1215
|
-
type: Input
|
|
1216
|
-
|
|
1363
|
+
}], maxFileSizeMB: [{
|
|
1364
|
+
type: Input
|
|
1365
|
+
}], cardSize: [{
|
|
1366
|
+
type: Input
|
|
1367
|
+
}], customCardWidthPx: [{
|
|
1368
|
+
type: Input
|
|
1369
|
+
}], customCardHeightPx: [{
|
|
1370
|
+
type: Input
|
|
1217
1371
|
}], openLinkLabel: [{
|
|
1218
1372
|
type: Input,
|
|
1219
1373
|
args: ["openLinkLabel"]
|
|
@@ -1253,9 +1407,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1253
1407
|
}], cropLabel: [{
|
|
1254
1408
|
type: Input,
|
|
1255
1409
|
args: ["cropLabel"]
|
|
1256
|
-
}], eqpTableSearchText: [{
|
|
1257
|
-
type: Input,
|
|
1258
|
-
args: ["eqpTableSearchText"]
|
|
1259
1410
|
}], deleteDialogTitle: [{
|
|
1260
1411
|
type: Input,
|
|
1261
1412
|
args: ["deleteDialogTitle"]
|
|
@@ -1286,6 +1437,35 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1286
1437
|
}], rotateLeftLabel: [{
|
|
1287
1438
|
type: Input,
|
|
1288
1439
|
args: ["rotateLeftLabel"]
|
|
1440
|
+
}], uploadTitle: [{
|
|
1441
|
+
type: Input
|
|
1442
|
+
}], uploadSubtitle: [{
|
|
1443
|
+
type: Input
|
|
1444
|
+
}], dropHereLabel: [{
|
|
1445
|
+
type: Input
|
|
1446
|
+
}], supportedFormatsLabel: [{
|
|
1447
|
+
type: Input
|
|
1448
|
+
}], browseFilesLabel: [{
|
|
1449
|
+
type: Input
|
|
1450
|
+
}], uploadSummaryLabel: [{
|
|
1451
|
+
type: Input
|
|
1452
|
+
}], filesLabel: [{
|
|
1453
|
+
type: Input
|
|
1454
|
+
}], totalSizeLabel: [{
|
|
1455
|
+
type: Input
|
|
1456
|
+
}], emptyStateLabel: [{
|
|
1457
|
+
type: Input
|
|
1458
|
+
}], addedSuccessfullyLabel: [{
|
|
1459
|
+
type: Input
|
|
1460
|
+
}], removedLabel: [{
|
|
1461
|
+
type: Input
|
|
1462
|
+
}], chooseView: [{
|
|
1463
|
+
type: Input
|
|
1464
|
+
}], showSummary: [{
|
|
1465
|
+
type: Input
|
|
1466
|
+
}], viewMode: [{
|
|
1467
|
+
type: Input,
|
|
1468
|
+
args: ["viewMode"]
|
|
1289
1469
|
}], localEditedAttachments: [{
|
|
1290
1470
|
type: Output
|
|
1291
1471
|
}], abortAddAttachment: [{
|
|
@@ -1305,15 +1485,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1305
1485
|
}], dialogCropImage: [{
|
|
1306
1486
|
type: ViewChild,
|
|
1307
1487
|
args: ["dialogCropImage", { static: true }]
|
|
1488
|
+
}], addingLinkTemplate: [{
|
|
1489
|
+
type: ViewChild,
|
|
1490
|
+
args: ['addingLinkTemplate']
|
|
1308
1491
|
}], imageCropper: [{
|
|
1309
1492
|
type: ViewChild,
|
|
1310
1493
|
args: [ImageCropperComponent]
|
|
1311
1494
|
}], imageInput: [{
|
|
1312
1495
|
type: ViewChild,
|
|
1313
1496
|
args: ["imageInput"]
|
|
1314
|
-
}], attachmentTable: [{
|
|
1315
|
-
type: ViewChild,
|
|
1316
|
-
args: ["attachmentTable", { static: false }]
|
|
1317
1497
|
}], inlinePreviewTemplate: [{
|
|
1318
1498
|
type: ViewChild,
|
|
1319
1499
|
args: ["inlinePreviewTemplate", { static: true }]
|
|
@@ -1525,16 +1705,12 @@ class EqpAttachmentsModule {
|
|
|
1525
1705
|
FormsModule,
|
|
1526
1706
|
CommonModule,
|
|
1527
1707
|
ReactiveFormsModule,
|
|
1528
|
-
|
|
1529
|
-
EqpTableModule,
|
|
1530
|
-
NgxFileDropModule], exports: [EqpAttachmentsComponent] });
|
|
1708
|
+
ImageCropperComponent], exports: [EqpAttachmentsComponent] });
|
|
1531
1709
|
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EqpAttachmentsModule, imports: [MaterialModule,
|
|
1532
1710
|
FormsModule,
|
|
1533
1711
|
CommonModule,
|
|
1534
1712
|
ReactiveFormsModule,
|
|
1535
|
-
|
|
1536
|
-
EqpTableModule,
|
|
1537
|
-
NgxFileDropModule] });
|
|
1713
|
+
ImageCropperComponent] });
|
|
1538
1714
|
}
|
|
1539
1715
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EqpAttachmentsModule, decorators: [{
|
|
1540
1716
|
type: NgModule,
|
|
@@ -1545,9 +1721,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImpo
|
|
|
1545
1721
|
FormsModule,
|
|
1546
1722
|
CommonModule,
|
|
1547
1723
|
ReactiveFormsModule,
|
|
1548
|
-
|
|
1549
|
-
EqpTableModule,
|
|
1550
|
-
NgxFileDropModule
|
|
1724
|
+
ImageCropperComponent
|
|
1551
1725
|
],
|
|
1552
1726
|
exports: [EqpAttachmentsComponent]
|
|
1553
1727
|
}]
|