fix(front-end): 3d view only for stl
All checks were successful
Build, Test and Deploy / test-backend (push) Successful in 33s
Build, Test and Deploy / build-and-push (push) Successful in 21s
Build, Test and Deploy / deploy (push) Successful in 8s

This commit is contained in:
2026-02-24 13:12:29 +01:00
parent a6eae757c5
commit 4ddd33662d
3 changed files with 41 additions and 41 deletions

View File

@@ -1,14 +1,14 @@
<form [formGroup]="form" (ngSubmit)="onSubmit()">
<div class="section">
@if (selectedFile()) {
<div class="viewer-wrapper">
@if (isStepFile(selectedFile())) {
@if (!isStepFile(selectedFile())) {
<div class="step-warning">
<p>{{ 'CALC.STEP_WARNING' | translate }}</p>
</div>
} @else {
<app-stl-viewer
<app-stl-viewer
[file]="selectedFile()"
[color]="getSelectedFileColor()">
</app-stl-viewer>
@@ -16,11 +16,11 @@
<!-- Close button removed as requested -->
</div>
}
<!-- Initial Dropzone (Visible only when no files) -->
@if (items().length === 0) {
<app-dropzone
[label]="'CALC.UPLOAD_LABEL' | translate"
<app-dropzone
[label]="'CALC.UPLOAD_LABEL' | translate"
[subtext]="'CALC.UPLOAD_SUB' | translate"
[accept]="acceptedFormats"
[multiple]="true"
@@ -36,14 +36,14 @@
<div class="card-header">
<span class="file-name" [title]="item.file.name">{{ item.file.name }}</span>
</div>
<div class="card-body">
<div class="card-controls">
<div class="qty-group">
<label>{{ 'CALC.QTY_SHORT' | translate }}</label>
<input
type="number"
min="1"
<input
type="number"
min="1"
[value]="item.quantity"
(change)="updateItemQuantity(i, $event)"
class="qty-input"
@@ -52,14 +52,14 @@
<div class="color-group">
<label>{{ 'CALC.COLOR_LABEL' | translate }}</label>
<app-color-selector
[selectedColor]="item.color"
<app-color-selector
[selectedColor]="item.color"
[variants]="currentMaterialVariants()"
(colorSelected)="updateItemColor(i, $event)">
</app-color-selector>
</div>
</div>
<button type="button" class="btn-remove" (click)="removeItem(i); $event.stopPropagation()" title="Remove file">
X
</button>
@@ -67,17 +67,17 @@
</div>
}
</div>
<!-- "Add Files" Button (Visible only when files exist) -->
<div class="add-more-container">
<input #additionalInput type="file" [accept]="acceptedFormats" multiple hidden (change)="onAdditionalFilesSelected($event)">
<button type="button" class="btn-add-more" (click)="additionalInput.click()">
+ {{ 'CALC.ADD_FILES' | translate }}
</button>
</div>
}
@if (items().length === 0 && form.get('itemsTouched')?.value) {
<div class="error-msg">{{ 'CALC.ERR_FILE_REQUIRED' | translate }}</div>
}
@@ -104,7 +104,7 @@
></app-select>
}
</div>
<!-- Global quantity removed, now per item -->
@if (mode() === 'advanced') {
@@ -128,7 +128,7 @@
type="number"
[label]="'CALC.INFILL' | translate"
></app-input>
<div class="checkbox-row">
<input type="checkbox" formControlName="supportEnabled" id="support">
<label for="support">{{ 'CALC.SUPPORT' | translate }}</label>
@@ -153,9 +153,9 @@
</div>
}
<app-button
type="submit"
[disabled]="items().length === 0 || loading()"
<app-button
type="submit"
[disabled]="items().length === 0 || loading()"
[fullWidth]="true">
{{ loading() ? (uploadProgress() < 100 ? ('CALC.UPLOADING' | translate) : ('CALC.PROCESSING' | translate)) : ('CALC.CALCULATE' | translate) }}
</app-button>

View File

@@ -34,7 +34,7 @@ export class UploadFormComponent implements OnInit {
private fb = inject(FormBuilder);
form: FormGroup;
items = signal<FormItem[]>([]);
selectedFile = signal<File | null>(null);
@@ -44,13 +44,13 @@ export class UploadFormComponent implements OnInit {
nozzleDiameters = signal<SimpleOption[]>([]);
infillPatterns = signal<SimpleOption[]>([]);
layerHeights = signal<SimpleOption[]>([]);
// Store full material options to lookup variants/colors if needed later
private fullMaterialOptions: MaterialOption[] = [];
// Computed variants for valid material
currentMaterialVariants = signal<VariantOption[]>([]);
private updateVariants() {
const matCode = this.form.get('material')?.value;
if (matCode && this.fullMaterialOptions.length > 0) {
@@ -66,7 +66,7 @@ export class UploadFormComponent implements OnInit {
isStepFile(file: File | null): boolean {
if (!file) return false;
const name = file.name.toLowerCase();
return name.endsWith('.step') || name.endsWith('.stp');
return name.endsWith('.stl');
}
constructor() {
@@ -83,7 +83,7 @@ export class UploadFormComponent implements OnInit {
infillPattern: ['grid'],
supportEnabled: [false]
});
// Listen to material changes to update variants
this.form.get('material')?.valueChanges.subscribe(() => {
this.updateVariants();
@@ -95,7 +95,7 @@ export class UploadFormComponent implements OnInit {
next: (options: OptionsResponse) => {
this.fullMaterialOptions = options.materials;
this.updateVariants(); // Trigger initial update
this.materials.set(options.materials.map(m => ({ label: m.label, value: m.code })));
this.qualities.set(options.qualities.map(q => ({ label: q.label, value: q.id })));
this.infillPatterns.set(options.infillPatterns.map(p => ({ label: p.label, value: p.id })));
@@ -194,7 +194,7 @@ export class UploadFormComponent implements OnInit {
getSelectedFileColor(): string {
const file = this.selectedFile();
if (!file) return '#facf0a'; // Default
const item = this.items().find(i => i.file === file);
if (item) {
const vars = this.currentMaterialVariants();
@@ -211,7 +211,7 @@ export class UploadFormComponent implements OnInit {
const input = event.target as HTMLInputElement;
let val = parseInt(input.value, 10);
if (isNaN(val) || val < 1) val = 1;
this.items.update(current => {
const updated = [...current];
updated[index] = { ...updated[index], quantity: val };
@@ -255,33 +255,33 @@ export class UploadFormComponent implements OnInit {
patchSettings(settings: any) {
if (!settings) return;
// settings object matches keys in our form?
// settings object matches keys in our form?
// Session has: materialCode, etc. derived from QuoteSession entity properties
// We need to map them if names differ.
const patch: any = {};
if (settings.materialCode) patch.material = settings.materialCode;
// Heuristic for Quality if not explicitly stored as "draft/standard/high"
// But we stored it in session creation?
// But we stored it in session creation?
// QuoteSession entity does NOT store "quality" string directly, only layerHeight/infill.
// So we might need to deduce it or just set Custom/Advanced.
// But for Easy mode, we want to show "Standard" etc.
// Actually, let's look at what we have in QuoteSession.
// layerHeightMm, infillPercent, etc.
// layerHeightMm, infillPercent, etc.
// If we are in Easy mode, we might just set the "quality" dropdown to match approx?
// Or if we stored "quality" in notes or separate field? We didn't.
// Let's try to reverse map or defaults.
if (settings.layerHeightMm) {
if (settings.layerHeightMm >= 0.28) patch.quality = 'draft';
else if (settings.layerHeightMm <= 0.12) patch.quality = 'high';
else patch.quality = 'standard';
patch.layerHeight = settings.layerHeightMm;
}
if (settings.nozzleDiameterMm) patch.nozzleDiameter = settings.nozzleDiameterMm;
if (settings.infillPercent) patch.infillDensity = settings.infillPercent;
if (settings.infillPattern) patch.infillPattern = settings.infillPattern;
@@ -294,7 +294,7 @@ export class UploadFormComponent implements OnInit {
onSubmit() {
console.log('UploadFormComponent: onSubmit triggered');
console.log('Form Valid:', this.form.valid, 'Items:', this.items().length);
if (this.form.valid && this.items().length > 0) {
console.log('UploadFormComponent: Emitting submitRequest', this.form.value);
this.submitRequest.emit({

View File

@@ -102,7 +102,7 @@
"PROCESSING": "Elaborazione...",
"NOTES_PLACEHOLDER": "Istruzioni specifiche...",
"SETUP_NOTE": "* Include {{cost}} Costo di Setup",
"STEP_WARNING": "La visualizzazione 3D non è compatibile con i file STEP, ma il calcolatore funziona."
"STEP_WARNING": "La visualizzazione 3D non è compatibile con i file step e 3mf, ma il calcolatore funziona."
},
"QUOTE": {
"PROCEED_ORDER": "Procedi con l'ordine",