fix(front-end): 3d view only for stl
This commit is contained in:
@@ -1,14 +1,14 @@
|
|||||||
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
<form [formGroup]="form" (ngSubmit)="onSubmit()">
|
||||||
|
|
||||||
<div class="section">
|
<div class="section">
|
||||||
@if (selectedFile()) {
|
@if (selectedFile()) {
|
||||||
<div class="viewer-wrapper">
|
<div class="viewer-wrapper">
|
||||||
@if (isStepFile(selectedFile())) {
|
@if (!isStepFile(selectedFile())) {
|
||||||
<div class="step-warning">
|
<div class="step-warning">
|
||||||
<p>{{ 'CALC.STEP_WARNING' | translate }}</p>
|
<p>{{ 'CALC.STEP_WARNING' | translate }}</p>
|
||||||
</div>
|
</div>
|
||||||
} @else {
|
} @else {
|
||||||
<app-stl-viewer
|
<app-stl-viewer
|
||||||
[file]="selectedFile()"
|
[file]="selectedFile()"
|
||||||
[color]="getSelectedFileColor()">
|
[color]="getSelectedFileColor()">
|
||||||
</app-stl-viewer>
|
</app-stl-viewer>
|
||||||
@@ -16,11 +16,11 @@
|
|||||||
<!-- Close button removed as requested -->
|
<!-- Close button removed as requested -->
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<!-- Initial Dropzone (Visible only when no files) -->
|
<!-- Initial Dropzone (Visible only when no files) -->
|
||||||
@if (items().length === 0) {
|
@if (items().length === 0) {
|
||||||
<app-dropzone
|
<app-dropzone
|
||||||
[label]="'CALC.UPLOAD_LABEL' | translate"
|
[label]="'CALC.UPLOAD_LABEL' | translate"
|
||||||
[subtext]="'CALC.UPLOAD_SUB' | translate"
|
[subtext]="'CALC.UPLOAD_SUB' | translate"
|
||||||
[accept]="acceptedFormats"
|
[accept]="acceptedFormats"
|
||||||
[multiple]="true"
|
[multiple]="true"
|
||||||
@@ -36,14 +36,14 @@
|
|||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<span class="file-name" [title]="item.file.name">{{ item.file.name }}</span>
|
<span class="file-name" [title]="item.file.name">{{ item.file.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="card-body">
|
<div class="card-body">
|
||||||
<div class="card-controls">
|
<div class="card-controls">
|
||||||
<div class="qty-group">
|
<div class="qty-group">
|
||||||
<label>{{ 'CALC.QTY_SHORT' | translate }}</label>
|
<label>{{ 'CALC.QTY_SHORT' | translate }}</label>
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
min="1"
|
min="1"
|
||||||
[value]="item.quantity"
|
[value]="item.quantity"
|
||||||
(change)="updateItemQuantity(i, $event)"
|
(change)="updateItemQuantity(i, $event)"
|
||||||
class="qty-input"
|
class="qty-input"
|
||||||
@@ -52,14 +52,14 @@
|
|||||||
|
|
||||||
<div class="color-group">
|
<div class="color-group">
|
||||||
<label>{{ 'CALC.COLOR_LABEL' | translate }}</label>
|
<label>{{ 'CALC.COLOR_LABEL' | translate }}</label>
|
||||||
<app-color-selector
|
<app-color-selector
|
||||||
[selectedColor]="item.color"
|
[selectedColor]="item.color"
|
||||||
[variants]="currentMaterialVariants()"
|
[variants]="currentMaterialVariants()"
|
||||||
(colorSelected)="updateItemColor(i, $event)">
|
(colorSelected)="updateItemColor(i, $event)">
|
||||||
</app-color-selector>
|
</app-color-selector>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<button type="button" class="btn-remove" (click)="removeItem(i); $event.stopPropagation()" title="Remove file">
|
<button type="button" class="btn-remove" (click)="removeItem(i); $event.stopPropagation()" title="Remove file">
|
||||||
X
|
X
|
||||||
</button>
|
</button>
|
||||||
@@ -67,17 +67,17 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- "Add Files" Button (Visible only when files exist) -->
|
<!-- "Add Files" Button (Visible only when files exist) -->
|
||||||
<div class="add-more-container">
|
<div class="add-more-container">
|
||||||
<input #additionalInput type="file" [accept]="acceptedFormats" multiple hidden (change)="onAdditionalFilesSelected($event)">
|
<input #additionalInput type="file" [accept]="acceptedFormats" multiple hidden (change)="onAdditionalFilesSelected($event)">
|
||||||
|
|
||||||
<button type="button" class="btn-add-more" (click)="additionalInput.click()">
|
<button type="button" class="btn-add-more" (click)="additionalInput.click()">
|
||||||
+ {{ 'CALC.ADD_FILES' | translate }}
|
+ {{ 'CALC.ADD_FILES' | translate }}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (items().length === 0 && form.get('itemsTouched')?.value) {
|
@if (items().length === 0 && form.get('itemsTouched')?.value) {
|
||||||
<div class="error-msg">{{ 'CALC.ERR_FILE_REQUIRED' | translate }}</div>
|
<div class="error-msg">{{ 'CALC.ERR_FILE_REQUIRED' | translate }}</div>
|
||||||
}
|
}
|
||||||
@@ -104,7 +104,7 @@
|
|||||||
></app-select>
|
></app-select>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Global quantity removed, now per item -->
|
<!-- Global quantity removed, now per item -->
|
||||||
|
|
||||||
@if (mode() === 'advanced') {
|
@if (mode() === 'advanced') {
|
||||||
@@ -128,7 +128,7 @@
|
|||||||
type="number"
|
type="number"
|
||||||
[label]="'CALC.INFILL' | translate"
|
[label]="'CALC.INFILL' | translate"
|
||||||
></app-input>
|
></app-input>
|
||||||
|
|
||||||
<div class="checkbox-row">
|
<div class="checkbox-row">
|
||||||
<input type="checkbox" formControlName="supportEnabled" id="support">
|
<input type="checkbox" formControlName="supportEnabled" id="support">
|
||||||
<label for="support">{{ 'CALC.SUPPORT' | translate }}</label>
|
<label for="support">{{ 'CALC.SUPPORT' | translate }}</label>
|
||||||
@@ -153,9 +153,9 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<app-button
|
<app-button
|
||||||
type="submit"
|
type="submit"
|
||||||
[disabled]="items().length === 0 || loading()"
|
[disabled]="items().length === 0 || loading()"
|
||||||
[fullWidth]="true">
|
[fullWidth]="true">
|
||||||
{{ loading() ? (uploadProgress() < 100 ? ('CALC.UPLOADING' | translate) : ('CALC.PROCESSING' | translate)) : ('CALC.CALCULATE' | translate) }}
|
{{ loading() ? (uploadProgress() < 100 ? ('CALC.UPLOADING' | translate) : ('CALC.PROCESSING' | translate)) : ('CALC.CALCULATE' | translate) }}
|
||||||
</app-button>
|
</app-button>
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
private fb = inject(FormBuilder);
|
private fb = inject(FormBuilder);
|
||||||
|
|
||||||
form: FormGroup;
|
form: FormGroup;
|
||||||
|
|
||||||
items = signal<FormItem[]>([]);
|
items = signal<FormItem[]>([]);
|
||||||
selectedFile = signal<File | null>(null);
|
selectedFile = signal<File | null>(null);
|
||||||
|
|
||||||
@@ -44,13 +44,13 @@ export class UploadFormComponent implements OnInit {
|
|||||||
nozzleDiameters = signal<SimpleOption[]>([]);
|
nozzleDiameters = signal<SimpleOption[]>([]);
|
||||||
infillPatterns = signal<SimpleOption[]>([]);
|
infillPatterns = signal<SimpleOption[]>([]);
|
||||||
layerHeights = signal<SimpleOption[]>([]);
|
layerHeights = signal<SimpleOption[]>([]);
|
||||||
|
|
||||||
// Store full material options to lookup variants/colors if needed later
|
// Store full material options to lookup variants/colors if needed later
|
||||||
private fullMaterialOptions: MaterialOption[] = [];
|
private fullMaterialOptions: MaterialOption[] = [];
|
||||||
|
|
||||||
// Computed variants for valid material
|
// Computed variants for valid material
|
||||||
currentMaterialVariants = signal<VariantOption[]>([]);
|
currentMaterialVariants = signal<VariantOption[]>([]);
|
||||||
|
|
||||||
private updateVariants() {
|
private updateVariants() {
|
||||||
const matCode = this.form.get('material')?.value;
|
const matCode = this.form.get('material')?.value;
|
||||||
if (matCode && this.fullMaterialOptions.length > 0) {
|
if (matCode && this.fullMaterialOptions.length > 0) {
|
||||||
@@ -66,7 +66,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
isStepFile(file: File | null): boolean {
|
isStepFile(file: File | null): boolean {
|
||||||
if (!file) return false;
|
if (!file) return false;
|
||||||
const name = file.name.toLowerCase();
|
const name = file.name.toLowerCase();
|
||||||
return name.endsWith('.step') || name.endsWith('.stp');
|
return name.endsWith('.stl');
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -83,7 +83,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
infillPattern: ['grid'],
|
infillPattern: ['grid'],
|
||||||
supportEnabled: [false]
|
supportEnabled: [false]
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen to material changes to update variants
|
// Listen to material changes to update variants
|
||||||
this.form.get('material')?.valueChanges.subscribe(() => {
|
this.form.get('material')?.valueChanges.subscribe(() => {
|
||||||
this.updateVariants();
|
this.updateVariants();
|
||||||
@@ -95,7 +95,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
next: (options: OptionsResponse) => {
|
next: (options: OptionsResponse) => {
|
||||||
this.fullMaterialOptions = options.materials;
|
this.fullMaterialOptions = options.materials;
|
||||||
this.updateVariants(); // Trigger initial update
|
this.updateVariants(); // Trigger initial update
|
||||||
|
|
||||||
this.materials.set(options.materials.map(m => ({ label: m.label, value: m.code })));
|
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.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 })));
|
this.infillPatterns.set(options.infillPatterns.map(p => ({ label: p.label, value: p.id })));
|
||||||
@@ -194,7 +194,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
getSelectedFileColor(): string {
|
getSelectedFileColor(): string {
|
||||||
const file = this.selectedFile();
|
const file = this.selectedFile();
|
||||||
if (!file) return '#facf0a'; // Default
|
if (!file) return '#facf0a'; // Default
|
||||||
|
|
||||||
const item = this.items().find(i => i.file === file);
|
const item = this.items().find(i => i.file === file);
|
||||||
if (item) {
|
if (item) {
|
||||||
const vars = this.currentMaterialVariants();
|
const vars = this.currentMaterialVariants();
|
||||||
@@ -211,7 +211,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
const input = event.target as HTMLInputElement;
|
const input = event.target as HTMLInputElement;
|
||||||
let val = parseInt(input.value, 10);
|
let val = parseInt(input.value, 10);
|
||||||
if (isNaN(val) || val < 1) val = 1;
|
if (isNaN(val) || val < 1) val = 1;
|
||||||
|
|
||||||
this.items.update(current => {
|
this.items.update(current => {
|
||||||
const updated = [...current];
|
const updated = [...current];
|
||||||
updated[index] = { ...updated[index], quantity: val };
|
updated[index] = { ...updated[index], quantity: val };
|
||||||
@@ -255,33 +255,33 @@ export class UploadFormComponent implements OnInit {
|
|||||||
|
|
||||||
patchSettings(settings: any) {
|
patchSettings(settings: any) {
|
||||||
if (!settings) return;
|
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
|
// Session has: materialCode, etc. derived from QuoteSession entity properties
|
||||||
// We need to map them if names differ.
|
// We need to map them if names differ.
|
||||||
|
|
||||||
const patch: any = {};
|
const patch: any = {};
|
||||||
if (settings.materialCode) patch.material = settings.materialCode;
|
if (settings.materialCode) patch.material = settings.materialCode;
|
||||||
|
|
||||||
// Heuristic for Quality if not explicitly stored as "draft/standard/high"
|
// 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.
|
// QuoteSession entity does NOT store "quality" string directly, only layerHeight/infill.
|
||||||
// So we might need to deduce it or just set Custom/Advanced.
|
// So we might need to deduce it or just set Custom/Advanced.
|
||||||
// But for Easy mode, we want to show "Standard" etc.
|
// But for Easy mode, we want to show "Standard" etc.
|
||||||
|
|
||||||
// Actually, let's look at what we have in QuoteSession.
|
// 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?
|
// 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.
|
// Or if we stored "quality" in notes or separate field? We didn't.
|
||||||
|
|
||||||
// Let's try to reverse map or defaults.
|
// Let's try to reverse map or defaults.
|
||||||
if (settings.layerHeightMm) {
|
if (settings.layerHeightMm) {
|
||||||
if (settings.layerHeightMm >= 0.28) patch.quality = 'draft';
|
if (settings.layerHeightMm >= 0.28) patch.quality = 'draft';
|
||||||
else if (settings.layerHeightMm <= 0.12) patch.quality = 'high';
|
else if (settings.layerHeightMm <= 0.12) patch.quality = 'high';
|
||||||
else patch.quality = 'standard';
|
else patch.quality = 'standard';
|
||||||
|
|
||||||
patch.layerHeight = settings.layerHeightMm;
|
patch.layerHeight = settings.layerHeightMm;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.nozzleDiameterMm) patch.nozzleDiameter = settings.nozzleDiameterMm;
|
if (settings.nozzleDiameterMm) patch.nozzleDiameter = settings.nozzleDiameterMm;
|
||||||
if (settings.infillPercent) patch.infillDensity = settings.infillPercent;
|
if (settings.infillPercent) patch.infillDensity = settings.infillPercent;
|
||||||
if (settings.infillPattern) patch.infillPattern = settings.infillPattern;
|
if (settings.infillPattern) patch.infillPattern = settings.infillPattern;
|
||||||
@@ -294,7 +294,7 @@ export class UploadFormComponent implements OnInit {
|
|||||||
onSubmit() {
|
onSubmit() {
|
||||||
console.log('UploadFormComponent: onSubmit triggered');
|
console.log('UploadFormComponent: onSubmit triggered');
|
||||||
console.log('Form Valid:', this.form.valid, 'Items:', this.items().length);
|
console.log('Form Valid:', this.form.valid, 'Items:', this.items().length);
|
||||||
|
|
||||||
if (this.form.valid && this.items().length > 0) {
|
if (this.form.valid && this.items().length > 0) {
|
||||||
console.log('UploadFormComponent: Emitting submitRequest', this.form.value);
|
console.log('UploadFormComponent: Emitting submitRequest', this.form.value);
|
||||||
this.submitRequest.emit({
|
this.submitRequest.emit({
|
||||||
|
|||||||
@@ -102,7 +102,7 @@
|
|||||||
"PROCESSING": "Elaborazione...",
|
"PROCESSING": "Elaborazione...",
|
||||||
"NOTES_PLACEHOLDER": "Istruzioni specifiche...",
|
"NOTES_PLACEHOLDER": "Istruzioni specifiche...",
|
||||||
"SETUP_NOTE": "* Include {{cost}} Costo di Setup",
|
"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": {
|
"QUOTE": {
|
||||||
"PROCEED_ORDER": "Procedi con l'ordine",
|
"PROCEED_ORDER": "Procedi con l'ordine",
|
||||||
|
|||||||
Reference in New Issue
Block a user