feat(back-end and front-end) cad bill with order
This commit is contained in:
@@ -23,14 +23,16 @@
|
||||
<div
|
||||
class="mode-option"
|
||||
[class.active]="mode() === 'easy'"
|
||||
(click)="mode.set('easy')"
|
||||
[class.disabled]="cadSessionLocked()"
|
||||
(click)="!cadSessionLocked() && mode.set('easy')"
|
||||
>
|
||||
{{ "CALC.MODE_EASY" | translate }}
|
||||
</div>
|
||||
<div
|
||||
class="mode-option"
|
||||
[class.active]="mode() === 'advanced'"
|
||||
(click)="mode.set('advanced')"
|
||||
[class.disabled]="cadSessionLocked()"
|
||||
(click)="!cadSessionLocked() && mode.set('advanced')"
|
||||
>
|
||||
{{ "CALC.MODE_ADVANCED" | translate }}
|
||||
</div>
|
||||
@@ -39,6 +41,7 @@
|
||||
<app-upload-form
|
||||
#uploadForm
|
||||
[mode]="mode()"
|
||||
[lockedSettings]="cadSessionLocked()"
|
||||
[loading]="loading()"
|
||||
[uploadProgress]="uploadProgress()"
|
||||
(submitRequest)="onCalculate($event)"
|
||||
|
||||
@@ -80,6 +80,11 @@
|
||||
font-weight: 600;
|
||||
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
&.disabled {
|
||||
opacity: 0.6;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
}
|
||||
|
||||
.benefits {
|
||||
|
||||
@@ -48,6 +48,7 @@ export class CalculatorPageComponent implements OnInit {
|
||||
loading = signal(false);
|
||||
uploadProgress = signal(0);
|
||||
result = signal<QuoteResult | null>(null);
|
||||
cadSessionLocked = signal(false);
|
||||
error = signal<boolean>(false);
|
||||
errorKey = signal<string>('CALC.ERROR_GENERIC');
|
||||
isZeroQuoteError = computed(
|
||||
@@ -100,6 +101,8 @@ export class CalculatorPageComponent implements OnInit {
|
||||
this.error.set(false);
|
||||
this.errorKey.set('CALC.ERROR_GENERIC');
|
||||
this.result.set(result);
|
||||
const isCadSession = data?.session?.status === 'CAD_ACTIVE';
|
||||
this.cadSessionLocked.set(isCadSession);
|
||||
this.step.set('quote');
|
||||
|
||||
// 2. Determine Mode (Heuristic)
|
||||
@@ -206,6 +209,7 @@ export class CalculatorPageComponent implements OnInit {
|
||||
this.error.set(false);
|
||||
this.errorKey.set('CALC.ERROR_GENERIC');
|
||||
this.result.set(null);
|
||||
this.cadSessionLocked.set(false);
|
||||
this.orderSuccess.set(false);
|
||||
|
||||
// Auto-scroll on mobile to make analysis visible
|
||||
@@ -270,10 +274,10 @@ export class CalculatorPageComponent implements OnInit {
|
||||
onProceed() {
|
||||
const res = this.result();
|
||||
if (res && res.sessionId) {
|
||||
this.router.navigate(
|
||||
['/', this.languageService.selectedLang(), 'checkout'],
|
||||
{ queryParams: { session: res.sessionId } },
|
||||
);
|
||||
const segments = this.cadSessionLocked()
|
||||
? ['/', this.languageService.selectedLang(), 'checkout', 'cad']
|
||||
: ['/', this.languageService.selectedLang(), 'checkout'];
|
||||
this.router.navigate(segments, { queryParams: { session: res.sessionId } });
|
||||
} else {
|
||||
console.error('No session ID found in quote result');
|
||||
// Fallback or error handling
|
||||
@@ -343,6 +347,7 @@ export class CalculatorPageComponent implements OnInit {
|
||||
onNewQuote() {
|
||||
this.step.set('upload');
|
||||
this.result.set(null);
|
||||
this.cadSessionLocked.set(false);
|
||||
this.orderSuccess.set(false);
|
||||
this.mode.set('easy'); // Reset to default
|
||||
}
|
||||
|
||||
@@ -28,6 +28,12 @@
|
||||
: { cost: (result().setupCost | currency: result().currency) }
|
||||
}}</small
|
||||
><br />
|
||||
@if ((result().cadTotal || 0) > 0) {
|
||||
<small class="shipping-note" style="color: #666">
|
||||
Servizio CAD: {{ result().cadTotal | currency: result().currency }}
|
||||
</small>
|
||||
<br />
|
||||
}
|
||||
<small class="shipping-note" style="color: #666">{{
|
||||
"CALC.SHIPPING_NOTE" | translate
|
||||
}}</small>
|
||||
|
||||
@@ -120,8 +120,9 @@ export class QuoteResultComponent implements OnDestroy {
|
||||
totals = computed(() => {
|
||||
const currentItems = this.items();
|
||||
const setup = this.result().setupCost;
|
||||
const cad = this.result().cadTotal || 0;
|
||||
|
||||
let price = setup;
|
||||
let price = setup + cad;
|
||||
let time = 0;
|
||||
let weight = 0;
|
||||
|
||||
|
||||
@@ -118,6 +118,12 @@
|
||||
</div>
|
||||
|
||||
<div class="grid">
|
||||
@if (lockedSettings()) {
|
||||
<p class="upload-privacy-note">
|
||||
Parametri stampa bloccati per sessione CAD: materiale, nozzle, layer,
|
||||
infill e supporti sono definiti dal back-office.
|
||||
</p>
|
||||
}
|
||||
<app-select
|
||||
formControlName="material"
|
||||
[label]="'CALC.MATERIAL' | translate"
|
||||
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
signal,
|
||||
OnInit,
|
||||
inject,
|
||||
effect,
|
||||
} from '@angular/core';
|
||||
import { CommonModule } from '@angular/common';
|
||||
import {
|
||||
@@ -57,6 +58,7 @@ interface FormItem {
|
||||
})
|
||||
export class UploadFormComponent implements OnInit {
|
||||
mode = input<'easy' | 'advanced'>('easy');
|
||||
lockedSettings = input<boolean>(false);
|
||||
loading = input<boolean>(false);
|
||||
uploadProgress = input<number>(0);
|
||||
submitRequest = output<QuoteRequest>();
|
||||
@@ -139,6 +141,10 @@ export class UploadFormComponent implements OnInit {
|
||||
if (this.mode() !== 'easy' || this.isPatchingSettings) return;
|
||||
this.applyAdvancedPresetFromQuality(quality);
|
||||
});
|
||||
|
||||
effect(() => {
|
||||
this.applySettingsLock(this.lockedSettings());
|
||||
});
|
||||
}
|
||||
|
||||
private applyAdvancedPresetFromQuality(quality: string | null | undefined) {
|
||||
@@ -520,7 +526,7 @@ export class UploadFormComponent implements OnInit {
|
||||
this.form.value,
|
||||
);
|
||||
this.submitRequest.emit({
|
||||
...this.form.value,
|
||||
...this.form.getRawValue(),
|
||||
items: this.items(), // Pass the items array explicitly AFTER form value to prevent overwrite
|
||||
mode: this.mode(),
|
||||
});
|
||||
@@ -554,4 +560,26 @@ export class UploadFormComponent implements OnInit {
|
||||
private normalizeFileName(fileName: string): string {
|
||||
return (fileName || '').split(/[\\/]/).pop()?.trim().toLowerCase() ?? '';
|
||||
}
|
||||
|
||||
private applySettingsLock(locked: boolean): void {
|
||||
const controlsToLock = [
|
||||
'material',
|
||||
'quality',
|
||||
'nozzleDiameter',
|
||||
'infillPattern',
|
||||
'layerHeight',
|
||||
'infillDensity',
|
||||
'supportEnabled',
|
||||
];
|
||||
|
||||
controlsToLock.forEach((name) => {
|
||||
const control = this.form.get(name);
|
||||
if (!control) return;
|
||||
if (locked) {
|
||||
control.disable({ emitEvent: false });
|
||||
} else {
|
||||
control.enable({ emitEvent: false });
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,8 @@ export interface QuoteResult {
|
||||
items: QuoteItem[];
|
||||
setupCost: number;
|
||||
globalMachineCost: number;
|
||||
cadHours?: number;
|
||||
cadTotal?: number;
|
||||
currency: string;
|
||||
totalPrice: number;
|
||||
totalTimeHours: number;
|
||||
@@ -463,6 +465,8 @@ export class QuoteEstimatorService {
|
||||
})),
|
||||
setupCost: session.setupCostChf || 0,
|
||||
globalMachineCost: sessionData.globalMachineCostChf || 0,
|
||||
cadHours: session.cadHours || 0,
|
||||
cadTotal: sessionData.cadTotalChf || 0,
|
||||
currency: 'CHF', // Fixed for now
|
||||
totalPrice:
|
||||
(sessionData.itemsTotalChf || 0) +
|
||||
|
||||
Reference in New Issue
Block a user