feat(back-end and front-end): back-office pazzo
All checks were successful
Build, Test and Deploy / test-backend (push) Successful in 44s
Build, Test and Deploy / build-and-push (push) Successful in 46s
Build, Test and Deploy / deploy (push) Successful in 9s

This commit is contained in:
2026-02-27 15:46:41 +01:00
parent 47553ebb82
commit ed76b13e4c
30 changed files with 2616 additions and 272 deletions

View File

@@ -1,6 +1,11 @@
import { CommonModule } from '@angular/common';
import { Component, inject, OnInit } from '@angular/core';
import { AdminContactRequest, AdminOperationsService } from '../services/admin-operations.service';
import {
AdminContactRequest,
AdminContactRequestAttachment,
AdminContactRequestDetail,
AdminOperationsService
} from '../services/admin-operations.service';
@Component({
selector: 'app-admin-contact-requests',
@@ -13,7 +18,10 @@ export class AdminContactRequestsComponent implements OnInit {
private readonly adminOperationsService = inject(AdminOperationsService);
requests: AdminContactRequest[] = [];
selectedRequest: AdminContactRequestDetail | null = null;
selectedRequestId: string | null = null;
loading = false;
detailLoading = false;
errorMessage: string | null = null;
ngOnInit(): void {
@@ -26,6 +34,14 @@ export class AdminContactRequestsComponent implements OnInit {
this.adminOperationsService.getContactRequests().subscribe({
next: (requests) => {
this.requests = requests;
if (requests.length === 0) {
this.selectedRequest = null;
this.selectedRequestId = null;
} else if (this.selectedRequestId && requests.some(r => r.id === this.selectedRequestId)) {
this.openDetails(this.selectedRequestId);
} else {
this.openDetails(requests[0].id);
}
this.loading = false;
},
error: () => {
@@ -34,4 +50,73 @@ export class AdminContactRequestsComponent implements OnInit {
}
});
}
openDetails(requestId: string): void {
this.selectedRequestId = requestId;
this.detailLoading = true;
this.adminOperationsService.getContactRequestDetail(requestId).subscribe({
next: (detail) => {
this.selectedRequest = detail;
this.detailLoading = false;
},
error: () => {
this.detailLoading = false;
this.errorMessage = 'Impossibile caricare il dettaglio richiesta.';
}
});
}
isSelected(requestId: string): boolean {
return this.selectedRequestId === requestId;
}
downloadAttachment(attachment: AdminContactRequestAttachment): void {
if (!this.selectedRequest) {
return;
}
this.adminOperationsService.downloadContactRequestAttachment(this.selectedRequest.id, attachment.id).subscribe({
next: (blob) => this.downloadBlob(blob, attachment.originalFilename || `attachment-${attachment.id}`),
error: () => {
this.errorMessage = 'Download allegato non riuscito.';
}
});
}
formatFileSize(bytes?: number): string {
if (!bytes || bytes <= 0) {
return '-';
}
const units = ['B', 'KB', 'MB', 'GB'];
let value = bytes;
let unitIndex = 0;
while (value >= 1024 && unitIndex < units.length - 1) {
value /= 1024;
unitIndex += 1;
}
return `${value.toFixed(value >= 10 || unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
}
getStatusChipClass(status?: string): string {
const normalized = (status || '').trim().toUpperCase();
if (['PENDING', 'NEW', 'OPEN', 'IN_PROGRESS'].includes(normalized)) {
return 'chip-warning';
}
if (['DONE', 'COMPLETED', 'RESOLVED', 'CLOSED'].includes(normalized)) {
return 'chip-success';
}
if (['REJECTED', 'FAILED', 'ERROR', 'SPAM'].includes(normalized)) {
return 'chip-danger';
}
return 'chip-light';
}
private downloadBlob(blob: Blob, filename: string): void {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
window.URL.revokeObjectURL(url);
}
}