From cb7b44073c03f93288bc4d415a2c15030a2f2bf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20K=C3=BCng?= Date: Fri, 6 Feb 2026 11:09:46 +0100 Subject: [PATCH] feat(front-end): responsive layout --- .../calculator/calculator-page.component.ts | 19 +- .../quote-result/quote-result.component.ts | 13 +- .../upload-form/upload-form.component.ts | 182 +++++++++--------- 3 files changed, 121 insertions(+), 93 deletions(-) diff --git a/frontend/src/app/features/calculator/calculator-page.component.ts b/frontend/src/app/features/calculator/calculator-page.component.ts index c6d469c..dc33a4b 100644 --- a/frontend/src/app/features/calculator/calculator-page.component.ts +++ b/frontend/src/app/features/calculator/calculator-page.component.ts @@ -1,4 +1,4 @@ -import { Component, signal, ViewChild } from '@angular/core'; +import { Component, signal, ViewChild, ElementRef } from '@angular/core'; import { CommonModule } from '@angular/common'; import { TranslateModule } from '@ngx-translate/core'; @@ -47,7 +47,7 @@ import { Router } from '@angular/router'; -
+
@if (error()) { Si è verificato un errore durante il calcolo del preventivo. } @@ -86,9 +86,10 @@ import { Router } from '@angular/router'; .content-grid { display: grid; grid-template-columns: 1fr; - gap: var(--space-8); + gap: var(--space-6); @media(min-width: 768px) { grid-template-columns: 1.5fr 1fr; + gap: var(--space-8); } } @@ -98,6 +99,10 @@ import { Router } from '@angular/router'; align-self: center; } } + + .col-input, .col-result { + min-width: 0; /* Prevent grid blowout */ + } /* Mode Selector (Segmented Control style) */ .mode-selector { @@ -184,6 +189,7 @@ export class CalculatorPageComponent { error = signal(false); @ViewChild('uploadForm') uploadForm!: UploadFormComponent; + @ViewChild('resultCol') resultCol!: ElementRef; constructor(private estimator: QuoteEstimatorService, private router: Router) {} @@ -194,6 +200,13 @@ export class CalculatorPageComponent { this.error.set(false); this.result.set(null); + // Auto-scroll on mobile to make analysis visible + setTimeout(() => { + if (this.resultCol && window.innerWidth < 768) { + this.resultCol.nativeElement.scrollIntoView({ behavior: 'smooth', block: 'start' }); + } + }, 100); + this.estimator.calculate(req).subscribe({ next: (event) => { if (typeof event === 'number') { diff --git a/frontend/src/app/features/calculator/components/quote-result/quote-result.component.ts b/frontend/src/app/features/calculator/components/quote-result/quote-result.component.ts index cf325c4..7d41ec0 100644 --- a/frontend/src/app/features/calculator/components/quote-result/quote-result.component.ts +++ b/frontend/src/app/features/calculator/components/quote-result/quote-result.component.ts @@ -104,9 +104,11 @@ import { QuoteResult, QuoteItem } from '../../services/quote-estimator.service'; .item-info { display: flex; flex-direction: column; + min-width: 0; + flex: 1; /* Ensure it takes available space */ } - .file-name { font-weight: 500; font-size: 0.9rem; color: var(--color-text); } + .file-name { font-weight: 500; font-size: 0.9rem; color: var(--color-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } .file-details { font-size: 0.8rem; color: var(--color-text-muted); } .item-controls { @@ -140,9 +142,14 @@ import { QuoteResult, QuoteItem } from '../../services/quote-estimator.service'; .result-grid { display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--space-4); + grid-template-columns: 1fr; + gap: var(--space-3); margin-bottom: var(--space-2); + + @media(min-width: 500px) { + grid-template-columns: 1fr 1fr; + gap: var(--space-4); + } } .full-width { grid-column: span 2; } diff --git a/frontend/src/app/features/calculator/components/upload-form/upload-form.component.ts b/frontend/src/app/features/calculator/components/upload-form/upload-form.component.ts index f3319f5..399d1c7 100644 --- a/frontend/src/app/features/calculator/components/upload-form/upload-form.component.ts +++ b/frontend/src/app/features/calculator/components/upload-form/upload-form.component.ts @@ -25,9 +25,7 @@ interface FormItem { @if (selectedFile()) {
- +
} @@ -44,26 +42,26 @@ interface FormItem { @if (items().length > 0) { -
+
@for (item of items(); track item.file.name; let i = $index) { -
-
- {{ item.file.name }} - {{ (item.file.size / 1024 / 1024) | number:'1.1-2' }} MB +
+
+ {{ item.file.name }}
-
-
+
+
+ class="qty-input" + (click)="$event.stopPropagation()">
-
@@ -74,9 +72,10 @@ interface FormItem {
- + +
} @@ -102,38 +101,7 @@ interface FormItem { @if (mode() === 'advanced') { -
- - - -
- -
- - -
- - -
-
- - + }
@@ -157,100 +125,140 @@ interface FormItem { `, styles: [` .section { margin-bottom: var(--space-6); } - .grid { display: grid; grid-template-columns: 1fr 1fr; gap: var(--space-4); } + .grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-4); + + @media(min-width: 640px) { + grid-template-columns: 1fr 1fr; + } + } .actions { margin-top: var(--space-6); } .error-msg { color: var(--color-danger-500); font-size: 0.875rem; margin-top: var(--space-2); text-align: center; } .viewer-wrapper { position: relative; margin-bottom: var(--space-4); } - .btn-clear-viewer { - position: absolute; - top: 10px; - right: 10px; - background: rgba(0,0,0,0.5); - color: white; - border: none; - padding: 4px 8px; - border-radius: 4px; - cursor: pointer; - z-index: 10; - &:hover { background: rgba(0,0,0,0.7); } - } - .items-list { - display: flex; - flex-direction: column; - gap: var(--space-2); + /* Grid Layout for Files */ + .items-grid { + display: grid; + grid-template-columns: 1fr; + gap: var(--space-3); margin-top: var(--space-4); + margin-bottom: var(--space-4); + + @media(min-width: 640px) { + grid-template-columns: 1fr 1fr; + } } - .file-row { - display: flex; - justify-content: space-between; - align-items: center; + .file-card { padding: var(--space-3); background: var(--color-neutral-100); border: 1px solid var(--color-border); border-radius: var(--radius-md); transition: all 0.2s; + cursor: pointer; + display: flex; + flex-direction: column; + gap: var(--space-2); + &:hover { border-color: var(--color-neutral-300); } &.active { border-color: var(--color-brand); background: rgba(250, 207, 10, 0.05); + box-shadow: 0 0 0 1px var(--color-brand); } } - .file-info { - flex: 1; - display: flex; - flex-direction: column; - cursor: pointer; + .card-header { + overflow: hidden; } - .file-name { font-weight: 500; font-size: 0.9rem; color: var(--color-text); } - .file-size { font-size: 0.75rem; color: var(--color-text-muted); } - .file-actions { + .file-name { + font-weight: 500; + font-size: 0.85rem; + color: var(--color-text); + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + + .card-body { display: flex; + justify-content: space-between; align-items: center; - gap: var(--space-3); } - .qty-control { + .qty-group { display: flex; align-items: center; gap: var(--space-2); - label { font-size: 0.8rem; color: var(--color-text-muted); } + label { font-size: 0.75rem; color: var(--color-text-muted); text-transform: uppercase; letter-spacing: 0.5px; } } .qty-input { - width: 50px; - padding: 4px; + width: 40px; + padding: 2px 4px; border: 1px solid var(--color-border); border-radius: var(--radius-sm); text-align: center; font-size: 0.9rem; + background: white; &:focus { outline: none; border-color: var(--color-brand); } } .btn-remove { - width: 28px; - height: 28px; - border-radius: 50%; - border: none; - background: transparent; // var(--color-neutral-200); - color: var(--color-text-muted); // var(--color-danger-500); + width: 24px; + height: 24px; + border-radius: 4px; + border: 1px solid transparent; // var(--color-border); + background: transparent; // white; + color: var(--color-text-muted); font-weight: bold; cursor: pointer; display: flex; align-items: center; justify-content: center; transition: all 0.2s; + font-size: 0.9rem; &:hover { background: var(--color-danger-100); color: var(--color-danger-500); + border-color: var(--color-danger-200); } } + /* Prominent Add Button */ + .add-more-container { + margin-top: var(--space-2); + } + + .btn-add-more { + width: 100%; + padding: var(--space-3); + background: var(--color-neutral-800); + color: white; + border: none; + border-radius: var(--radius-md); + font-weight: 600; + font-size: 0.9rem; + cursor: pointer; + transition: all 0.2s; + display: flex; + align-items: center; + justify-content: center; + gap: var(--space-2); + + &:hover { + background: var(--color-neutral-900); + transform: translateY(-1px); + } + &:active { transform: translateY(0); } + } + .checkbox-row { display: flex; align-items: center;