diff --git a/backend/Dockerfile b/backend/Dockerfile
index 2239f80..6be68ae 100644
--- a/backend/Dockerfile
+++ b/backend/Dockerfile
@@ -10,6 +10,8 @@ RUN ./gradlew bootJar -x test --no-daemon
# Stage 2: Runtime Environment
FROM eclipse-temurin:21-jre-jammy
+ARG ORCA_VERSION=2.3.1
+ARG ORCA_DOWNLOAD_URL
# Install system dependencies for OrcaSlicer (same as before)
RUN apt-get update && apt-get install -y \
@@ -25,7 +27,12 @@ RUN apt-get update && apt-get install -y \
# Install OrcaSlicer
WORKDIR /opt
-RUN wget -q https://github.com/SoftFever/OrcaSlicer/releases/download/v2.2.0/OrcaSlicer_Linux_V2.2.0.AppImage -O OrcaSlicer.AppImage \
+RUN set -eux; \
+ ORCA_URL="${ORCA_DOWNLOAD_URL:-}"; \
+ if [ -z "${ORCA_URL}" ]; then \
+ ORCA_URL="https://github.com/OrcaSlicer/OrcaSlicer/releases/download/v${ORCA_VERSION}/OrcaSlicer_Linux_V${ORCA_VERSION}.AppImage"; \
+ fi; \
+ wget -q "${ORCA_URL}" -O OrcaSlicer.AppImage \
&& 7z x OrcaSlicer.AppImage -o/opt/orcaslicer \
&& chmod -R +x /opt/orcaslicer \
&& rm OrcaSlicer.AppImage
diff --git a/frontend/src/app/features/calculator/calculator-page.component.html b/frontend/src/app/features/calculator/calculator-page.component.html
index ab7ae3a..03c86d5 100644
--- a/frontend/src/app/features/calculator/calculator-page.component.html
+++ b/frontend/src/app/features/calculator/calculator-page.component.html
@@ -4,6 +4,13 @@
@if (error()) {
{{ errorKey() | translate }}
+ @if (isZeroQuoteError()) {
+
+
+ {{ "QUOTE.CONSULT" | translate }}
+
+
+ }
}
@@ -65,6 +72,16 @@
(proceed)="onProceed()"
(itemChange)="onItemChange($event)"
>
+ } @else if (isZeroQuoteError()) {
+
+ {{ "CALC.ZERO_RESULT_TITLE" | translate }}
+ {{ "CALC.ZERO_RESULT_HELP" | translate }}
+
+
+ {{ "QUOTE.CONSULT" | translate }}
+
+
+
} @else {
{{ "CALC.BENEFITS_TITLE" | translate }}
diff --git a/frontend/src/app/features/calculator/calculator-page.component.scss b/frontend/src/app/features/calculator/calculator-page.component.scss
index 1eb6b2e..1b878cd 100644
--- a/frontend/src/app/features/calculator/calculator-page.component.scss
+++ b/frontend/src/app/features/calculator/calculator-page.component.scss
@@ -9,6 +9,12 @@
margin: 0 auto;
}
+.error-action {
+ display: flex;
+ justify-content: center;
+ margin-top: calc(var(--space-4) * -1);
+}
+
.content-grid {
display: grid;
grid-template-columns: 1fr;
@@ -82,6 +88,15 @@
line-height: 2;
}
+.zero-result-card p {
+ color: var(--color-text-muted);
+ line-height: 1.6;
+}
+
+.zero-result-action {
+ margin-top: var(--space-4);
+}
+
.loader-content {
text-align: center;
max-width: 300px;
diff --git a/frontend/src/app/features/calculator/calculator-page.component.ts b/frontend/src/app/features/calculator/calculator-page.component.ts
index 14e7a12..8619be5 100644
--- a/frontend/src/app/features/calculator/calculator-page.component.ts
+++ b/frontend/src/app/features/calculator/calculator-page.component.ts
@@ -1,5 +1,6 @@
import {
Component,
+ computed,
signal,
ViewChild,
ElementRef,
@@ -12,6 +13,7 @@ import { map } from 'rxjs/operators';
import { AppCardComponent } from '../../shared/components/app-card/app-card.component';
import { AppAlertComponent } from '../../shared/components/app-alert/app-alert.component';
+import { AppButtonComponent } from '../../shared/components/app-button/app-button.component';
import { UploadFormComponent } from './components/upload-form/upload-form.component';
import { QuoteResultComponent } from './components/quote-result/quote-result.component';
import {
@@ -31,6 +33,7 @@ import { LanguageService } from '../../core/services/language.service';
TranslateModule,
AppCardComponent,
AppAlertComponent,
+ AppButtonComponent,
UploadFormComponent,
QuoteResultComponent,
SuccessStateComponent,
@@ -47,6 +50,9 @@ export class CalculatorPageComponent implements OnInit {
result = signal(null);
error = signal(false);
errorKey = signal('CALC.ERROR_GENERIC');
+ isZeroQuoteError = computed(
+ () => this.error() && this.errorKey() === 'CALC.ERROR_ZERO_PRICE',
+ );
orderSuccess = signal(false);
@@ -318,7 +324,10 @@ export class CalculatorPageComponent implements OnInit {
private currentRequest: QuoteRequest | null = null;
onConsult() {
- if (!this.currentRequest) return;
+ if (!this.currentRequest) {
+ this.router.navigate(['/', this.languageService.selectedLang(), 'contact']);
+ return;
+ }
const req = this.currentRequest;
let details = `Richiesta Preventivo:\n`;
@@ -349,7 +358,16 @@ export class CalculatorPageComponent implements OnInit {
}
private isInvalidQuote(result: QuoteResult): boolean {
- return !Number.isFinite(result.totalPrice) || result.totalPrice <= 0;
+ const invalidPrice =
+ !Number.isFinite(result.totalPrice) || result.totalPrice <= 0;
+ const invalidWeight =
+ !Number.isFinite(result.totalWeight) || result.totalWeight <= 0;
+ const invalidTime =
+ !Number.isFinite(result.totalTimeHours) ||
+ !Number.isFinite(result.totalTimeMinutes) ||
+ (result.totalTimeHours <= 0 && result.totalTimeMinutes <= 0);
+
+ return invalidPrice || invalidWeight || invalidTime;
}
private setQuoteError(key: string): void {
diff --git a/frontend/src/assets/i18n/de.json b/frontend/src/assets/i18n/de.json
index 49b29ed..f2bb3d1 100644
--- a/frontend/src/assets/i18n/de.json
+++ b/frontend/src/assets/i18n/de.json
@@ -91,7 +91,9 @@
"NOTES_PLACEHOLDER": "Spezifische Anweisungen...",
"SETUP_NOTE": "* Beinhaltet {{cost}} als Einrichtungskosten",
"SHIPPING_NOTE": "** Versandkosten ausgeschlossen, werden im nächsten Schritt berechnet",
- "ERROR_ZERO_PRICE": "Etwas ist schiefgelaufen. Versuche ein anderes Format oder kontaktiere uns."
+ "ERROR_ZERO_PRICE": "Bei der Berechnung ist etwas schiefgelaufen. Versuche ein anderes Format oder kontaktiere uns direkt über \"Beratung anfragen\".",
+ "ZERO_RESULT_TITLE": "Ungültiges Ergebnis",
+ "ZERO_RESULT_HELP": "Die Berechnung hat ungültige Werte (0) geliefert. Versuche ein anderes Dateiformat oder kontaktiere uns direkt über \"Beratung anfragen\"."
},
"SHOP": {
"TITLE": "Technische Lösungen",
diff --git a/frontend/src/assets/i18n/en.json b/frontend/src/assets/i18n/en.json
index a61b7d3..e65a8ce 100644
--- a/frontend/src/assets/i18n/en.json
+++ b/frontend/src/assets/i18n/en.json
@@ -91,7 +91,9 @@
"NOTES_PLACEHOLDER": "Specific instructions...",
"SETUP_NOTE": "* Includes {{cost}} as setup cost",
"SHIPPING_NOTE": "** Shipping costs excluded, calculated at the next step",
- "ERROR_ZERO_PRICE": "Something went wrong. Try another format or contact us."
+ "ERROR_ZERO_PRICE": "Something went wrong during the calculation. Try another format or contact us directly via Request Consultation.",
+ "ZERO_RESULT_TITLE": "Invalid Result",
+ "ZERO_RESULT_HELP": "The calculation returned invalid zero values. Try another file format or contact us directly via Request Consultation."
},
"SHOP": {
"TITLE": "Technical solutions",
diff --git a/frontend/src/assets/i18n/fr.json b/frontend/src/assets/i18n/fr.json
index e9f3ad4..70f2bde 100644
--- a/frontend/src/assets/i18n/fr.json
+++ b/frontend/src/assets/i18n/fr.json
@@ -116,7 +116,9 @@
"FALLBACK_MATERIAL": "PLA (fallback)",
"FALLBACK_QUALITY_STANDARD": "Standard",
"ERR_FILE_TOO_LARGE": "Certains fichiers dépassent la limite de 200MB et n'ont pas été ajoutés.",
- "ERROR_ZERO_PRICE": "Quelque chose s'est mal passé. Essayez un autre format ou contactez-nous."
+ "ERROR_ZERO_PRICE": "Un problème est survenu pendant le calcul. Essayez un autre format ou contactez-nous directement via Demander une consultation.",
+ "ZERO_RESULT_TITLE": "Résultat invalide",
+ "ZERO_RESULT_HELP": "Le calcul a renvoyé des valeurs nulles invalides. Essayez un autre format de fichier ou contactez-nous directement via Demander une consultation."
},
"QUOTE": {
"PROCEED_ORDER": "Procéder à la commande",
diff --git a/frontend/src/assets/i18n/it.json b/frontend/src/assets/i18n/it.json
index 15437f2..af78b60 100644
--- a/frontend/src/assets/i18n/it.json
+++ b/frontend/src/assets/i18n/it.json
@@ -116,7 +116,9 @@
"FALLBACK_MATERIAL": "PLA (fallback)",
"FALLBACK_QUALITY_STANDARD": "Standard",
"ERR_FILE_TOO_LARGE": "Alcuni file superano il limite di 200MB e non sono stati aggiunti.",
- "ERROR_ZERO_PRICE": "Qualcosa è andato storto. Prova con un altro formato oppure contattaci."
+ "ERROR_ZERO_PRICE": "Qualcosa è andato storto nel calcolo. Prova un altro formato o contattaci direttamente con Richiedi Consulenza.",
+ "ZERO_RESULT_TITLE": "Risultato non valido",
+ "ZERO_RESULT_HELP": "Il calcolo ha restituito valori non validi (0). Prova con un altro formato file oppure contattaci direttamente con Richiedi Consulenza."
},
"QUOTE": {
"PROCEED_ORDER": "Procedi con l'ordine",