398 lines
12 KiB
HTML
398 lines
12 KiB
HTML
<section class="admin-dashboard section-card">
|
|
<header class="dashboard-header section-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"
|
|
class="ui-button"
|
|
(click)="loadOrders()"
|
|
[disabled]="loading"
|
|
>
|
|
Aggiorna
|
|
</button>
|
|
</div>
|
|
</header>
|
|
|
|
<p class="error" *ngIf="errorMessage">{{ errorMessage }}</p>
|
|
|
|
<div class="workspace ui-split-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
|
|
class="ui-form-control"
|
|
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
|
|
class="ui-form-control"
|
|
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
|
|
class="ui-form-control"
|
|
id="order-status-filter"
|
|
[ngModel]="orderStatusFilter"
|
|
(ngModelChange)="onOrderStatusFilterChange($event)"
|
|
>
|
|
<option
|
|
*ngFor="let option of orderStatusFilterOptions"
|
|
[ngValue]="option"
|
|
>
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
<label class="toolbar-field" for="order-type-filter">
|
|
<span>Tipo ordine</span>
|
|
<select
|
|
class="ui-form-control"
|
|
id="order-type-filter"
|
|
[ngModel]="orderTypeFilter"
|
|
(ngModelChange)="onOrderTypeFilterChange($event)"
|
|
>
|
|
<option
|
|
*ngFor="let option of orderTypeFilterOptions"
|
|
[ngValue]="option"
|
|
>
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
</label>
|
|
</div>
|
|
<div class="table-wrap ui-table-wrap">
|
|
<table class="ui-data-table">
|
|
<thead>
|
|
<tr>
|
|
<th>Ordine</th>
|
|
<th>Tipo</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>
|
|
<span
|
|
class="order-type-badge"
|
|
[class.order-type-badge--mixed]="orderKind(order) === 'MIXED'"
|
|
>
|
|
{{ orderKindLabel(order) }}
|
|
</span>
|
|
</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="6">
|
|
Nessun ordine trovato per i filtri selezionati.
|
|
</td>
|
|
</tr>
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
</section>
|
|
|
|
<section class="detail-panel ui-detail-panel" *ngIf="selectedOrder">
|
|
<div class="detail-header">
|
|
<div class="detail-title-row">
|
|
<h2>Dettaglio ordine {{ selectedOrder.orderNumber }}</h2>
|
|
<span
|
|
class="order-type-badge"
|
|
[class.order-type-badge--mixed]="
|
|
orderKind(selectedOrder) === 'MIXED'
|
|
"
|
|
>
|
|
{{ orderKindLabel(selectedOrder) }}
|
|
</span>
|
|
</div>
|
|
<p class="order-uuid">
|
|
UUID:
|
|
<code
|
|
class="ui-code-pill"
|
|
[title]="selectedOrder.id"
|
|
[appCopyOnClick]="selectedOrder.id"
|
|
>{{ selectedOrder.id }}</code
|
|
>
|
|
</p>
|
|
<p *ngIf="detailLoading">Caricamento dettaglio...</p>
|
|
</div>
|
|
|
|
<div class="meta-grid ui-meta-grid">
|
|
<div class="ui-meta-item">
|
|
<strong>Cliente</strong><span>{{ selectedOrder.customerEmail }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Stato pagamento</strong
|
|
><span>{{ selectedOrder.paymentStatus || "PENDING" }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Stato ordine</strong><span>{{ selectedOrder.status }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Tipo ordine</strong
|
|
><span>{{ orderKindLabel(selectedOrder) }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<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
|
|
class="ui-form-control"
|
|
id="order-status"
|
|
[value]="selectedStatus"
|
|
(change)="onStatusChange($event)"
|
|
>
|
|
<option *ngFor="let option of orderStatusOptions" [value]="option">
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
<button
|
|
class="ui-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
|
|
class="ui-form-control"
|
|
id="payment-method"
|
|
[value]="selectedPaymentMethod"
|
|
(change)="onPaymentMethodChange($event)"
|
|
>
|
|
<option
|
|
*ngFor="let option of paymentMethodOptions"
|
|
[value]="option"
|
|
>
|
|
{{ option }}
|
|
</option>
|
|
</select>
|
|
<button
|
|
class="ui-button"
|
|
type="button"
|
|
(click)="updatePaymentMethod()"
|
|
[disabled]="confirmingPayment"
|
|
>
|
|
{{
|
|
confirmingPayment ? "Salvataggio..." : "Cambia metodo pagamento"
|
|
}}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="doc-actions">
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button--ghost"
|
|
(click)="downloadConfirmation()"
|
|
>
|
|
Scarica conferma + QR bill
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button--ghost"
|
|
(click)="downloadInvoice()"
|
|
>
|
|
Scarica fattura
|
|
</button>
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button--ghost"
|
|
(click)="openPrintDetails()"
|
|
[disabled]="!hasPrintItems(selectedOrder)"
|
|
>
|
|
Dettagli stampa
|
|
</button>
|
|
</div>
|
|
|
|
<div class="items">
|
|
<div class="item" *ngFor="let item of selectedOrder.items">
|
|
<div class="item-main">
|
|
<div class="item-heading">
|
|
<p class="file-name">
|
|
<strong>{{ itemDisplayName(item) }}</strong>
|
|
</p>
|
|
<span class="item-kind-badge">
|
|
{{ isShopItem(item) ? "Shop" : "Calcolatore" }}
|
|
</span>
|
|
</div>
|
|
<p class="item-meta">
|
|
<span>Qta: {{ item.quantity }}</span>
|
|
<span *ngIf="showItemMaterial(item)">
|
|
Materiale: {{ getItemMaterialLabel(item) }}
|
|
</span>
|
|
<span *ngIf="itemVariantLabel(item) as variantLabel">
|
|
Variante: {{ variantLabel }}
|
|
</span>
|
|
<span class="item-meta__color">
|
|
Colore:
|
|
<span
|
|
class="color-swatch"
|
|
*ngIf="getItemColorHex(item) as colorHex"
|
|
[style.background-color]="colorHex"
|
|
></span>
|
|
<span>
|
|
{{ getItemColorLabel(item) }}
|
|
<ng-container
|
|
*ngIf="getItemColorCodeSuffix(item) as colorCode"
|
|
>
|
|
({{ colorCode }})
|
|
</ng-container>
|
|
</span>
|
|
</span>
|
|
</p>
|
|
<p class="item-tech" *ngIf="showItemPrintDetails(item)">
|
|
Nozzle: {{ item.nozzleDiameterMm ?? "-" }} mm | Layer:
|
|
{{ item.layerHeightMm ?? "-" }} mm | Infill:
|
|
{{ item.infillPercent ?? "-" }}% | Supporti:
|
|
{{ formatSupports(item.supportsEnabled) }}
|
|
</p>
|
|
<p class="item-total">
|
|
Riga:
|
|
{{ item.lineTotalChf | currency: "CHF" : "symbol" : "1.2-2" }}
|
|
</p>
|
|
</div>
|
|
<div class="item-actions">
|
|
<button
|
|
type="button"
|
|
class="ui-button ui-button--ghost"
|
|
(click)="
|
|
downloadItemFile(
|
|
item.id,
|
|
item.originalFilename || itemDisplayName(item)
|
|
)
|
|
"
|
|
>
|
|
{{ downloadItemLabel(item) }}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
<section
|
|
class="detail-panel ui-detail-panel ui-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="ui-button ui-button--ghost close-btn"
|
|
(click)="closePrintDetails()"
|
|
>
|
|
Chiudi
|
|
</button>
|
|
</header>
|
|
|
|
<div class="modal-grid ui-meta-grid">
|
|
<div class="ui-meta-item">
|
|
<strong>Qualità</strong
|
|
><span>{{ getQualityLabel(selectedOrder.printLayerHeightMm) }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Materiale</strong
|
|
><span>{{ selectedOrder.printMaterialCode || "-" }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Layer height</strong
|
|
><span>{{ selectedOrder.printLayerHeightMm || "-" }} mm</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Nozzle</strong
|
|
><span>{{ selectedOrder.printNozzleDiameterMm || "-" }} mm</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Infill pattern</strong
|
|
><span>{{ selectedOrder.printInfillPattern || "-" }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<strong>Infill %</strong
|
|
><span>{{ selectedOrder.printInfillPercent ?? "-" }}</span>
|
|
</div>
|
|
<div class="ui-meta-item">
|
|
<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 printItems(selectedOrder)"
|
|
>
|
|
<span class="filename">{{ item.originalFilename }}</span>
|
|
<span class="file-color">
|
|
{{ getItemMaterialLabel(item) }} | Colore:
|
|
{{ getItemColorLabel(item) }} | {{ item.nozzleDiameterMm ?? "-" }} mm
|
|
| {{ item.layerHeightMm ?? "-" }} mm |
|
|
{{ item.infillPercent ?? "-" }}% | {{ item.infillPattern || "-" }} |
|
|
{{ formatSupportsState(item.supportsEnabled) }}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|