- @for (item of items(); track item.file.name; let i = $index) {
+ @for (item of items(); track item; let i = $index) {
{{ item.file.name }}
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 9258067..d407b69 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
@@ -12,6 +12,7 @@ import { QuoteRequest, QuoteEstimatorService, OptionsResponse, SimpleOption, Mat
import { getColorHex } from '../../../../core/constants/colors.const';
interface FormItem {
+ id?: string;
file: File;
quantity: number;
color: string;
@@ -178,6 +179,37 @@ export class UploadFormComponent implements OnInit {
});
}
+ updateItemQuantityAtIndex(index: number, quantity: number) {
+ this.items.update(current => {
+ const updated = [...current];
+ if (updated[index]) {
+ updated[index] = { ...updated[index], quantity };
+ }
+ return updated;
+ });
+ }
+
+ updateItemIds(itemsWithIds: { fileName: string, id: string }[]) {
+ this.items.update(current => {
+ return current.map(item => {
+ const match = itemsWithIds.find(i => i.fileName === item.file.name && !i.id); // This matching is weak
+ // Better: matching should be based on index if we trust order
+ return item;
+ });
+ });
+ }
+
+ updateItemIdsByIndex(ids: (string | undefined)[]) {
+ this.items.update(current => {
+ return current.map((item, i) => {
+ if (ids[i]) {
+ return { ...item, id: ids[i] };
+ }
+ return item;
+ });
+ });
+ }
+
selectFile(file: File) {
if (this.selectedFile() === file) {
// toggle off? no, keep active
@@ -208,11 +240,7 @@ export class UploadFormComponent implements OnInit {
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 };
- return updated;
- });
+ this.updateItemQuantityAtIndex(index, val);
}
updateItemColor(index: number, newColor: string) {
@@ -234,12 +262,12 @@ export class UploadFormComponent implements OnInit {
});
}
- setFiles(files: File[]) {
+ setFiles(files: File[], colors?: string[]) {
const validItems: FormItem[] = [];
- for (const file of files) {
- // Default color is Black or derive from somewhere if possible, but here we just init
- validItems.push({ file, quantity: 1, color: 'Black' });
- }
+ files.forEach((file, i) => {
+ const color = (colors && colors[i]) ? colors[i] : 'Black';
+ validItems.push({ file, quantity: 1, color: color });
+ });
if (validItems.length > 0) {
this.items.set(validItems);
diff --git a/frontend/src/app/features/calculator/services/quote-estimator.service.ts b/frontend/src/app/features/calculator/services/quote-estimator.service.ts
index 93a5f80..35c8cdf 100644
--- a/frontend/src/app/features/calculator/services/quote-estimator.service.ts
+++ b/frontend/src/app/features/calculator/services/quote-estimator.service.ts
@@ -26,6 +26,7 @@ export interface QuoteItem {
quantity: number;
material?: string;
color?: string;
+ error?: string;
}
export interface QuoteResult {
@@ -255,11 +256,22 @@ export class QuoteEstimatorService {
let validCount = 0;
responses.forEach((res, idx) => {
- if (!res || !res.success) return;
- validCount++;
+ const quantity = res?.originalQty || request.items[idx].quantity || 1;
+ if (!res || !res.success) {
+ items.push({
+ fileName: request.items[idx].file.name,
+ unitPrice: 0,
+ unitTime: 0,
+ unitWeight: 0,
+ quantity: quantity,
+ error: res?.error || 'UPLOAD_FAILED'
+ });
+ return;
+ }
+
+ validCount++;
const unitPrice = res.unitPriceChf || 0;
- const quantity = res.originalQty || 1;
items.push({
id: res.id,
@@ -270,8 +282,6 @@ export class QuoteEstimatorService {
quantity: quantity,
material: request.material,
color: res.originalItem.color || 'Default'
- // Store ID if needed for updates? QuoteItem interface might need update
- // or we map it in component
});
grandTotal += unitPrice * quantity;
diff --git a/frontend/src/app/features/contact/components/contact-form/contact-form.component.html b/frontend/src/app/features/contact/components/contact-form/contact-form.component.html
index b0f7bed..3119dab 100644
--- a/frontend/src/app/features/contact/components/contact-form/contact-form.component.html
+++ b/frontend/src/app/features/contact/components/contact-form/contact-form.component.html
@@ -37,20 +37,20 @@
-
+
-
+
{{ 'CONTACT.UPLOAD_HINT' | translate }}
-
-
-
{{ 'CONTACT.DROP_FILES' | translate }}
diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json
index 22dbba7..2b5a432 100644
--- a/frontend/src/assets/i18n/en.json
+++ b/frontend/src/assets/i18n/en.json
@@ -61,6 +61,9 @@
"ORDER": "Order Now",
"CONSULT": "Request Consultation",
"ERROR_GENERIC": "An error occurred while calculating the quote.",
+ "ERROR_UPLOAD_FAILED": "Calculation failed",
+ "ERROR_VIRUS_DETECTED": "File removed (virus detected)",
+ "ERROR_SLICING_FAILED": "Slicing error (complex geometry?)",
"NEW_QUOTE": "Calculate New Quote",
"ORDER_SUCCESS_TITLE": "Order Submitted Successfully",
"ORDER_SUCCESS_DESC": "We have received your order details. You will receive a confirmation email shortly with the payment details.",
diff --git a/frontend/src/assets/i18n/it.json b/frontend/src/assets/i18n/it.json
index 9597eed..b8d0504 100644
--- a/frontend/src/assets/i18n/it.json
+++ b/frontend/src/assets/i18n/it.json
@@ -40,6 +40,9 @@
"ORDER": "Ordina Ora",
"CONSULT": "Richiedi Consulenza",
"ERROR_GENERIC": "Si è verificato un errore durante il calcolo del preventivo.",
+ "ERROR_UPLOAD_FAILED": "Calcolo fallito",
+ "ERROR_VIRUS_DETECTED": "File rimosso (virus rilevato)",
+ "ERROR_SLICING_FAILED": "Errore slicing (geometria complessa?)",
"NEW_QUOTE": "Calcola Nuovo Preventivo",
"ORDER_SUCCESS_TITLE": "Ordine Inviato con Successo",
"ORDER_SUCCESS_DESC": "Abbiamo ricevuto i dettagli del tuo ordine. Riceverai a breve una email di conferma con i dettagli per il pagamento.",