@eqproject/eqp-attachments 3.1.0 → 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.
@@ -268,10 +268,6 @@ class EqpAttachmentsComponent {
268
268
  * Se TRUE mostra il titolo nell'header nel caso in cui "multipleAttachment" è TRUE ("Elenco allegati" di default).
269
269
  */
270
270
  showHeader = true;
271
- /**
272
- * Titolo da visualizzare se il parametro "showHeader" è TRUE. Di devault viene visualizzato "Elenco allegati".
273
- */
274
- headerTitle = "Elenco allegati";
275
271
  /**
276
272
  * Sorgente dati da visualizzare. Nel caso si vuole gestire un singolo allegato va passato in ogni caso come Array.
277
273
  */
@@ -781,6 +777,9 @@ class EqpAttachmentsComponent {
781
777
  EqpAttachmentDialogService.Info("Impossibile aprire l'anteprima dell'allegato, file mancante.");
782
778
  return;
783
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
+ }
784
783
  else if (!this.selectedAttachment.IsImage) {
785
784
  if (this.selectedAttachment.FilePath && this.productionBaseUrl) {
786
785
  this.selectedAttachment.TrustedUrl = this.sanitizer.bypassSecurityTrustResourceUrl("https://docs.google.com/gview?url=" +
@@ -812,6 +811,11 @@ class EqpAttachmentsComponent {
812
811
  * immagine per il croppie (in caso di caricamento multiplo le funzionalità del croppie sono disabilitate).
813
812
  */
814
813
  async onFileAdded(event, isFileDropped = false) {
814
+ if (this.isDisabled) {
815
+ EqpAttachmentDialogService.Info("Caricamento allegati non disponibile.");
816
+ event.preventDefault();
817
+ return;
818
+ }
815
819
  this.showCropImage = false;
816
820
  // let filesOnInput = isFileDropped ? event : event.target.files;
817
821
  const filesOnInput = isFileDropped
@@ -849,7 +853,8 @@ class EqpAttachmentsComponent {
849
853
  hasBackdrop: true,
850
854
  width: "60%",
851
855
  maxHeight: "80%",
852
- maxWidth: "70vh"
856
+ maxWidth: "70vh",
857
+ panelClass: 'eqp-attachments-dialog'
853
858
  });
854
859
  }
855
860
  else {
@@ -1200,6 +1205,7 @@ class EqpAttachmentsComponent {
1200
1205
  // 2. Apri il dialogo
1201
1206
  const dialogRef = this.dialog.open(this.addingLinkTemplate, {
1202
1207
  width: '500px',
1208
+ panelClass: 'eqp-attachments-dialog'
1203
1209
  });
1204
1210
  // 3. Gestisci il risultato alla chiusura
1205
1211
  dialogRef.afterClosed().subscribe(result => {
@@ -1245,21 +1251,46 @@ class EqpAttachmentsComponent {
1245
1251
  }
1246
1252
  return { '--card-min-width': minWidth };
1247
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;
1281
+ }
1248
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 });
1249
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "17.3.12", type: EqpAttachmentsComponent, selector: "eqp-attachments", inputs: { disableAction: "disableAction", showHeader: "showHeader", headerTitle: "headerTitle", 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]=\"linkMenu\" (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=\"secondary-action-link\" (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=\"secondary-action-link\" (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 <div class=\"secondary-action-link\">\r\n o <a (click)=\"$event.stopPropagation(); switchToAddingLinkMode()\">aggiungi un link</a>\r\n </div>\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\r\n <div class=\"preview-img-container\" (click)=\"!att.isUploading && openPreviewDialog(att)\">\r\n @if (att.IsImage) {\r\n <img class=\"preview-img\" [src]=\"'data:image/png;base64,' + (att.FileThumbnailBase64 || att.FileDataBase64)\"\r\n [alt]=\"att.FileName\" />\r\n } @else {\r\n <div class=\"file-icon\">\r\n <i [ngClass]=\"getAttachmentIcon(att)\"></i>\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)\">\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 {\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: [":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 .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) 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}\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"] }] });
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"] }] });
1250
1284
  }
1251
1285
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "17.3.12", ngImport: i0, type: EqpAttachmentsComponent, decorators: [{
1252
1286
  type: Component,
1253
- 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]=\"linkMenu\" (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=\"secondary-action-link\" (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=\"secondary-action-link\" (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 <div class=\"secondary-action-link\">\r\n o <a (click)=\"$event.stopPropagation(); switchToAddingLinkMode()\">aggiungi un link</a>\r\n </div>\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\r\n <div class=\"preview-img-container\" (click)=\"!att.isUploading && openPreviewDialog(att)\">\r\n @if (att.IsImage) {\r\n <img class=\"preview-img\" [src]=\"'data:image/png;base64,' + (att.FileThumbnailBase64 || att.FileDataBase64)\"\r\n [alt]=\"att.FileName\" />\r\n } @else {\r\n <div class=\"file-icon\">\r\n <i [ngClass]=\"getAttachmentIcon(att)\"></i>\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)\">\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 {\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: [":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 .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) 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}\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"] }]
1254
1288
  }], ctorParameters: () => [{ type: i1.MatDialog }, { type: i2.FormBuilder }, { type: i3.DomSanitizer }, { type: i4.HttpClient }, { type: i0.ChangeDetectorRef }, { type: EqpAttachmentService }], propDecorators: { disableAction: [{
1255
1289
  type: Input,
1256
1290
  args: ["disableAction"]
1257
1291
  }], showHeader: [{
1258
1292
  type: Input,
1259
1293
  args: ["showHeader"]
1260
- }], headerTitle: [{
1261
- type: Input,
1262
- args: ["headerTitle"]
1263
1294
  }], attachmentsList: [{
1264
1295
  type: Input,
1265
1296
  args: ["attachmentsList"]