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",