From f0e0f57e7cf3df318e0789c14da4d62c32ebb3db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Joe=20K=C3=BCng?= Date: Mon, 9 Feb 2026 18:54:06 +0100 Subject: [PATCH] feat(web): multiple feature --- backend/Dockerfile | 2 +- backend/build.gradle | 13 +++++++ .../printcalculator/config/CorsConfig.java | 20 ++++++++++ .../controller/QuoteController.java | 29 ++++++++++----- .../printcalculator/service/GCodeParser.java | 14 ++++--- .../service/ProfileManager.java | 29 ++++++++++++++- .../service/QuoteCalculator.java | 4 +- .../service/SlicerService.java | 10 +++-- frontend/angular.json | 14 +++++++ frontend/src/app/app.routes.ts | 4 ++ .../src/app/core/layout/footer.component.html | 4 +- .../src/app/features/legal/legal.routes.ts | 12 ++++++ .../legal/privacy/privacy.component.html | 17 +++++++++ .../legal/privacy/privacy.component.scss | 37 +++++++++++++++++++ .../legal/privacy/privacy.component.ts | 11 ++++++ .../features/legal/terms/terms.component.html | 18 +++++++++ .../features/legal/terms/terms.component.scss | 37 +++++++++++++++++++ .../features/legal/terms/terms.component.ts | 11 ++++++ frontend/src/assets/i18n/en.json | 15 ++++++++ frontend/src/assets/i18n/it.json | 15 ++++++++ 20 files changed, 293 insertions(+), 23 deletions(-) create mode 100644 backend/src/main/java/com/printcalculator/config/CorsConfig.java create mode 100644 frontend/src/app/features/legal/legal.routes.ts create mode 100644 frontend/src/app/features/legal/privacy/privacy.component.html create mode 100644 frontend/src/app/features/legal/privacy/privacy.component.scss create mode 100644 frontend/src/app/features/legal/privacy/privacy.component.ts create mode 100644 frontend/src/app/features/legal/terms/terms.component.html create mode 100644 frontend/src/app/features/legal/terms/terms.component.scss create mode 100644 frontend/src/app/features/legal/terms/terms.component.ts diff --git a/backend/Dockerfile b/backend/Dockerfile index 7d08ed4..32b5ac1 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -19,7 +19,7 @@ RUN apt-get update && apt-get install -y \ libglib2.0-0 \ libgtk-3-0 \ libdbus-1-3 \ - libwebkit2gtk-4.1-0 \ + libwebkit2gtk-4.0-37 \ && rm -rf /var/lib/apt/lists/* # Install OrcaSlicer diff --git a/backend/build.gradle b/backend/build.gradle index b664e88..82927d9 100644 --- a/backend/build.gradle +++ b/backend/build.gradle @@ -1,5 +1,6 @@ plugins { id 'java' + id 'application' id 'org.springframework.boot' version '3.4.1' id 'io.spring.dependency-management' version '1.1.7' } @@ -13,6 +14,10 @@ java { } } +application { + mainClass = 'com.printcalculator.BackendApplication' +} + repositories { mavenCentral() } @@ -27,3 +32,11 @@ dependencies { tasks.named('test') { useJUnitPlatform() } + +tasks.named('bootRun') { + args = ["--spring.profiles.active=local"] +} + +application { + applicationDefaultJvmArgs = ["-Dspring.profiles.active=local"] +} diff --git a/backend/src/main/java/com/printcalculator/config/CorsConfig.java b/backend/src/main/java/com/printcalculator/config/CorsConfig.java new file mode 100644 index 0000000..39eaea5 --- /dev/null +++ b/backend/src/main/java/com/printcalculator/config/CorsConfig.java @@ -0,0 +1,20 @@ +package com.printcalculator.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Profile; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +@Profile("local") +public class CorsConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**") + .allowedOrigins("*") + .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") + .allowedHeaders("*") + .allowCredentials(false); + } +} diff --git a/backend/src/main/java/com/printcalculator/controller/QuoteController.java b/backend/src/main/java/com/printcalculator/controller/QuoteController.java index 301f01a..b85a6a6 100644 --- a/backend/src/main/java/com/printcalculator/controller/QuoteController.java +++ b/backend/src/main/java/com/printcalculator/controller/QuoteController.java @@ -13,16 +13,15 @@ import java.nio.file.Files; import java.nio.file.Path; @RestController -@CrossOrigin(origins = "*") // Allow all for development public class QuoteController { private final SlicerService slicerService; private final QuoteCalculator quoteCalculator; - // Defaults - private static final String DEFAULT_MACHINE = "Bambu_Lab_A1_machine"; - private static final String DEFAULT_FILAMENT = "Bambu_PLA_Basic"; - private static final String DEFAULT_PROCESS = "Bambu_Process_0.20_Standard"; + // Defaults (using aliases defined in ProfileManager) + private static final String DEFAULT_MACHINE = "bambu_a1"; + private static final String DEFAULT_FILAMENT = "pla_basic"; + private static final String DEFAULT_PROCESS = "standard"; public QuoteController(SlicerService slicerService, QuoteCalculator quoteCalculator) { this.slicerService = slicerService; @@ -32,12 +31,24 @@ public class QuoteController { @PostMapping("/api/quote") public ResponseEntity calculateQuote( @RequestParam("file") MultipartFile file, - @RequestParam(value = "machine", defaultValue = DEFAULT_MACHINE) String machine, - @RequestParam(value = "filament", defaultValue = DEFAULT_FILAMENT) String filament, - @RequestParam(value = "process", defaultValue = DEFAULT_PROCESS) String process + @RequestParam(value = "machine", required = false, defaultValue = DEFAULT_MACHINE) String machine, + @RequestParam(value = "filament", required = false, defaultValue = DEFAULT_FILAMENT) String filament, + @RequestParam(value = "process", required = false) String process, + @RequestParam(value = "quality", required = false) String quality ) throws IOException { - return processRequest(file, machine, filament, process); + // Frontend sends 'quality', backend expects 'process'. + // If process is missing, try quality. If both missing, use default. + String actualProcess = process; + if (actualProcess == null || actualProcess.isEmpty()) { + if (quality != null && !quality.isEmpty()) { + actualProcess = quality; + } else { + actualProcess = DEFAULT_PROCESS; + } + } + + return processRequest(file, machine, filament, actualProcess); } @PostMapping("/calculate/stl") diff --git a/backend/src/main/java/com/printcalculator/service/GCodeParser.java b/backend/src/main/java/com/printcalculator/service/GCodeParser.java index fef2500..f8a1691 100644 --- a/backend/src/main/java/com/printcalculator/service/GCodeParser.java +++ b/backend/src/main/java/com/printcalculator/service/GCodeParser.java @@ -13,9 +13,13 @@ import java.util.regex.Pattern; @Service public class GCodeParser { - private static final Pattern TIME_PATTERN = Pattern.compile("estimated printing time = (.*)"); - private static final Pattern FILAMENT_G_PATTERN = Pattern.compile("filament used \\[g\\] = (.*)"); - private static final Pattern FILAMENT_MM_PATTERN = Pattern.compile("filament used \\[mm\\] = (.*)"); + // OrcaSlicer/BambuStudio format + // ; estimated printing time = 1h 2m 3s + // ; filament used [g] = 12.34 + // ; filament used [mm] = 1234.56 + private static final Pattern TIME_PATTERN = Pattern.compile(";\\s*estimated printing time\\s*=\\s*(.*)"); + private static final Pattern FILAMENT_G_PATTERN = Pattern.compile(";\\s*filament used \\[g\\]\\s*=\\s*(.*)"); + private static final Pattern FILAMENT_MM_PATTERN = Pattern.compile(";\\s*filament used \\[mm\\]\\s*=\\s*(.*)"); public PrintStats parse(File gcodeFile) throws IOException { long seconds = 0; @@ -25,9 +29,9 @@ public class GCodeParser { try (BufferedReader reader = new BufferedReader(new FileReader(gcodeFile))) { String line; - // Scan first 500 lines for efficiency + // Scan first 5000 lines for efficiency (metadata might be further down) int count = 0; - while ((line = reader.readLine()) != null && count < 500) { + while ((line = reader.readLine()) != null && count < 5000) { line = line.trim(); if (!line.startsWith(";")) { count++; diff --git a/backend/src/main/java/com/printcalculator/service/ProfileManager.java b/backend/src/main/java/com/printcalculator/service/ProfileManager.java index 748f5ab..c6bd78a 100644 --- a/backend/src/main/java/com/printcalculator/service/ProfileManager.java +++ b/backend/src/main/java/com/printcalculator/service/ProfileManager.java @@ -14,6 +14,8 @@ import java.util.Iterator; import java.util.Optional; import java.util.logging.Logger; import java.util.stream.Stream; +import java.util.Map; +import java.util.HashMap; @Service public class ProfileManager { @@ -22,9 +24,31 @@ public class ProfileManager { private final String profilesRoot; private final ObjectMapper mapper; + private final Map profileAliases; + public ProfileManager(@Value("${profiles.root:profiles}") String profilesRoot, ObjectMapper mapper) { this.profilesRoot = profilesRoot; this.mapper = mapper; + this.profileAliases = new HashMap<>(); + initializeAliases(); + } + + private void initializeAliases() { + // Machine Aliases + profileAliases.put("bambu_a1", "Bambu Lab A1 0.4 nozzle"); + + // Material Aliases + profileAliases.put("pla_basic", "Bambu PLA Basic @BBL A1"); + profileAliases.put("petg_basic", "Bambu PETG Basic @BBL A1"); + profileAliases.put("tpu_95a", "Bambu TPU 95A @BBL A1"); + + // Quality/Process Aliases + profileAliases.put("draft", "0.24mm Draft @BBL A1"); + profileAliases.put("standard", "0.20mm Standard @BBL A1"); // or 0.20mm Standard @BBL A1 + profileAliases.put("extra_fine", "0.08mm High Quality @BBL A1"); + + // Additional aliases from error logs + profileAliases.put("Bambu_Process_0.20_Standard", "0.20mm Standard @BBL A1"); } public ObjectNode getMergedProfile(String profileName, String type) throws IOException { @@ -36,9 +60,12 @@ public class ProfileManager { } private Path findProfileFile(String name, String type) { + // Check aliases first + String resolvedName = profileAliases.getOrDefault(name, name); + // Simple search: look for name.json in the profiles_root recursively // Type could be "machine", "process", "filament" to narrow down, but for now global search - String filename = name.endsWith(".json") ? name : name + ".json"; + String filename = resolvedName.endsWith(".json") ? resolvedName : resolvedName + ".json"; try (Stream stream = Files.walk(Paths.get(profilesRoot))) { Optional found = stream diff --git a/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java b/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java index b7d8f2e..ada3fe5 100644 --- a/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java +++ b/backend/src/main/java/com/printcalculator/service/QuoteCalculator.java @@ -52,8 +52,8 @@ public class QuoteCalculator { ); List notes = new ArrayList<>(); - notes.add("Generated via Dynamic Slicer (Java Backend)"); + // notes.add("Generated via Dynamic Slicer (Java Backend)"); - return new QuoteResult(totalPrice, "EUR", stats, breakdown, notes); + return new QuoteResult(totalPrice, "CHF", stats, breakdown, notes); } } diff --git a/backend/src/main/java/com/printcalculator/service/SlicerService.java b/backend/src/main/java/com/printcalculator/service/SlicerService.java index 65de876..1594d32 100644 --- a/backend/src/main/java/com/printcalculator/service/SlicerService.java +++ b/backend/src/main/java/com/printcalculator/service/SlicerService.java @@ -55,12 +55,16 @@ public class SlicerService { // 3. Build Command // --load-settings "machine.json;process.json" --load-filaments "filament.json" - String settingsArg = mFile.getAbsolutePath() + ";" + pFile.getAbsolutePath(); - List command = new ArrayList<>(); command.add(slicerPath); + + // Load machine settings command.add("--load-settings"); - command.add(settingsArg); + command.add(mFile.getAbsolutePath()); + + // Load process settings + command.add("--load-settings"); + command.add(pFile.getAbsolutePath()); command.add("--load-filaments"); command.add(fFile.getAbsolutePath()); command.add("--ensure-on-bed"); diff --git a/frontend/angular.json b/frontend/angular.json index 03d92e6..bc7951c 100644 --- a/frontend/angular.json +++ b/frontend/angular.json @@ -70,6 +70,17 @@ "optimization": false, "extractLicenses": false, "sourceMap": true + }, + "local": { + "fileReplacements": [ + { + "replace": "src/environments/environment.ts", + "with": "src/environments/environment.local.ts" + } + ], + "optimization": false, + "extractLicenses": false, + "sourceMap": true } }, @@ -83,6 +94,9 @@ }, "development": { "buildTarget": "frontend:build:development" + }, + "local": { + "buildTarget": "frontend:build:local" } }, "defaultConfiguration": "development" diff --git a/frontend/src/app/app.routes.ts b/frontend/src/app/app.routes.ts index 19d0cdf..9d013b7 100644 --- a/frontend/src/app/app.routes.ts +++ b/frontend/src/app/app.routes.ts @@ -24,6 +24,10 @@ export const routes: Routes = [ { path: 'contact', loadChildren: () => import('./features/contact/contact.routes').then(m => m.CONTACT_ROUTES) + }, + { + path: '', + loadChildren: () => import('./features/legal/legal.routes').then(m => m.LEGAL_ROUTES) } ] } diff --git a/frontend/src/app/core/layout/footer.component.html b/frontend/src/app/core/layout/footer.component.html index 64f9312..d828dcd 100644 --- a/frontend/src/app/core/layout/footer.component.html +++ b/frontend/src/app/core/layout/footer.component.html @@ -4,11 +4,11 @@ 3D fab - +