feat(back-end and front-end): calculator improvements
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
[rows]="priceBreakdownRows()"
|
||||
[total]="costBreakdown().total"
|
||||
[currency]="result().currency"
|
||||
[totalSuffix]="'*'"
|
||||
[totalLabelKey]="'CHECKOUT.TOTAL'"
|
||||
></app-price-breakdown>
|
||||
|
||||
@@ -68,6 +69,7 @@
|
||||
[ngModel]="item.quantity"
|
||||
(ngModelChange)="updateQuantity(i, $event)"
|
||||
(blur)="flushQuantityUpdate(i)"
|
||||
(keydown.enter)="flushQuantityUpdate(i)"
|
||||
class="qty-input"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import {
|
||||
Component,
|
||||
OnDestroy,
|
||||
input,
|
||||
output,
|
||||
signal,
|
||||
@@ -34,10 +33,9 @@ import { QuoteResult, QuoteItem } from '../../services/quote-estimator.service';
|
||||
templateUrl: './quote-result.component.html',
|
||||
styleUrl: './quote-result.component.scss',
|
||||
})
|
||||
export class QuoteResultComponent implements OnDestroy {
|
||||
export class QuoteResultComponent {
|
||||
readonly maxInputQuantity = 500;
|
||||
readonly directOrderLimit = 100;
|
||||
readonly quantityAutoRefreshMs = 2000;
|
||||
|
||||
result = input.required<QuoteResult>();
|
||||
recalculationRequired = input<boolean>(false);
|
||||
@@ -62,13 +60,10 @@ export class QuoteResultComponent implements OnDestroy {
|
||||
// Local mutable state for items to handle quantity changes
|
||||
items = signal<QuoteItem[]>([]);
|
||||
private lastSentQuantities = new Map<string, number>();
|
||||
private quantityTimers = new Map<string, ReturnType<typeof setTimeout>>();
|
||||
|
||||
constructor() {
|
||||
effect(
|
||||
() => {
|
||||
this.clearAllQuantityTimers();
|
||||
|
||||
// Initialize local items when result inputs change
|
||||
// We map to new objects to avoid mutating the input directly if it was a reference
|
||||
const nextItems = this.result().items.map((i) => ({ ...i }));
|
||||
@@ -84,17 +79,12 @@ export class QuoteResultComponent implements OnDestroy {
|
||||
);
|
||||
}
|
||||
|
||||
ngOnDestroy(): void {
|
||||
this.clearAllQuantityTimers();
|
||||
}
|
||||
|
||||
updateQuantity(index: number, newQty: number | string) {
|
||||
const normalizedQty = this.normalizeQuantity(newQty);
|
||||
if (normalizedQty === null) return;
|
||||
|
||||
const item = this.items()[index];
|
||||
if (!item) return;
|
||||
const key = item.id ?? item.fileName;
|
||||
|
||||
this.items.update((current) => {
|
||||
const updated = [...current];
|
||||
@@ -108,8 +98,6 @@ export class QuoteResultComponent implements OnDestroy {
|
||||
fileName: item.fileName,
|
||||
quantity: normalizedQty,
|
||||
});
|
||||
|
||||
this.scheduleQuantityRefresh(index, key);
|
||||
}
|
||||
|
||||
flushQuantityUpdate(index: number): void {
|
||||
@@ -117,7 +105,6 @@ export class QuoteResultComponent implements OnDestroy {
|
||||
if (!item) return;
|
||||
|
||||
const key = item.id ?? item.fileName;
|
||||
this.clearQuantityRefreshTimer(key);
|
||||
|
||||
const normalizedQty = this.normalizeQuantity(item.quantity);
|
||||
if (normalizedQty === null) return;
|
||||
@@ -213,27 +200,6 @@ export class QuoteResultComponent implements OnDestroy {
|
||||
return Math.min(qty, this.maxInputQuantity);
|
||||
}
|
||||
|
||||
private scheduleQuantityRefresh(index: number, key: string): void {
|
||||
this.clearQuantityRefreshTimer(key);
|
||||
const timer = setTimeout(() => {
|
||||
this.quantityTimers.delete(key);
|
||||
this.flushQuantityUpdate(index);
|
||||
}, this.quantityAutoRefreshMs);
|
||||
this.quantityTimers.set(key, timer);
|
||||
}
|
||||
|
||||
private clearQuantityRefreshTimer(key: string): void {
|
||||
const timer = this.quantityTimers.get(key);
|
||||
if (!timer) return;
|
||||
clearTimeout(timer);
|
||||
this.quantityTimers.delete(key);
|
||||
}
|
||||
|
||||
private clearAllQuantityTimers(): void {
|
||||
this.quantityTimers.forEach((timer) => clearTimeout(timer));
|
||||
this.quantityTimers.clear();
|
||||
}
|
||||
|
||||
getItemDifferenceLabel(fileName: string, materialCode?: string): string {
|
||||
const differences =
|
||||
this.itemSettingsDiffByFileName()[fileName]?.differences || [];
|
||||
|
||||
@@ -447,6 +447,7 @@ export class QuoteEstimatorService {
|
||||
|
||||
return {
|
||||
complexityMode: request.mode === 'easy' ? 'BASIC' : 'ADVANCED',
|
||||
quantity: this.normalizeQuantity(item.quantity),
|
||||
material: String(item.material || request.material || 'PLA'),
|
||||
color: item.color || '#FFFFFF',
|
||||
filamentVariantId: item.filamentVariantId,
|
||||
@@ -475,6 +476,14 @@ export class QuoteEstimatorService {
|
||||
};
|
||||
}
|
||||
|
||||
private normalizeQuantity(value: number | undefined): number {
|
||||
const numeric = Number(value);
|
||||
if (!Number.isFinite(numeric) || numeric < 1) {
|
||||
return 1;
|
||||
}
|
||||
return Math.floor(numeric);
|
||||
}
|
||||
|
||||
private normalizeQuality(value: string | undefined): string {
|
||||
const normalized = String(value || 'standard')
|
||||
.trim()
|
||||
|
||||
Reference in New Issue
Block a user