feat(web): update color selector
This commit is contained in:
@@ -72,11 +72,14 @@ export class CalculatorPageComponent {
|
||||
|
||||
details += `- File:\n`;
|
||||
req.items.forEach(item => {
|
||||
details += ` * ${item.file.name} (Qtà: ${item.quantity})\n`;
|
||||
details += ` * ${item.file.name} (Qtà: ${item.quantity}`;
|
||||
if (item.color) {
|
||||
details += `, Colore: ${item.color}`;
|
||||
}
|
||||
details += `)\n`;
|
||||
});
|
||||
|
||||
if (req.mode === 'advanced') {
|
||||
if (req.color) details += `- Colore: ${req.color}\n`;
|
||||
if (req.infillDensity) details += `- Infill: ${req.infillDensity}%\n`;
|
||||
}
|
||||
|
||||
|
||||
@@ -3,7 +3,10 @@
|
||||
<div class="section">
|
||||
@if (selectedFile()) {
|
||||
<div class="viewer-wrapper">
|
||||
<app-stl-viewer [file]="selectedFile()"></app-stl-viewer>
|
||||
<app-stl-viewer
|
||||
[file]="selectedFile()"
|
||||
[color]="getSelectedFileColor()">
|
||||
</app-stl-viewer>
|
||||
<!-- Close button removed as requested -->
|
||||
</div>
|
||||
}
|
||||
@@ -29,15 +32,25 @@
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="qty-group">
|
||||
<label>Qtà</label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
[value]="item.quantity"
|
||||
(change)="updateItemQuantity(i, $event)"
|
||||
class="qty-input"
|
||||
(click)="$event.stopPropagation()">
|
||||
<div class="card-controls">
|
||||
<div class="qty-group">
|
||||
<label>QTÀ</label>
|
||||
<input
|
||||
type="number"
|
||||
min="1"
|
||||
[value]="item.quantity"
|
||||
(change)="updateItemQuantity(i, $event)"
|
||||
class="qty-input"
|
||||
(click)="$event.stopPropagation()">
|
||||
</div>
|
||||
|
||||
<div class="color-group">
|
||||
<label>COLORE</label>
|
||||
<app-color-selector
|
||||
[selectedColor]="item.color"
|
||||
(colorSelected)="updateItemColor(i, $event)">
|
||||
</app-color-selector>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" class="btn-remove" (click)="removeItem(i); $event.stopPropagation()" title="Remove file">
|
||||
@@ -81,12 +94,6 @@
|
||||
|
||||
@if (mode() === 'advanced') {
|
||||
<div class="grid">
|
||||
<app-select
|
||||
formControlName="color"
|
||||
[label]="'CALC.COLOR' | translate"
|
||||
[options]="colors"
|
||||
></app-select>
|
||||
|
||||
<app-select
|
||||
formControlName="infillPattern"
|
||||
[label]="'CALC.PATTERN' | translate"
|
||||
|
||||
@@ -16,18 +16,18 @@
|
||||
/* Grid Layout for Files */
|
||||
.items-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
gap: var(--space-3);
|
||||
grid-template-columns: 1fr 1fr; /* Force 2 columns on mobile */
|
||||
gap: var(--space-2); /* Tighten gap for mobile */
|
||||
margin-top: var(--space-4);
|
||||
margin-bottom: var(--space-4);
|
||||
|
||||
@media(min-width: 640px) {
|
||||
grid-template-columns: 1fr 1fr;
|
||||
gap: var(--space-3);
|
||||
}
|
||||
}
|
||||
|
||||
.file-card {
|
||||
padding: var(--space-3);
|
||||
padding: var(--space-2); /* Reduced from space-3 */
|
||||
background: var(--color-neutral-100);
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-md);
|
||||
@@ -35,7 +35,9 @@
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--space-2);
|
||||
gap: 4px; /* Reduced gap */
|
||||
position: relative; /* For absolute positioning of remove btn */
|
||||
min-width: 0; /* Allow flex item to shrink below content size if needed */
|
||||
|
||||
&:hover { border-color: var(--color-neutral-300); }
|
||||
&.active {
|
||||
@@ -47,11 +49,13 @@
|
||||
|
||||
.card-header {
|
||||
overflow: hidden;
|
||||
padding-right: 25px; /* Adjusted */
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.file-name {
|
||||
font-weight: 500;
|
||||
font-size: 0.85rem;
|
||||
font-size: 0.8rem; /* Smaller font */
|
||||
color: var(--color-text);
|
||||
display: block;
|
||||
white-space: nowrap;
|
||||
@@ -61,34 +65,64 @@
|
||||
|
||||
.card-body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.qty-group {
|
||||
.card-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: var(--space-2);
|
||||
label { font-size: 0.75rem; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.5px; }
|
||||
align-items: flex-end; /* Align bottom of input and color circle */
|
||||
gap: 16px; /* Space between Qty and Color */
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.qty-group, .color-group {
|
||||
display: flex;
|
||||
flex-direction: column; /* Stack label and input */
|
||||
align-items: flex-start;
|
||||
gap: 0px;
|
||||
|
||||
label {
|
||||
font-size: 0.6rem;
|
||||
color: var(--color-text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
font-weight: 600;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.color-group {
|
||||
align-items: flex-start; /* Align label left */
|
||||
/* margin-right removed */
|
||||
|
||||
/* Override margin in selector for this context */
|
||||
::ng-deep .color-selector-container {
|
||||
margin-left: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.qty-input {
|
||||
width: 40px;
|
||||
padding: 2px 4px;
|
||||
width: 36px; /* Slightly smaller */
|
||||
padding: 1px 2px;
|
||||
border: 1px solid var(--color-border);
|
||||
border-radius: var(--radius-sm);
|
||||
text-align: center;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.85rem;
|
||||
background: white;
|
||||
height: 24px; /* Explicit height to match color circle somewhat */
|
||||
&:focus { outline: none; border-color: var(--color-brand); }
|
||||
}
|
||||
|
||||
.btn-remove {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
position: absolute;
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid transparent; // var(--color-border);
|
||||
background: transparent; // white;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: var(--color-text-muted);
|
||||
font-weight: bold;
|
||||
cursor: pointer;
|
||||
@@ -96,12 +130,11 @@
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transition: all 0.2s;
|
||||
font-size: 0.9rem;
|
||||
font-size: 0.8rem;
|
||||
|
||||
&:hover {
|
||||
background: var(--color-danger-100);
|
||||
color: var(--color-danger-500);
|
||||
border-color: var(--color-danger-200);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -7,17 +7,20 @@ import { AppSelectComponent } from '../../../../shared/components/app-select/app
|
||||
import { AppDropzoneComponent } from '../../../../shared/components/app-dropzone/app-dropzone.component';
|
||||
import { AppButtonComponent } from '../../../../shared/components/app-button/app-button.component';
|
||||
import { StlViewerComponent } from '../../../../shared/components/stl-viewer/stl-viewer.component';
|
||||
import { ColorSelectorComponent } from '../../../../shared/components/color-selector/color-selector.component';
|
||||
import { QuoteRequest } from '../../services/quote-estimator.service';
|
||||
import { getColorHex } from '../../../../core/constants/colors.const';
|
||||
|
||||
interface FormItem {
|
||||
file: File;
|
||||
quantity: number;
|
||||
color: string;
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'app-upload-form',
|
||||
standalone: true,
|
||||
imports: [CommonModule, ReactiveFormsModule, TranslateModule, AppInputComponent, AppSelectComponent, AppDropzoneComponent, AppButtonComponent, StlViewerComponent],
|
||||
imports: [CommonModule, ReactiveFormsModule, TranslateModule, AppInputComponent, AppSelectComponent, AppDropzoneComponent, AppButtonComponent, StlViewerComponent, ColorSelectorComponent],
|
||||
templateUrl: './upload-form.component.html',
|
||||
styleUrl: './upload-form.component.scss'
|
||||
})
|
||||
@@ -44,15 +47,6 @@ export class UploadFormComponent {
|
||||
{ label: 'Alta definizione', value: 'High' }
|
||||
];
|
||||
|
||||
colors = [
|
||||
{ label: 'Black', value: 'Black' },
|
||||
{ label: 'White', value: 'White' },
|
||||
{ label: 'Gray', value: 'Gray' },
|
||||
{ label: 'Red', value: 'Red' },
|
||||
{ label: 'Blue', value: 'Blue' },
|
||||
{ label: 'Green', value: 'Green' },
|
||||
{ label: 'Yellow', value: 'Yellow' }
|
||||
];
|
||||
infillPatterns = [
|
||||
{ label: 'Grid', value: 'grid' },
|
||||
{ label: 'Gyroid', value: 'gyroid' },
|
||||
@@ -69,7 +63,7 @@ export class UploadFormComponent {
|
||||
quality: ['Standard', Validators.required],
|
||||
notes: [''],
|
||||
// Advanced fields
|
||||
color: ['Black'],
|
||||
// Color removed from global form
|
||||
infillDensity: [20, [Validators.min(0), Validators.max(100)]],
|
||||
infillPattern: ['grid'],
|
||||
supportEnabled: [false]
|
||||
@@ -85,7 +79,8 @@ export class UploadFormComponent {
|
||||
if (file.size > MAX_SIZE) {
|
||||
hasError = true;
|
||||
} else {
|
||||
validItems.push({ file, quantity: 1 });
|
||||
// Default color is Black
|
||||
validItems.push({ file, quantity: 1, color: 'Black' });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,6 +124,18 @@ export class UploadFormComponent {
|
||||
}
|
||||
}
|
||||
|
||||
// Helper to get color of currently selected file
|
||||
getSelectedFileColor(): string {
|
||||
const file = this.selectedFile();
|
||||
if (!file) return '#facf0a'; // Default
|
||||
|
||||
const item = this.items().find(i => i.file === file);
|
||||
if (item) {
|
||||
return getColorHex(item.color);
|
||||
}
|
||||
return '#facf0a';
|
||||
}
|
||||
|
||||
updateItemQuantity(index: number, event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
let val = parseInt(input.value, 10);
|
||||
@@ -141,6 +148,14 @@ export class UploadFormComponent {
|
||||
});
|
||||
}
|
||||
|
||||
updateItemColor(index: number, newColor: string) {
|
||||
this.items.update(current => {
|
||||
const updated = [...current];
|
||||
updated[index] = { ...updated[index], color: newColor };
|
||||
return updated;
|
||||
});
|
||||
}
|
||||
|
||||
removeItem(index: number) {
|
||||
this.items.update(current => {
|
||||
const updated = [...current];
|
||||
@@ -155,7 +170,7 @@ export class UploadFormComponent {
|
||||
onSubmit() {
|
||||
if (this.form.valid && this.items().length > 0) {
|
||||
this.submitRequest.emit({
|
||||
items: this.items(), // Pass the items array
|
||||
items: this.items(), // Pass the items array including colors
|
||||
...this.form.value,
|
||||
mode: this.mode()
|
||||
});
|
||||
|
||||
@@ -5,11 +5,11 @@ import { map, catchError } from 'rxjs/operators';
|
||||
import { environment } from '../../../../environments/environment';
|
||||
|
||||
export interface QuoteRequest {
|
||||
items: { file: File, quantity: number }[];
|
||||
items: { file: File, quantity: number, color?: string }[];
|
||||
material: string;
|
||||
quality: string;
|
||||
notes?: string;
|
||||
color?: string;
|
||||
// color removed from global scope
|
||||
infillDensity?: number;
|
||||
infillPattern?: string;
|
||||
supportEnabled?: boolean;
|
||||
@@ -69,8 +69,11 @@ export class QuoteEstimatorService {
|
||||
formData.append('machine', 'bambu_a1');
|
||||
formData.append('filament', this.mapMaterial(request.material));
|
||||
formData.append('quality', this.mapQuality(request.quality));
|
||||
|
||||
// Send color for both modes if present, defaulting to Black
|
||||
formData.append('material_color', item.color || 'Black');
|
||||
|
||||
if (request.mode === 'advanced') {
|
||||
if (request.color) formData.append('material_color', request.color);
|
||||
if (request.infillDensity) formData.append('infill_density', request.infillDensity.toString());
|
||||
if (request.infillPattern) formData.append('infill_pattern', request.infillPattern);
|
||||
if (request.supportEnabled) formData.append('support_enabled', 'true');
|
||||
|
||||
Reference in New Issue
Block a user