feat(back-end and front-end): back-office
This commit is contained in:
@@ -1,30 +1,39 @@
|
||||
import { CommonModule } from '@angular/common';
|
||||
import { Component, inject, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute, Router } from '@angular/router';
|
||||
import { AdminAuthService } from '../services/admin-auth.service';
|
||||
import { FormsModule } from '@angular/forms';
|
||||
import { AdminOrder, AdminOrdersService } from '../services/admin-orders.service';
|
||||
|
||||
const SUPPORTED_LANGS = new Set(['it', 'en', 'de', 'fr']);
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-dashboard',
|
||||
standalone: true,
|
||||
imports: [CommonModule],
|
||||
imports: [CommonModule, FormsModule],
|
||||
templateUrl: './admin-dashboard.component.html',
|
||||
styleUrl: './admin-dashboard.component.scss'
|
||||
})
|
||||
export class AdminDashboardComponent implements OnInit {
|
||||
private readonly adminOrdersService = inject(AdminOrdersService);
|
||||
private readonly adminAuthService = inject(AdminAuthService);
|
||||
private readonly route = inject(ActivatedRoute);
|
||||
private readonly router = inject(Router);
|
||||
|
||||
orders: AdminOrder[] = [];
|
||||
filteredOrders: AdminOrder[] = [];
|
||||
selectedOrder: AdminOrder | null = null;
|
||||
selectedStatus = '';
|
||||
selectedPaymentMethod = 'OTHER';
|
||||
orderSearchTerm = '';
|
||||
showPrintDetails = false;
|
||||
loading = false;
|
||||
detailLoading = false;
|
||||
confirmingOrderId: string | null = null;
|
||||
confirmingPayment = false;
|
||||
updatingStatus = false;
|
||||
errorMessage: string | null = null;
|
||||
readonly orderStatusOptions = [
|
||||
'PENDING_PAYMENT',
|
||||
'PAID',
|
||||
'IN_PRODUCTION',
|
||||
'SHIPPED',
|
||||
'COMPLETED',
|
||||
'CANCELLED'
|
||||
];
|
||||
readonly paymentMethodOptions = ['TWINT', 'BANK_TRANSFER', 'CARD', 'CASH', 'OTHER'];
|
||||
|
||||
ngOnInit(): void {
|
||||
this.loadOrders();
|
||||
@@ -36,6 +45,22 @@ export class AdminDashboardComponent implements OnInit {
|
||||
this.adminOrdersService.listOrders().subscribe({
|
||||
next: (orders) => {
|
||||
this.orders = orders;
|
||||
this.refreshFilteredOrders();
|
||||
|
||||
if (!this.selectedOrder && this.filteredOrders.length > 0) {
|
||||
this.openDetails(this.filteredOrders[0].id);
|
||||
} else if (this.selectedOrder) {
|
||||
const exists = orders.find(order => order.id === this.selectedOrder?.id);
|
||||
const selectedIsVisible = this.filteredOrders.some(order => order.id === this.selectedOrder?.id);
|
||||
if (exists && selectedIsVisible) {
|
||||
this.openDetails(exists.id);
|
||||
} else if (this.filteredOrders.length > 0) {
|
||||
this.openDetails(this.filteredOrders[0].id);
|
||||
} else {
|
||||
this.selectedOrder = null;
|
||||
this.selectedStatus = '';
|
||||
}
|
||||
}
|
||||
this.loading = false;
|
||||
},
|
||||
error: () => {
|
||||
@@ -45,11 +70,28 @@ export class AdminDashboardComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
onSearchChange(value: string): void {
|
||||
this.orderSearchTerm = value;
|
||||
this.refreshFilteredOrders();
|
||||
|
||||
if (this.filteredOrders.length === 0) {
|
||||
this.selectedOrder = null;
|
||||
this.selectedStatus = '';
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.selectedOrder || !this.filteredOrders.some(order => order.id === this.selectedOrder?.id)) {
|
||||
this.openDetails(this.filteredOrders[0].id);
|
||||
}
|
||||
}
|
||||
|
||||
openDetails(orderId: string): void {
|
||||
this.detailLoading = true;
|
||||
this.adminOrdersService.getOrder(orderId).subscribe({
|
||||
next: (order) => {
|
||||
this.selectedOrder = order;
|
||||
this.selectedStatus = order.status;
|
||||
this.selectedPaymentMethod = order.paymentMethod || 'OTHER';
|
||||
this.detailLoading = false;
|
||||
},
|
||||
error: () => {
|
||||
@@ -59,41 +101,156 @@ export class AdminDashboardComponent implements OnInit {
|
||||
});
|
||||
}
|
||||
|
||||
confirmPayment(orderId: string): void {
|
||||
this.confirmingOrderId = orderId;
|
||||
this.adminOrdersService.confirmPayment(orderId).subscribe({
|
||||
confirmPayment(): void {
|
||||
if (!this.selectedOrder || this.confirmingPayment) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.confirmingPayment = true;
|
||||
this.adminOrdersService.confirmPayment(this.selectedOrder.id, this.selectedPaymentMethod).subscribe({
|
||||
next: (updatedOrder) => {
|
||||
this.confirmingOrderId = null;
|
||||
this.orders = this.orders.map((order) => order.id === updatedOrder.id ? updatedOrder : order);
|
||||
if (this.selectedOrder?.id === updatedOrder.id) {
|
||||
this.selectedOrder = updatedOrder;
|
||||
}
|
||||
this.confirmingPayment = false;
|
||||
this.applyOrderUpdate(updatedOrder);
|
||||
},
|
||||
error: () => {
|
||||
this.confirmingOrderId = null;
|
||||
this.confirmingPayment = false;
|
||||
this.errorMessage = 'Conferma pagamento non riuscita.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
logout(): void {
|
||||
this.adminAuthService.logout().subscribe({
|
||||
next: () => {
|
||||
void this.router.navigate(['/', this.resolveLang(), 'admin', 'login']);
|
||||
updateStatus(): void {
|
||||
if (!this.selectedOrder || this.updatingStatus || !this.selectedStatus.trim()) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updatingStatus = true;
|
||||
this.adminOrdersService.updateOrderStatus(this.selectedOrder.id, {
|
||||
status: this.selectedStatus.trim()
|
||||
}).subscribe({
|
||||
next: (updatedOrder) => {
|
||||
this.updatingStatus = false;
|
||||
this.applyOrderUpdate(updatedOrder);
|
||||
},
|
||||
error: () => {
|
||||
void this.router.navigate(['/', this.resolveLang(), 'admin', 'login']);
|
||||
this.updatingStatus = false;
|
||||
this.errorMessage = 'Aggiornamento stato ordine non riuscito.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private resolveLang(): string {
|
||||
for (const level of this.route.pathFromRoot) {
|
||||
const lang = level.snapshot.paramMap.get('lang');
|
||||
if (lang && SUPPORTED_LANGS.has(lang)) {
|
||||
return lang;
|
||||
}
|
||||
downloadItemFile(itemId: string, filename: string): void {
|
||||
if (!this.selectedOrder) {
|
||||
return;
|
||||
}
|
||||
return 'it';
|
||||
|
||||
this.adminOrdersService.downloadOrderItemFile(this.selectedOrder.id, itemId).subscribe({
|
||||
next: (blob) => {
|
||||
this.downloadBlob(blob, filename || `order-item-${itemId}`);
|
||||
},
|
||||
error: () => {
|
||||
this.errorMessage = 'Download file non riuscito.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
downloadConfirmation(): void {
|
||||
if (!this.selectedOrder) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.adminOrdersService.downloadOrderConfirmation(this.selectedOrder.id).subscribe({
|
||||
next: (blob) => {
|
||||
this.downloadBlob(blob, `conferma-${this.selectedOrder?.orderNumber || this.selectedOrder?.id}.pdf`);
|
||||
},
|
||||
error: () => {
|
||||
this.errorMessage = 'Download conferma ordine non riuscito.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
downloadInvoice(): void {
|
||||
if (!this.selectedOrder) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.adminOrdersService.downloadOrderInvoice(this.selectedOrder.id).subscribe({
|
||||
next: (blob) => {
|
||||
this.downloadBlob(blob, `fattura-${this.selectedOrder?.orderNumber || this.selectedOrder?.id}.pdf`);
|
||||
},
|
||||
error: () => {
|
||||
this.errorMessage = 'Download fattura non riuscito.';
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onStatusChange(event: Event): void {
|
||||
const value = (event.target as HTMLSelectElement | null)?.value ?? '';
|
||||
this.selectedStatus = value;
|
||||
}
|
||||
|
||||
onPaymentMethodChange(event: Event): void {
|
||||
const value = (event.target as HTMLSelectElement | null)?.value ?? 'OTHER';
|
||||
this.selectedPaymentMethod = value;
|
||||
}
|
||||
|
||||
openPrintDetails(): void {
|
||||
this.showPrintDetails = true;
|
||||
}
|
||||
|
||||
closePrintDetails(): void {
|
||||
this.showPrintDetails = false;
|
||||
}
|
||||
|
||||
getQualityLabel(layerHeight?: number): string {
|
||||
if (!layerHeight || Number.isNaN(layerHeight)) {
|
||||
return '-';
|
||||
}
|
||||
if (layerHeight <= 0.12) {
|
||||
return 'Alta';
|
||||
}
|
||||
if (layerHeight <= 0.2) {
|
||||
return 'Standard';
|
||||
}
|
||||
return 'Bozza';
|
||||
}
|
||||
|
||||
isHexColor(value?: string): boolean {
|
||||
return typeof value === 'string' && /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(value);
|
||||
}
|
||||
|
||||
isSelected(orderId: string): boolean {
|
||||
return this.selectedOrder?.id === orderId;
|
||||
}
|
||||
|
||||
private applyOrderUpdate(updatedOrder: AdminOrder): void {
|
||||
this.orders = this.orders.map((order) => order.id === updatedOrder.id ? updatedOrder : order);
|
||||
this.refreshFilteredOrders();
|
||||
this.selectedOrder = updatedOrder;
|
||||
this.selectedStatus = updatedOrder.status;
|
||||
this.selectedPaymentMethod = updatedOrder.paymentMethod || this.selectedPaymentMethod;
|
||||
}
|
||||
|
||||
private refreshFilteredOrders(): void {
|
||||
const term = this.orderSearchTerm.trim().toLowerCase();
|
||||
if (!term) {
|
||||
this.filteredOrders = [...this.orders];
|
||||
return;
|
||||
}
|
||||
|
||||
this.filteredOrders = this.orders.filter((order) => {
|
||||
const fullUuid = order.id.toLowerCase();
|
||||
const shortUuid = (order.orderNumber || '').toLowerCase();
|
||||
return fullUuid.includes(term) || shortUuid.includes(term);
|
||||
});
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user