295 lines
9.1 KiB
HTML
295 lines
9.1 KiB
HTML
<section class="admin-dashboard">
|
|
<header class="dashboard-header">
|
|
<div>
|
|
<h1>Ordini</h1>
|
|
<p>Seleziona un ordine a sinistra e gestiscilo nel dettaglio a destra.</p>
|
|
</div>
|
|
<div class="header-actions">
|
|
<button type="button" (click)="loadOrders()" [disabled]="loading">
|
|
Aggiorna
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<p class="error" *ngIf="errorMessage">{{ errorMessage }}</p>
|
|
|
|
<div class="workspace" *ngIf="!loading; else loadingTpl">
|
|
<section class="list-panel">
|
|
<h2>Lista ordini</h2>
|
|
<div class="list-toolbar">
|
|
<label class="toolbar-field" for="order-search">
|
|
<span>Cerca UUID</span>
|
|
<input
|
|
id="order-search"
|
|
type="search"
|
|
[ngModel]="orderSearchTerm"
|
|
(ngModelChange)="onSearchChange($event)"
|
|
placeholder="UUID completo o prefisso (es. 738131d8)"
|
|
/>
|
|
</label>
|
|
<label class="toolbar-field" for="payment-status-filter">
|
|
<span>Stato pagamento</span>
|
|
<select
|
|
id="payment-status-filter"
|
|
[ngModel]="paymentStatusFilter"
|
|
(ngModelChange)="onPaymentStatusFilterChange($event)"
|
|
>
|
|
<option
|
|
*ngFor="let option of paymentStatusFilterOptions"
|
|
[ngValue]="option"
|
|
>
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label class="toolbar-field" for="order-status-filter">
|
|
<span>Stato ordine</span>
|
|
<select
|
|
id="order-status-filter"
|
|
[ngModel]="orderStatusFilter"
|
|
(ngModelChange)="onOrderStatusFilterChange($event)"
|
|
>
|
|
<option
|
|
*ngFor="let option of orderStatusFilterOptions"
|
|
[ngValue]="option"
|
|
>
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<div class="table-wrap">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Ordine</th>
|
|
<th>Email</th>
|
|
<th>Pagamento</th>
|
|
<th>Stato ordine</th>
|
|
<th>Totale</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
<tr
|
|
*ngFor="let order of filteredOrders"
|
|
[class.selected]="isSelected(order.id)"
|
|
(click)="openDetails(order.id)"
|
|
>
|
|
<td>{{ order.orderNumber }}</td>
|
|
<td>{{ order.customerEmail }}</td>
|
|
<td>{{ order.paymentStatus || "PENDING" }}</td>
|
|
<td>{{ order.status }}</td>
|
|
<td>
|
|
{{ order.totalChf | currency: "CHF" : "symbol" : "1.2-2" }}
|
|
</td>
|
|
</tr>
|
|
<tr class="no-results" *ngIf="filteredOrders.length === 0">
|
|
<td colspan="5">
|
|
Nessun ordine trovato per i filtri selezionati.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="detail-panel" *ngIf="selectedOrder">
|
|
<div class="detail-header">
|
|
<h2>Dettaglio ordine {{ selectedOrder.orderNumber }}</h2>
|
|
<p class="order-uuid">
|
|
UUID:
|
|
<code
|
|
[title]="selectedOrder.id"
|
|
[appCopyOnClick]="selectedOrder.id"
|
|
>{{ selectedOrder.id }}</code
|
|
>
|
|
</p>
|
|
<p *ngIf="detailLoading">Caricamento dettaglio...</p>
|
|
</div>
|
|
|
|
<div class="meta-grid">
|
|
<div>
|
|
<strong>Cliente</strong><span>{{ selectedOrder.customerEmail }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Stato pagamento</strong
|
|
><span>{{ selectedOrder.paymentStatus || "PENDING" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Stato ordine</strong><span>{{ selectedOrder.status }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Totale</strong
|
|
><span>{{
|
|
selectedOrder.totalChf | currency: "CHF" : "symbol" : "1.2-2"
|
|
}}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="actions-block">
|
|
<div class="status-editor">
|
|
<label for="order-status">Stato ordine</label>
|
|
<select
|
|
id="order-status"
|
|
[value]="selectedStatus"
|
|
(change)="onStatusChange($event)"
|
|
>
|
|
<option *ngFor="let option of orderStatusOptions" [value]="option">
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
<button
|
|
type="button"
|
|
(click)="updateStatus()"
|
|
[disabled]="updatingStatus"
|
|
>
|
|
{{ updatingStatus ? "Salvataggio..." : "Aggiorna stato" }}
|
|
</button>
|
|
</div>
|
|
|
|
<div class="status-editor">
|
|
<label for="payment-method">Metodo pagamento</label>
|
|
<select
|
|
id="payment-method"
|
|
[value]="selectedPaymentMethod"
|
|
(change)="onPaymentMethodChange($event)"
|
|
>
|
|
<option
|
|
*ngFor="let option of paymentMethodOptions"
|
|
[value]="option"
|
|
>
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
<button
|
|
type="button"
|
|
(click)="updatePaymentMethod()"
|
|
[disabled]="confirmingPayment"
|
|
>
|
|
{{
|
|
confirmingPayment ? "Salvataggio..." : "Cambia metodo pagamento"
|
|
}}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="doc-actions">
|
|
<button type="button" class="ghost" (click)="downloadConfirmation()">
|
|
Scarica conferma + QR bill
|
|
</button>
|
|
<button type="button" class="ghost" (click)="downloadInvoice()">
|
|
Scarica fattura
|
|
</button>
|
|
<button type="button" class="ghost" (click)="openPrintDetails()">
|
|
Dettagli stampa
|
|
</button>
|
|
</div>
|
|
|
|
<div class="items">
|
|
<div class="item" *ngFor="let item of selectedOrder.items">
|
|
<div class="item-main">
|
|
<p class="file-name">
|
|
<strong>{{ item.originalFilename }}</strong>
|
|
</p>
|
|
<p class="item-meta">
|
|
Qta: {{ item.quantity }} | Materiale:
|
|
{{ item.materialCode || "-" }} | Colore:
|
|
<span
|
|
class="color-swatch"
|
|
*ngIf="isHexColor(item.colorCode)"
|
|
[style.background-color]="item.colorCode"
|
|
></span>
|
|
<span>{{ item.colorCode || "-" }}</span>
|
|
| Nozzle: {{ item.nozzleDiameterMm ?? "-" }} mm | Layer:
|
|
{{ item.layerHeightMm ?? "-" }} mm | Infill:
|
|
{{ item.infillPercent ?? "-" }}% | Supporti:
|
|
{{ item.supportsEnabled ? "Sì" : "No" }}
|
|
| Riga:
|
|
{{ item.lineTotalChf | currency: "CHF" : "symbol" : "1.2-2" }}
|
|
</p>
|
|
</div>
|
|
<button
|
|
type="button"
|
|
class="ghost"
|
|
(click)="downloadItemFile(item.id, item.originalFilename)"
|
|
>
|
|
Scarica file
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="detail-panel empty" *ngIf="!selectedOrder">
|
|
<h2>Nessun ordine selezionato</h2>
|
|
<p>Seleziona un ordine dalla lista per vedere i dettagli.</p>
|
|
</section>
|
|
</div>
|
|
</section>
|
|
|
|
<ng-template #loadingTpl>
|
|
<p>Caricamento ordini...</p>
|
|
</ng-template>
|
|
|
|
<div
|
|
class="modal-backdrop"
|
|
*ngIf="showPrintDetails && selectedOrder"
|
|
(click)="closePrintDetails()"
|
|
>
|
|
<div class="modal-card" (click)="$event.stopPropagation()">
|
|
<header class="modal-header">
|
|
<h3>Dettagli stampa ordine {{ selectedOrder.orderNumber }}</h3>
|
|
<button
|
|
type="button"
|
|
class="ghost close-btn"
|
|
(click)="closePrintDetails()"
|
|
>
|
|
Chiudi
|
|
</button>
|
|
</header>
|
|
|
|
<div class="modal-grid">
|
|
<div>
|
|
<strong>Qualità</strong
|
|
><span>{{ getQualityLabel(selectedOrder.printLayerHeightMm) }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Materiale</strong
|
|
><span>{{ selectedOrder.printMaterialCode || "-" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Layer height</strong
|
|
><span>{{ selectedOrder.printLayerHeightMm || "-" }} mm</span>
|
|
</div>
|
|
<div>
|
|
<strong>Nozzle</strong
|
|
><span>{{ selectedOrder.printNozzleDiameterMm || "-" }} mm</span>
|
|
</div>
|
|
<div>
|
|
<strong>Infill pattern</strong
|
|
><span>{{ selectedOrder.printInfillPattern || "-" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Infill %</strong
|
|
><span>{{ selectedOrder.printInfillPercent ?? "-" }}</span>
|
|
</div>
|
|
<div>
|
|
<strong>Supporti</strong
|
|
><span>{{ selectedOrder.printSupportsEnabled ? "Sì" : "No" }}</span>
|
|
</div>
|
|
</div>
|
|
|
|
<h4>Parametri per file</h4>
|
|
<div class="file-color-list">
|
|
<div class="file-color-row" *ngFor="let item of selectedOrder.items">
|
|
<span class="filename">{{ item.originalFilename }}</span>
|
|
<span class="file-color">
|
|
{{ item.materialCode || "-" }} | {{ item.nozzleDiameterMm ?? "-" }} mm
|
|
| {{ item.layerHeightMm ?? "-" }} mm | {{ item.infillPercent ?? "-" }}%
|
|
| {{ item.infillPattern || "-" }} |
|
|
{{ item.supportsEnabled ? "Supporti ON" : "Supporti OFF" }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|