From ff8c2f9a82ce094b7ad27595c5515a89025509f0 Mon Sep 17 00:00:00 2001 From: Mireya Serrano Date: Fri, 15 May 2026 16:51:26 +0200 Subject: [PATCH] DesktopClient con JavaFx --- javafx-client/pom.xml | 145 ++++++++++ .../javafxclient/JavaFxClientApplication.java | 33 +++ .../javafxclient/api/SyncServiceClient.java | 179 ++++++++++++ .../javafxclient/model/SessionManager.java | 60 ++++ .../javafxclient/model/dto/MappingDto.java | 4 + .../tfg/javafxclient/model/dto/OrderDto.java | 4 + .../javafxclient/model/dto/ProductDto.java | 4 + .../tfg/javafxclient/model/dto/SyncDto.java | 4 + .../ui/controllers/DashboardController.java | 232 +++++++++++++++ .../ui/controllers/LoginController.java | 4 + .../ui/controllers/LogsController.java | 222 ++++++++++++++ .../ui/controllers/OrdersController.java | 225 +++++++++++++++ .../ui/controllers/ProductsController.java | 271 ++++++++++++++++++ .../javafxclient/ui/stages/LoginStage.java | 173 +++++++++++ .../tfg/javafxclient/ui/stages/MainStage.java | 139 +++++++++ .../tfg/javafxclient/util/AlertUtil.java | 34 +++ .../tfg/javafxclient/util/FormatUtil.java | 4 + .../tfg/javafxclient/util/TaskExecutor.java | 4 + .../main/resources/applications.properties | 0 .../src/main/resources/fxml/dashboard.fxml | 0 .../src/main/resources/fxml/login.fxml | 0 .../src/main/resources/fxml/logs.fxml | 0 .../src/main/resources/fxml/orders.fxml | 0 .../src/main/resources/fxml/products.fxml | 0 javafx-client/src/main/resources/logback.xml | 14 + javafx-client/src/main/resources/styles.css | 93 ++++++ .../target/classes/applications.properties | 0 .../JavaFxClientApplication.class | Bin 0 -> 1442 bytes .../javafxclient/api/SyncServiceClient.class | Bin 0 -> 8681 bytes .../javafxclient/model/SessionManager.class | Bin 0 -> 2814 bytes .../javafxclient/model/dto/MappingDto.class | Bin 0 -> 353 bytes .../tfg/javafxclient/model/dto/OrderDto.class | Bin 0 -> 347 bytes .../javafxclient/model/dto/ProductDto.class | Bin 0 -> 353 bytes .../tfg/javafxclient/model/dto/SyncDto.class | Bin 0 -> 344 bytes .../ui/controllers/DashboardController.class | Bin 0 -> 10730 bytes .../ui/controllers/LoginController.class | Bin 0 -> 378 bytes .../ui/controllers/LogsController$1.class | Bin 0 -> 1506 bytes .../ui/controllers/LogsController.class | Bin 0 -> 13325 bytes .../ui/controllers/OrdersController$1.class | Bin 0 -> 1531 bytes .../ui/controllers/OrdersController.class | Bin 0 -> 13165 bytes .../ui/controllers/ProductsController$1.class | Bin 0 -> 1845 bytes .../ui/controllers/ProductsController.class | Bin 0 -> 15178 bytes .../javafxclient/ui/stages/LoginStage.class | Bin 0 -> 9242 bytes .../javafxclient/ui/stages/MainStage.class | Bin 0 -> 8330 bytes .../tfg/javafxclient/util/AlertUtil.class | Bin 0 -> 1299 bytes .../tfg/javafxclient/util/FormatUtil.class | Bin 0 -> 343 bytes .../tfg/javafxclient/util/TaskExecutor.class | Bin 0 -> 349 bytes .../target/classes/fxml/dashboard.fxml | 0 javafx-client/target/classes/fxml/login.fxml | 0 javafx-client/target/classes/fxml/logs.fxml | 0 javafx-client/target/classes/fxml/orders.fxml | 0 .../target/classes/fxml/products.fxml | 0 javafx-client/target/classes/logback.xml | 14 + javafx-client/target/classes/styles.css | 93 ++++++ .../compile/default-compile/createdFiles.lst | 20 ++ .../compile/default-compile/inputFiles.lst | 17 ++ 56 files changed, 1992 insertions(+) create mode 100644 javafx-client/pom.xml create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.java create mode 100644 javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.java create mode 100644 javafx-client/src/main/resources/applications.properties create mode 100644 javafx-client/src/main/resources/fxml/dashboard.fxml create mode 100644 javafx-client/src/main/resources/fxml/login.fxml create mode 100644 javafx-client/src/main/resources/fxml/logs.fxml create mode 100644 javafx-client/src/main/resources/fxml/orders.fxml create mode 100644 javafx-client/src/main/resources/fxml/products.fxml create mode 100644 javafx-client/src/main/resources/logback.xml create mode 100644 javafx-client/src/main/resources/styles.css create mode 100644 javafx-client/target/classes/applications.properties create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController$1.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController$1.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController$1.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.class create mode 100644 javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.class create mode 100644 javafx-client/target/classes/fxml/dashboard.fxml create mode 100644 javafx-client/target/classes/fxml/login.fxml create mode 100644 javafx-client/target/classes/fxml/logs.fxml create mode 100644 javafx-client/target/classes/fxml/orders.fxml create mode 100644 javafx-client/target/classes/fxml/products.fxml create mode 100644 javafx-client/target/classes/logback.xml create mode 100644 javafx-client/target/classes/styles.css create mode 100644 javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst create mode 100644 javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst diff --git a/javafx-client/pom.xml b/javafx-client/pom.xml new file mode 100644 index 0000000..6b7b70a --- /dev/null +++ b/javafx-client/pom.xml @@ -0,0 +1,145 @@ + + + 4.0.0 + + com.teterialosjuanjos.tfg + javafx-client + 0.0.1-SNAPSHOT + javafx-client + TFG — JavaFX Desktop Client para Sync Service + + + UTF-8 + 21 + 21 + 21.0.2 + 0.0.8 + + + + + + org.openjfx + javafx-controls + ${javafx.version} + + + org.openjfx + javafx-fxml + ${javafx.version} + + + org.openjfx + javafx-graphics + ${javafx.version} + + + org.openjfx + javafx-base + ${javafx.version} + + + org.openjfx + javafx-swing + ${javafx.version} + + + + + com.fasterxml.jackson.core + jackson-databind + 2.18.0 + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + 2.18.0 + + + + + org.projectlombok + lombok + 1.18.30 + provided + + + + + org.slf4j + slf4j-api + 2.0.13 + + + ch.qos.logback + logback-classic + 1.5.6 + + + + + org.junit.jupiter + junit-jupiter + 5.10.2 + test + + + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.11.0 + + 21 + 21 + + + org.projectlombok + lombok + 1.18.30 + + + + + + + + org.openjfx + javafx-maven-plugin + ${javafx.maven.plugin.version} + + com.teterialosjuanjos.tfg.javafxclient.JavaFxClientApplication + + + + + + org.apache.maven.plugins + maven-shade-plugin + 3.5.0 + + + package + + shade + + + true + fat + + + com.teterialosjuanjos.tfg.javafxclient.JavaFxClientApplication + + + + + + + + + \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.java new file mode 100644 index 0000000..660ef85 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.java @@ -0,0 +1,33 @@ +package com.teterialosjuanjos.tfg.javafxclient; + +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.ui.stages.LoginStage; +import javafx.application.Application; +import javafx.stage.Stage; +import lombok.extern.slf4j.Slf4j; + +/** + * JavaFX Desktop Client para TFG Integración Dolibarr-PrestaShop. + * + *

Punto de entrada de la aplicación. Inicia sesión y luego accede al + * {@code sync-service} en Railway para ver/crear productos, pedidos, logs.

+ */ +@Slf4j +public class JavaFxClientApplication extends Application { + + @Override + public void start(Stage primaryStage) { + log.info("Starting JavaFX Client Application"); + + // Inicializa SessionManager (almacena credenciales + cliente HTTP) + SessionManager sessionManager = new SessionManager(); + + // Muestra LoginStage + LoginStage loginStage = new LoginStage(sessionManager, primaryStage); + loginStage.show(); + } + + public static void main(String[] args) { + launch(args); + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.java new file mode 100644 index 0000000..b5c78ae --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.java @@ -0,0 +1,179 @@ +package com.teterialosjuanjos.tfg.javafxclient.api; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; +import lombok.extern.slf4j.Slf4j; + +import java.io.IOException; +import java.net.URI; +import java.net.http.HttpClient; +import java.net.http.HttpRequest; +import java.net.http.HttpResponse; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Cliente HTTP tipado para el sync-service en Railway. + * + *

Maneja autenticación Basic Auth para todos los endpoints.

+ */ +@Slf4j +public class SyncServiceClient { + + private final HttpClient httpClient; + private final String baseUrl; + private final String authHeader; + private final ObjectMapper objectMapper; + + public SyncServiceClient(String baseUrl, String username, String password) { + this.baseUrl = baseUrl.replaceAll("/$", ""); // Remove trailing slash + this.httpClient = HttpClient.newHttpClient(); + this.authHeader = buildBasicAuthHeader(username, password); + this.objectMapper = new ObjectMapper(); + this.objectMapper.registerModule(new JavaTimeModule()); + } + + // ── Health Check ──────────────────────────────────────────────────────── + + public Map checkHealth() throws IOException, InterruptedException { + String url = baseUrl + "/actuator/health"; + return getJson(url, Map.class); + } + + // ── Logs ───────────────────────────────────────────────────────────────── + + /** + * Obtiene todos los logs de sincronización. + * + * @param type opcional, filtra por tipo (PRODUCT_PUSH, STOCK_PUSH, ORDER_PULL) + */ + public List> getLogs(String type) throws IOException, InterruptedException { + String url = baseUrl + "/api/logs"; + if (type != null && !type.isEmpty()) { + url += "?type=" + type; + } + + HttpResponse response = sendRequest("GET", url, null); + if (response.statusCode() == 200) { + return objectMapper.readValue( + response.body(), + objectMapper.getTypeFactory().constructCollectionType(List.class, Map.class) + ); + } + throw new IOException("Failed to fetch logs: " + response.statusCode()); + } + + /** + * Obtiene un log específico por ID. + */ + public Map getLog(Long id) throws IOException, InterruptedException { + String url = baseUrl + "/api/logs/" + id; + return getJson(url, Map.class); + } + + // ── Mappings ───────────────────────────────────────────────────────────── + + /** + * Obtiene todos los mapeos de productos. + * + * @param status opcional, filtra por status (PENDING, SYNCED, ERROR) + */ + public List> getProductMappings(String status) + throws IOException, InterruptedException { + String url = baseUrl + "/api/mappings/products"; + if (status != null && !status.isEmpty()) { + url += "?status=" + status; + } + + HttpResponse response = sendRequest("GET", url, null); + if (response.statusCode() == 200) { + return objectMapper.readValue( + response.body(), + objectMapper.getTypeFactory().constructCollectionType(List.class, Map.class) + ); + } + throw new IOException("Failed to fetch product mappings: " + response.statusCode()); + } + + /** + * Obtiene todos los mapeos de pedidos. + */ + public List> getOrderMappings() throws IOException, InterruptedException { + String url = baseUrl + "/api/mappings/orders"; + + HttpResponse response = sendRequest("GET", url, null); + if (response.statusCode() == 200) { + return objectMapper.readValue( + response.body(), + objectMapper.getTypeFactory().constructCollectionType(List.class, Map.class) + ); + } + throw new IOException("Failed to fetch order mappings: " + response.statusCode()); + } + + // ── Sync Triggers ─────────────────────────────────────────────────────── + + /** + * Dispara sincronización de productos. + */ + public Map triggerProductSync() throws IOException, InterruptedException { + String url = baseUrl + "/api/sync/products"; + return postJson(url, new HashMap<>(), Map.class); + } + + /** + * Dispara sincronización de stock. + */ + public Map triggerStockSync() throws IOException, InterruptedException { + String url = baseUrl + "/api/sync/stock"; + return postJson(url, new HashMap<>(), Map.class); + } + + /** + * Dispara sincronización de pedidos. + */ + public Map triggerOrderSync() throws IOException, InterruptedException { + String url = baseUrl + "/api/sync/orders"; + return postJson(url, new HashMap<>(), Map.class); + } + + // ── Helpers ────────────────────────────────────────────────────────────── + + private T getJson(String url, Class responseType) throws IOException, InterruptedException { + HttpResponse response = sendRequest("GET", url, null); + if (response.statusCode() == 200) { + return objectMapper.readValue(response.body(), responseType); + } + throw new IOException("HTTP " + response.statusCode() + ": " + response.body()); + } + + private T postJson(String url, Object body, Class responseType) + throws IOException, InterruptedException { + String jsonBody = objectMapper.writeValueAsString(body); + HttpResponse response = sendRequest("POST", url, jsonBody); + if (response.statusCode() == 200 || response.statusCode() == 201) { + return objectMapper.readValue(response.body(), responseType); + } + throw new IOException("HTTP " + response.statusCode() + ": " + response.body()); + } + + private HttpResponse sendRequest(String method, String url, String body) + throws IOException, InterruptedException { + HttpRequest.Builder builder = HttpRequest.newBuilder() + .uri(URI.create(url)) + .header("Authorization", authHeader) + .header("Content-Type", "application/json") + .method(method, body != null + ? HttpRequest.BodyPublishers.ofString(body) + : HttpRequest.BodyPublishers.noBody()); + + HttpRequest request = builder.build(); + return httpClient.send(request, HttpResponse.BodyHandlers.ofString()); + } + + private String buildBasicAuthHeader(String username, String password) { + String credentials = username + ":" + password; + String encoded = Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8)); + return "Basic " + encoded; + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.java new file mode 100644 index 0000000..9625931 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.java @@ -0,0 +1,60 @@ +package com.teterialosjuanjos.tfg.javafxclient.model; + +import com.teterialosjuanjos.tfg.javafxclient.api.SyncServiceClient; +import lombok.Getter; +import lombok.Setter; +import lombok.extern.slf4j.Slf4j; + +/** + * Gestiona la sesión actual: credenciales, cliente HTTP, estado de autenticación. + */ +@Slf4j +@Getter +@Setter +public class SessionManager { + + private String baseUrl; + private String username; + private String password; + private SyncServiceClient syncServiceClient; + private boolean authenticated; + + public SessionManager() { + this.authenticated = false; + // Default: Railway production + this.baseUrl = "https://proyectointermodular-production-a9c3.up.railway.app"; + } + + /** + * Intenta autenticarse contra el sync-service. + * Si es exitoso, inicializa el cliente HTTP. + * + * @return true si auth fue exitosa + */ + public boolean authenticate(String baseUrl, String username, String password) { + this.baseUrl = baseUrl; + this.username = username; + this.password = password; + + try { + this.syncServiceClient = new SyncServiceClient(baseUrl, username, password); + // Prueba conectividad + syncServiceClient.checkHealth(); + this.authenticated = true; + log.info("Authentication successful for user: {}", username); + return true; + } catch (Exception e) { + log.error("Authentication failed: {}", e.getMessage()); + this.authenticated = false; + return false; + } + } + + public void logout() { + this.authenticated = false; + this.syncServiceClient = null; + this.username = null; + this.password = null; + log.info("Logged out"); + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.java new file mode 100644 index 0000000..24fa348 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.model.dto; + +public class MappingDto { +} diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.java new file mode 100644 index 0000000..c2ea5b5 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.model.dto; + +public class OrderDto { +} diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.java new file mode 100644 index 0000000..791751d --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.model.dto; + +public class ProductDto { +} diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.java new file mode 100644 index 0000000..93be485 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.model.dto; + +public class SyncDto { +} diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.java new file mode 100644 index 0000000..ce7517a --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.java @@ -0,0 +1,232 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.controllers; + +import com.teterialosjuanjos.tfg.javafxclient.api.SyncServiceClient; +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.util.AlertUtil; +import javafx.animation.Animation; +import javafx.animation.KeyFrame; +import javafx.animation.Timeline; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.layout.GridPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.scene.control.Label; +import javafx.scene.paint.Color; +import javafx.util.Duration; +import lombok.extern.slf4j.Slf4j; + +import java.time.Instant; +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.List; +import java.util.Map; + +/** + * Dashboard con estado de sincronizaciones y estadísticas. + */ +@Slf4j +public class DashboardController { + + private final SessionManager sessionManager; + private Label lastProductSyncLabel; + private Label lastStockSyncLabel; + private Label lastOrderSyncLabel; + private Label successRateLabel; + private Label totalSyncsLabel; + + public DashboardController(SessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + public VBox createView() { + VBox root = new VBox(20); + root.setPadding(new Insets(20)); + root.setStyle("-fx-background-color: #fafafa;"); + + // Title + Label title = new Label("Synchronization Dashboard"); + title.setStyle("-fx-font-size: 18; -fx-font-weight: bold;"); + + // Sync stats cards + GridPane statsGrid = createStatsGrid(); + + // Action buttons + HBox actionsBox = createActionsBox(); + + root.getChildren().addAll( + title, + statsGrid, + actionsBox, + new Label() // spacer + ); + + // Auto-refresh cada 10 segundos + startAutoRefresh(); + + // Carga inicial + refreshDashboard(); + + return root; + } + + private GridPane createStatsGrid() { + GridPane grid = new GridPane(); + grid.setHgap(15); + grid.setVgap(15); + grid.setPadding(new Insets(10)); + grid.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0; -fx-border-radius: 4;"); + + // Card: Last Product Sync + VBox productCard = createStatCard( + "📦 Last Product Sync", + "Never", + "#1976d2" + ); + lastProductSyncLabel = (Label) productCard.getChildren().get(1); + GridPane.setConstraints(productCard, 0, 0); + + // Card: Last Stock Sync + VBox stockCard = createStatCard( + "📊 Last Stock Sync", + "Never", + "#388e3c" + ); + lastStockSyncLabel = (Label) stockCard.getChildren().get(1); + GridPane.setConstraints(stockCard, 1, 0); + + // Card: Last Order Sync + VBox orderCard = createStatCard( + "🛒 Last Order Sync", + "Never", + "#f57c00" + ); + lastOrderSyncLabel = (Label) orderCard.getChildren().get(1); + GridPane.setConstraints(orderCard, 2, 0); + + // Card: Total Syncs + VBox totalCard = createStatCard( + "📈 Total Syncs", + "0", + "#6a1b9a" + ); + totalSyncsLabel = (Label) totalCard.getChildren().get(1); + GridPane.setConstraints(totalCard, 0, 1); + + // Card: Success Rate + VBox rateCard = createStatCard( + "✓ Success Rate", + "0%", + "#00796b" + ); + successRateLabel = (Label) rateCard.getChildren().get(1); + GridPane.setConstraints(rateCard, 1, 1); + + grid.getChildren().addAll(productCard, stockCard, orderCard, totalCard, rateCard); + + return grid; + } + + private VBox createStatCard(String title, String value, String color) { + VBox card = new VBox(10); + card.setPadding(new Insets(15)); + card.setStyle( + "-fx-background-color: white; " + + "-fx-border-color: " + color + "; " + + "-fx-border-width: 3 0 0 0; " + + "-fx-border-radius: 0;" + ); + card.setMinWidth(250); + card.setAlignment(Pos.TOP_LEFT); + + Label titleLabel = new Label(title); + titleLabel.setStyle("-fx-font-size: 13; -fx-font-weight: bold; -fx-text-fill: " + color + ";"); + + Label valueLabel = new Label(value); + valueLabel.setStyle("-fx-font-size: 16; -fx-font-weight: bold; -fx-text-fill: #333333;"); + + card.getChildren().addAll(titleLabel, valueLabel); + + return card; + } + + private HBox createActionsBox() { + HBox box = new HBox(10); + box.setPadding(new Insets(15)); + box.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0;"); + box.setAlignment(Pos.CENTER_LEFT); + + Label label = new Label("Quick Actions:"); + label.setStyle("-fx-font-weight: bold;"); + + // Buttons + javafx.scene.control.Button refreshButton = new javafx.scene.control.Button("🔄 Refresh Now"); + refreshButton.setStyle("-fx-font-size: 11; -fx-padding: 8;"); + refreshButton.setOnAction(e -> refreshDashboard()); + + box.getChildren().addAll(label, refreshButton); + + return box; + } + + public void refreshDashboard() { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + List> logs = client.getLogs(null); + + Platform.runLater(() -> updateDashboard(logs)); + } catch (Exception e) { + log.error("Error refreshing dashboard: {}", e.getMessage()); + } + }); + thread.setDaemon(true); + thread.start(); + } + + private void updateDashboard(List> logs) { + if (logs.isEmpty()) { + totalSyncsLabel.setText("0"); + successRateLabel.setText("0%"); + return; + } + + totalSyncsLabel.setText(String.valueOf(logs.size())); + + // Busca último sync de cada tipo + for (Map log : logs) { + String syncType = (String) log.get("syncType"); + Instant startedAt = Instant.parse((String) log.get("startedAt")); + String timeStr = formatInstant(startedAt); + + switch (syncType) { + case "PRODUCT_PUSH" -> lastProductSyncLabel.setText(timeStr); + case "STOCK_PUSH" -> lastStockSyncLabel.setText(timeStr); + case "ORDER_PULL" -> lastOrderSyncLabel.setText(timeStr); + } + } + + // Success rate + long successCount = logs.stream() + .filter(l -> ((Number) l.get("itemsFailed")).longValue() == 0) + .count(); + long successRate = (successCount * 100) / logs.size(); + successRateLabel.setText(successRate + "%"); + } + + private void startAutoRefresh() { + Timeline timeline = new Timeline( + new KeyFrame(Duration.seconds(10), e -> refreshDashboard()) + ); + timeline.setCycleCount(Animation.INDEFINITE); + timeline.play(); + } + + private String formatInstant(Instant instant) { + LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneId.systemDefault()); + DateTimeFormatter formatter = DateTimeFormatter.ofPattern("HH:mm:ss"); + return ldt.format(formatter); + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.java new file mode 100644 index 0000000..8ecaf85 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.controllers; + +public class LoginController { +} diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.java new file mode 100644 index 0000000..89ac883 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.java @@ -0,0 +1,222 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.controllers; + +import com.teterialosjuanjos.tfg.javafxclient.api.SyncServiceClient; +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.util.AlertUtil; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Map; + +/** + * Tab para logs de sincronización. + */ +@Slf4j +public class LogsController { + + private final SessionManager sessionManager; + private TableView> logsTable; + private TextArea errorDetailsArea; + + public LogsController(SessionManager sessionManager) { + this.sessionManager = sessionManager; + } + + public VBox createView() { + VBox root = new VBox(15); + root.setPadding(new Insets(20)); + root.setStyle("-fx-background-color: #fafafa;"); + + // Title + Label title = new Label("Synchronization Logs"); + title.setStyle("-fx-font-size: 18; -fx-font-weight: bold;"); + + // Table + logsTable = createLogsTable(); + VBox.setVgrow(logsTable, javafx.scene.layout.Priority.ALWAYS); + logsTable.getSelectionModel().selectedItemProperty().addListener((obs, old, newVal) -> { + if (newVal != null) { + showLogDetails(newVal); + } + }); + + // Details panel + VBox detailsPanel = createDetailsPanel(); + + // Filter & Actions + HBox filterBox = createFilterBox(); + + root.getChildren().addAll(title, filterBox, logsTable, detailsPanel); + + // Load initial data + loadLogs(null); + + return root; + } + + private TableView> createLogsTable() { + TableView> table = new TableView<>(); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + table.setStyle("-fx-font-size: 11;"); + table.setPrefHeight(300); + + // ID column + TableColumn, String> idCol = new TableColumn<>("ID"); + idCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("id"); + return new javafx.beans.property.SimpleObjectProperty<>(String.valueOf(value)); + }); + idCol.setPrefWidth(50); + + // Type column + TableColumn, String> typeCol = new TableColumn<>("Type"); + typeCol.setCellValueFactory(cf -> { + String value = (String) cf.getValue().get("syncType"); + return new javafx.beans.property.SimpleObjectProperty<>(value); + }); + typeCol.setPrefWidth(120); + + // Started At column + TableColumn, String> startedCol = new TableColumn<>("Started At"); + startedCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("startedAt"); + return new javafx.beans.property.SimpleObjectProperty<>( + value != null ? String.valueOf(value).substring(0, 19) : "—" + ); + }); + startedCol.setPrefWidth(150); + + // Finished At column + TableColumn, String> finishedCol = new TableColumn<>("Finished At"); + finishedCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("finishedAt"); + return new javafx.beans.property.SimpleObjectProperty<>( + value != null ? String.valueOf(value).substring(0, 19) : "—" + ); + }); + finishedCol.setPrefWidth(150); + + // Processed column + TableColumn, String> processedCol = new TableColumn<>("Processed"); + processedCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("itemsProcessed"); + return new javafx.beans.property.SimpleObjectProperty<>(String.valueOf(value)); + }); + processedCol.setPrefWidth(80); + + // Failed column + TableColumn, String> failedCol = new TableColumn<>("Failed"); + failedCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("itemsFailed"); + return new javafx.beans.property.SimpleObjectProperty<>(String.valueOf(value)); + }); + failedCol.setPrefWidth(80); + failedCol.setCellFactory(col -> new TableCell, String>() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null || "0".equals(item)) { + setText(item); + setStyle(""); + } else { + setText(item); + setStyle("-fx-text-fill: #c62828; -fx-font-weight: bold;"); + } + } + }); + + table.getColumns().addAll(idCol, typeCol, startedCol, finishedCol, processedCol, failedCol); + + return table; + } + + private VBox createDetailsPanel() { + VBox panel = new VBox(10); + panel.setPadding(new Insets(10)); + panel.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0;"); + + Label label = new Label("Error Details (click a row to see details):"); + label.setStyle("-fx-font-weight: bold;"); + + errorDetailsArea = new TextArea(); + errorDetailsArea.setEditable(false); + errorDetailsArea.setWrapText(true); + errorDetailsArea.setPrefHeight(120); + errorDetailsArea.setStyle("-fx-font-size: 10; -fx-control-inner-background: #f5f5f5;"); + + panel.getChildren().addAll(label, errorDetailsArea); + + return panel; + } + + private HBox createFilterBox() { + HBox box = new HBox(10); + box.setPadding(new Insets(10)); + box.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0;"); + box.setAlignment(Pos.CENTER_LEFT); + + Label filterLabel = new Label("Filter by type:"); + + ComboBox typeCombo = new ComboBox<>(); + typeCombo.setItems(FXCollections.observableArrayList( + "All", "PRODUCT_PUSH", "STOCK_PUSH", "ORDER_PULL" + )); + typeCombo.setValue("All"); + typeCombo.setPrefWidth(140); + + typeCombo.setOnAction(e -> { + String selected = typeCombo.getValue(); + loadLogs("All".equals(selected) ? null : selected); + }); + + Region spacer = new Region(); + HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + Button refreshButton = new Button("🔄 Refresh"); + refreshButton.setStyle("-fx-font-size: 11; -fx-padding: 8;"); + refreshButton.setOnAction(e -> loadLogs(null)); + + box.getChildren().addAll(filterLabel, typeCombo, spacer, refreshButton); + + return box; + } + + private void loadLogs(String type) { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + List> logs = client.getLogs(type); + + ObservableList> items = FXCollections.observableArrayList(logs); + Platform.runLater(() -> logsTable.setItems(items)); + } catch (Exception e) { + log.error("Error loading logs: {}", e.getMessage()); + Platform.runLater(() -> AlertUtil.showError("Error", "Failed to load logs: " + e.getMessage())); + } + }); + thread.setDaemon(true); + thread.start(); + } + + private void showLogDetails(Map log) { + String errorDetails = (String) log.get("errorDetails"); + if (errorDetails != null && !errorDetails.isEmpty()) { + errorDetailsArea.setText(errorDetails); + } else { + int processed = ((Number) log.get("itemsProcessed")).intValue(); + int failed = ((Number) log.get("itemsFailed")).intValue(); + errorDetailsArea.setText("✓ Sync completed successfully\n" + + "Processed: " + processed + "\n" + + "Failed: " + failed); + } + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.java new file mode 100644 index 0000000..1f17b0d --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.java @@ -0,0 +1,225 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.controllers; + +import com.teterialosjuanjos.tfg.javafxclient.api.SyncServiceClient; +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.util.AlertUtil; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.VBox; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Map; + +/** + * Tab para pedidos: ver mapeos de PrestaShop → Dolibarr y disparar sync. + */ +@Slf4j +public class OrdersController { + + private final SessionManager sessionManager; + private final DashboardController dashboardController; + private TableView> ordersTable; + + public OrdersController(SessionManager sessionManager, DashboardController dashboardController) { + this.sessionManager = sessionManager; + this.dashboardController = dashboardController; + } + + public VBox createView() { + VBox root = new VBox(15); + root.setPadding(new Insets(20)); + root.setStyle("-fx-background-color: #fafafa;"); + + // Title + Label title = new Label("Order Mappings (PrestaShop → Dolibarr)"); + title.setStyle("-fx-font-size: 18; -fx-font-weight: bold;"); + + // Info box + HBox infoBox = createInfoBox(); + + // Table + ordersTable = createOrdersTable(); + VBox.setVgrow(ordersTable, javafx.scene.layout.Priority.ALWAYS); + + // Action buttons + HBox actionBox = createActionBox(); + + root.getChildren().addAll(title, infoBox, ordersTable, actionBox); + + // Load initial data + loadOrders(); + + return root; + } + + private HBox createInfoBox() { + HBox box = new HBox(10); + box.setPadding(new Insets(10)); + box.setStyle("-fx-background-color: #e3f2fd; -fx-border-color: #1976d2; -fx-border-width: 1;"); + box.setAlignment(Pos.CENTER_LEFT); + + Label infoLabel = new Label( + "ℹ Orders from PrestaShop are automatically imported into Dolibarr as sales orders and invoices." + ); + infoLabel.setStyle("-fx-font-size: 11; -fx-text-fill: #1565c0;"); + infoLabel.setWrapText(true); + + box.getChildren().add(infoLabel); + + return box; + } + + private TableView> createOrdersTable() { + TableView> table = new TableView<>(); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + table.setStyle("-fx-font-size: 11;"); + + // PrestaShop Order ID column + TableColumn, String> psOrderIdCol = new TableColumn<>("PS Order ID"); + psOrderIdCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("prestashopOrderId"); + return new javafx.beans.property.SimpleObjectProperty<>(String.valueOf(value)); + }); + psOrderIdCol.setPrefWidth(120); + + // Dolibarr Order ID column + TableColumn, String> dolOrderIdCol = new TableColumn<>("Dolibarr Order ID"); + dolOrderIdCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("dolibarrOrderId"); + return new javafx.beans.property.SimpleObjectProperty<>( + value != null ? String.valueOf(value) : "—" + ); + }); + dolOrderIdCol.setPrefWidth(120); + + // Dolibarr Invoice ID column + TableColumn, String> dolInvoiceIdCol = new TableColumn<>("Invoice ID"); + dolInvoiceIdCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("dolibarrInvoiceId"); + return new javafx.beans.property.SimpleObjectProperty<>( + value != null ? String.valueOf(value) : "—" + ); + }); + dolInvoiceIdCol.setPrefWidth(100); + + // Status column + TableColumn, String> statusCol = new TableColumn<>("Status"); + statusCol.setCellValueFactory(cf -> { + String value = (String) cf.getValue().get("status"); + return new javafx.beans.property.SimpleObjectProperty<>(value); + }); + statusCol.setPrefWidth(100); + statusCol.setCellFactory(col -> new TableCell, String>() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setText(null); + setStyle(""); + } else { + setText(item); + if ("INVOICED".equals(item)) { + setStyle("-fx-text-fill: #2e7d32; -fx-font-weight: bold;"); + } + } + } + }); + + // Imported At column + TableColumn, String> importedAtCol = new TableColumn<>("Imported At"); + importedAtCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("importedAt"); + return new javafx.beans.property.SimpleObjectProperty<>( + value != null ? String.valueOf(value).substring(0, 19) : "—" + ); + }); + importedAtCol.setPrefWidth(150); + + table.getColumns().addAll(psOrderIdCol, dolOrderIdCol, dolInvoiceIdCol, statusCol, importedAtCol); + + return table; + } + + private HBox createActionBox() { + HBox box = new HBox(10); + box.setPadding(new Insets(15)); + box.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0;"); + box.setAlignment(Pos.CENTER_LEFT); + + Label label = new Label("Actions:"); + label.setStyle("-fx-font-weight: bold;"); + + Button syncButton = new Button("🔄 Sync Orders from PrestaShop"); + syncButton.setStyle("-fx-font-size: 12; -fx-padding: 10; -fx-background-color: #f57c00; -fx-text-fill: white;"); + syncButton.setOnAction(e -> triggerOrderSync()); + + Button refreshButton = new Button("🔄 Refresh"); + refreshButton.setStyle("-fx-font-size: 11; -fx-padding: 8;"); + refreshButton.setOnAction(e -> loadOrders()); + + box.getChildren().addAll(label, syncButton, refreshButton); + + return box; + } + + private void loadOrders() { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + List> mappings = client.getOrderMappings(); + + ObservableList> items = FXCollections.observableArrayList(mappings); + Platform.runLater(() -> ordersTable.setItems(items)); + } catch (Exception e) { + log.error("Error loading orders: {}", e.getMessage()); + Platform.runLater(() -> AlertUtil.showError("Error", "Failed to load orders: " + e.getMessage())); + } + }); + thread.setDaemon(true); + thread.start(); + } + + private void triggerOrderSync() { + Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION); + confirmAlert.setTitle("Confirm Sync"); + confirmAlert.setHeaderText("Sync Orders from PrestaShop to Dolibarr?"); + confirmAlert.setContentText("This will import new orders from PrestaShop as sales orders and invoices in Dolibarr."); + + if (confirmAlert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + Map result = client.triggerOrderSync(); + + Platform.runLater(() -> { + int processed = ((Number) result.get("processed")).intValue(); + int failed = ((Number) result.get("failed")).intValue(); + + if (failed == 0) { + AlertUtil.showInfo("Success", + "Orders synced: " + processed + " orders imported"); + } else { + AlertUtil.showWarn("Partial Success", + "Processed: " + processed + ", Failed: " + failed); + } + + loadOrders(); + dashboardController.refreshDashboard(); + }); + } catch (Exception e) { + Platform.runLater(() -> + AlertUtil.showError("Sync Error", e.getMessage()) + ); + } + }); + thread.setDaemon(true); + thread.start(); + } + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.java new file mode 100644 index 0000000..02df466 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.java @@ -0,0 +1,271 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.controllers; + +import com.teterialosjuanjos.tfg.javafxclient.api.SyncServiceClient; +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.util.AlertUtil; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.*; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.collections.FXCollections; +import javafx.collections.ObservableList; +import lombok.extern.slf4j.Slf4j; + +import java.util.*; + +/** + * Tab para productos: ver mapeos y disparar sync. + */ +@Slf4j +public class ProductsController { + + private final SessionManager sessionManager; + private final DashboardController dashboardController; + private TableView> productsTable; + + public ProductsController(SessionManager sessionManager, DashboardController dashboardController) { + this.sessionManager = sessionManager; + this.dashboardController = dashboardController; + } + + public VBox createView() { + VBox root = new VBox(15); + root.setPadding(new Insets(20)); + root.setStyle("-fx-background-color: #fafafa;"); + + // Title + Label title = new Label("Product Mappings"); + title.setStyle("-fx-font-size: 18; -fx-font-weight: bold;"); + + // Filter & Actions + HBox filterBox = createFilterBox(); + + // Table + productsTable = createProductsTable(); + VBox.setVgrow(productsTable, javafx.scene.layout.Priority.ALWAYS); + + // Action buttons + HBox actionBox = createActionBox(); + + root.getChildren().addAll(title, filterBox, productsTable, actionBox); + + // Load initial data + loadProducts(null); + + return root; + } + + private HBox createFilterBox() { + HBox box = new HBox(10); + box.setPadding(new Insets(10)); + box.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0;"); + box.setAlignment(Pos.CENTER_LEFT); + + Label filterLabel = new Label("Filter by status:"); + + ComboBox statusCombo = new ComboBox<>(); + statusCombo.setItems(FXCollections.observableArrayList( + "All", "SYNCED", "PENDING", "ERROR" + )); + statusCombo.setValue("All"); + statusCombo.setPrefWidth(120); + + statusCombo.setOnAction(e -> { + String selected = statusCombo.getValue(); + loadProducts("All".equals(selected) ? null : selected); + }); + + Region spacer = new Region(); + HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + Button refreshButton = new Button("🔄 Refresh"); + refreshButton.setStyle("-fx-font-size: 11; -fx-padding: 8;"); + refreshButton.setOnAction(e -> loadProducts(null)); + + box.getChildren().addAll(filterLabel, statusCombo, spacer, refreshButton); + + return box; + } + + private TableView> createProductsTable() { + TableView> table = new TableView<>(); + table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY_ALL_COLUMNS); + table.setStyle("-fx-font-size: 11;"); + + // SKU column + TableColumn, String> skuCol = new TableColumn<>("SKU"); + skuCol.setCellValueFactory(cf -> { + String value = (String) cf.getValue().get("sku"); + return new javafx.beans.property.SimpleObjectProperty<>(value); + }); + skuCol.setPrefWidth(120); + + // Dolibarr ID column + TableColumn, String> dolIdCol = new TableColumn<>("Dolibarr ID"); + dolIdCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("dolibarrId"); + return new javafx.beans.property.SimpleObjectProperty<>(String.valueOf(value)); + }); + dolIdCol.setPrefWidth(100); + + // PrestaShop ID column + TableColumn, String> psIdCol = new TableColumn<>("PrestaShop ID"); + psIdCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("prestashopId"); + return new javafx.beans.property.SimpleObjectProperty<>(String.valueOf(value)); + }); + psIdCol.setPrefWidth(100); + + // Status column + TableColumn, String> statusCol = new TableColumn<>("Status"); + statusCol.setCellValueFactory(cf -> { + String value = (String) cf.getValue().get("syncStatus"); + return new javafx.beans.property.SimpleObjectProperty<>(value); + }); + statusCol.setPrefWidth(80); + statusCol.setCellFactory(col -> new TableCell, String>() { + @Override + protected void updateItem(String item, boolean empty) { + super.updateItem(item, empty); + if (empty || item == null) { + setText(null); + setStyle(""); + } else { + setText(item); + switch (item) { + case "SYNCED" -> setStyle("-fx-text-fill: #2e7d32; -fx-font-weight: bold;"); + case "PENDING" -> setStyle("-fx-text-fill: #f57f17; -fx-font-weight: bold;"); + case "ERROR" -> setStyle("-fx-text-fill: #c62828; -fx-font-weight: bold;"); + } + } + } + }); + + // Error message column + TableColumn, String> errorCol = new TableColumn<>("Error"); + errorCol.setCellValueFactory(cf -> { + Object value = cf.getValue().get("errorMessage"); + String text = value != null ? (String) value : ""; + return new javafx.beans.property.SimpleObjectProperty<>(text); + }); + errorCol.setPrefWidth(200); + + table.getColumns().addAll(skuCol, dolIdCol, psIdCol, statusCol, errorCol); + + return table; + } + + private HBox createActionBox() { + HBox box = new HBox(10); + box.setPadding(new Insets(15)); + box.setStyle("-fx-background-color: white; -fx-border-color: #e0e0e0;"); + box.setAlignment(Pos.CENTER_RIGHT); + + Button syncButton = new Button("🔄 Sync Products from Dolibarr"); + syncButton.setStyle("-fx-font-size: 12; -fx-padding: 10; -fx-background-color: #1976d2; -fx-text-fill: white;"); + syncButton.setOnAction(e -> triggerProductSync()); + + Button stockButton = new Button("📊 Sync Stock"); + stockButton.setStyle("-fx-font-size: 12; -fx-padding: 10; -fx-background-color: #388e3c; -fx-text-fill: white;"); + stockButton.setOnAction(e -> triggerStockSync()); + + box.getChildren().addAll(syncButton, stockButton); + + return box; + } + + private void loadProducts(String status) { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + List> mappings = client.getProductMappings(status); + + ObservableList> items = FXCollections.observableArrayList(mappings); + Platform.runLater(() -> productsTable.setItems(items)); + } catch (Exception e) { + log.error("Error loading products: {}", e.getMessage()); + Platform.runLater(() -> AlertUtil.showError("Error", "Failed to load products: " + e.getMessage())); + } + }); + thread.setDaemon(true); + thread.start(); + } + + private void triggerProductSync() { + Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION); + confirmAlert.setTitle("Confirm Sync"); + confirmAlert.setHeaderText("Sync Products from Dolibarr to PrestaShop?"); + confirmAlert.setContentText("This will push new/modified products from Dolibarr to PrestaShop."); + + if (confirmAlert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + Map result = client.triggerProductSync(); + + Platform.runLater(() -> { + int processed = ((Number) result.get("processed")).intValue(); + int failed = ((Number) result.get("failed")).intValue(); + + if (failed == 0) { + AlertUtil.showInfo("Success", + "Sync completed: " + processed + " products processed"); + } else { + AlertUtil.showWarn("Partial Success", + "Processed: " + processed + ", Failed: " + failed); + } + + loadProducts(null); + dashboardController.refreshDashboard(); + }); + } catch (Exception e) { + Platform.runLater(() -> + AlertUtil.showError("Sync Error", e.getMessage()) + ); + } + }); + thread.setDaemon(true); + thread.start(); + } + } + + private void triggerStockSync() { + Alert confirmAlert = new Alert(Alert.AlertType.CONFIRMATION); + confirmAlert.setTitle("Confirm Sync"); + confirmAlert.setHeaderText("Sync Stock from Dolibarr to PrestaShop?"); + confirmAlert.setContentText("This will update stock levels in PrestaShop from Dolibarr."); + + if (confirmAlert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { + Thread thread = new Thread(() -> { + try { + SyncServiceClient client = sessionManager.getSyncServiceClient(); + Map result = client.triggerStockSync(); + + Platform.runLater(() -> { + int processed = ((Number) result.get("processed")).intValue(); + int failed = ((Number) result.get("failed")).intValue(); + + if (failed == 0) { + AlertUtil.showInfo("Success", + "Stock sync completed: " + processed + " items updated"); + } else { + AlertUtil.showWarn("Partial Success", + "Processed: " + processed + ", Failed: " + failed); + } + + dashboardController.refreshDashboard(); + }); + } catch (Exception e) { + Platform.runLater(() -> + AlertUtil.showError("Sync Error", e.getMessage()) + ); + } + }); + thread.setDaemon(true); + thread.start(); + } + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.java new file mode 100644 index 0000000..d8b6ebe --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.java @@ -0,0 +1,173 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.stages; + +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.util.AlertUtil; +import javafx.application.Platform; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import lombok.extern.slf4j.Slf4j; + +/** + * Pantalla de login para autenticación contra el sync-service. + */ +@Slf4j +public class LoginStage { + + private final SessionManager sessionManager; + private final Stage primaryStage; + private Stage loginWindow; + + public LoginStage(SessionManager sessionManager, Stage primaryStage) { + this.sessionManager = sessionManager; + this.primaryStage = primaryStage; + } + + public void show() { + loginWindow = new Stage(); + loginWindow.setTitle("TFG Sync Service — Login"); + loginWindow.setWidth(400); + loginWindow.setHeight(500); + loginWindow.setResizable(false); + + VBox root = createLoginForm(); + + Scene scene = new Scene(root); + scene.getStylesheets().add(getClass().getResource("/styles.css").toExternalForm()); + + loginWindow.setScene(scene); + loginWindow.setOnCloseRequest(e -> Platform.exit()); + loginWindow.show(); + } + + private VBox createLoginForm() { + VBox form = new VBox(15); + form.setPadding(new Insets(40)); + form.setAlignment(Pos.TOP_CENTER); + form.setStyle("-fx-border-color: #e0e0e0; -fx-border-width: 0;"); + + // Title + Label title = new Label("Sync Service Client"); + title.setStyle("-fx-font-size: 24; -fx-font-weight: bold; -fx-text-fill: #1976d2;"); + + Label subtitle = new Label("Dolibarr ↔ PrestaShop Integration"); + subtitle.setStyle("-fx-font-size: 12; -fx-text-fill: #666666;"); + + // URL input + Label urlLabel = new Label("Base URL:"); + urlLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 11;"); + TextField urlField = new TextField(); + urlField.setText("https://proyectointermodular-production-a9c3.up.railway.app"); + urlField.setStyle("-fx-font-size: 12; -fx-padding: 8;"); + + // Username input + Label usernameLabel = new Label("Username:"); + usernameLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 11;"); + TextField usernameField = new TextField(); + usernameField.setPromptText("admin"); + usernameField.setText("admin"); + usernameField.setStyle("-fx-font-size: 12; -fx-padding: 8;"); + + // Password input + Label passwordLabel = new Label("Password:"); + passwordLabel.setStyle("-fx-font-weight: bold; -fx-font-size: 11;"); + PasswordField passwordField = new PasswordField(); + passwordField.setPromptText("••••••••"); + passwordField.setText("admin123"); + passwordField.setStyle("-fx-font-size: 12; -fx-padding: 8;"); + + // Login button + Button loginButton = new Button("Login"); + loginButton.setStyle( + "-fx-font-size: 12; " + + "-fx-padding: 10; " + + "-fx-background-color: #1976d2; " + + "-fx-text-fill: white; " + + "-fx-font-weight: bold; " + + "-fx-cursor: hand;" + ); + loginButton.setPrefWidth(Double.MAX_VALUE); + + loginButton.setOnAction(e -> handleLogin( + urlField.getText(), + usernameField.getText(), + passwordField.getText() + )); + + // Dev mode toggle + CheckBox devModeCheckBox = new CheckBox("Use local dev server (http://localhost:8080)"); + devModeCheckBox.setStyle("-fx-font-size: 11;"); + devModeCheckBox.selectedProperty().addListener((obs, old, newVal) -> { + if (newVal) { + urlField.setText("http://localhost:8080"); + } else { + urlField.setText("https://proyectointermodular-production-a9c3.up.railway.app"); + } + }); + + // Separator + Separator separator = new Separator(); + + // Info label + Label infoLabel = new Label("Tip: Default credentials are admin / admin123"); + infoLabel.setStyle("-fx-font-size: 10; -fx-text-fill: #999999; -fx-font-style: italic;"); + + form.getChildren().addAll( + title, + subtitle, + new Separator(), + urlLabel, + urlField, + usernameLabel, + usernameField, + passwordLabel, + passwordField, + separator, + devModeCheckBox, + new Region(), // spacer + loginButton, + infoLabel + ); + + return form; + } + + private void handleLogin(String baseUrl, String username, String password) { + if (baseUrl.isEmpty() || username.isEmpty() || password.isEmpty()) { + AlertUtil.showError("Validation Error", "All fields are required"); + return; + } + + // Ejecuta en background para no freezear UI + Thread loginThread = new Thread(() -> { + try { + boolean success = sessionManager.authenticate(baseUrl, username, password); + Platform.runLater(() -> { + if (success) { + AlertUtil.showInfo("Success", "Authentication successful!"); + loginWindow.close(); + openMainStage(); + } else { + AlertUtil.showError("Login Failed", + "Could not authenticate. Check credentials and URL."); + } + }); + } catch (Exception ex) { + Platform.runLater(() -> { + AlertUtil.showError("Connection Error", ex.getMessage()); + }); + } + }); + loginThread.setDaemon(true); + loginThread.start(); + } + + private void openMainStage() { + MainStage mainStage = new MainStage(sessionManager, primaryStage); + mainStage.show(); + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.java new file mode 100644 index 0000000..4897e33 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.java @@ -0,0 +1,139 @@ +package com.teterialosjuanjos.tfg.javafxclient.ui.stages; + +import com.teterialosjuanjos.tfg.javafxclient.model.SessionManager; +import com.teterialosjuanjos.tfg.javafxclient.ui.controllers.*; +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.Scene; +import javafx.scene.control.*; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import javafx.scene.layout.Region; +import javafx.scene.layout.VBox; +import javafx.stage.Stage; +import lombok.extern.slf4j.Slf4j; + +/** + * Pantalla principal con navegación por tabs y dashboard. + */ +@Slf4j +public class MainStage { + + private final SessionManager sessionManager; + private final Stage primaryStage; + private Stage mainWindow; + + private DashboardController dashboardController; + private ProductsController productsController; + private OrdersController ordersController; + private LogsController logsController; + + public MainStage(SessionManager sessionManager, Stage primaryStage) { + this.sessionManager = sessionManager; + this.primaryStage = primaryStage; + } + + public void show() { + mainWindow = new Stage(); + mainWindow.setTitle("TFG Sync Service — Dashboard"); + mainWindow.setWidth(1200); + mainWindow.setHeight(700); + + BorderPane root = createMainLayout(); + + Scene scene = new Scene(root); + scene.getStylesheets().add(getClass().getResource("/styles.css").toExternalForm()); + + mainWindow.setScene(scene); + mainWindow.show(); + } + + private BorderPane createMainLayout() { + BorderPane root = new BorderPane(); + + // Top: Header con usuario y logout + root.setTop(createHeader()); + + // Center: Tabs + root.setCenter(createTabPane()); + + return root; + } + + private VBox createHeader() { + VBox header = new VBox(); + header.setPadding(new Insets(15)); + header.setStyle("-fx-background-color: #1976d2; -fx-border-color: #1565c0; -fx-border-width: 0 0 2 0;"); + + HBox topBar = new HBox(10); + topBar.setAlignment(Pos.CENTER_LEFT); + + Label title = new Label("Sync Service Control Panel"); + title.setStyle("-fx-font-size: 18; -fx-font-weight: bold; -fx-text-fill: white;"); + + Region spacer = new Region(); + HBox.setHgrow(spacer, javafx.scene.layout.Priority.ALWAYS); + + Label userLabel = new Label("User: " + sessionManager.getUsername()); + userLabel.setStyle("-fx-font-size: 12; -fx-text-fill: white;"); + + Button logoutButton = new Button("Logout"); + logoutButton.setStyle("-fx-font-size: 11; -fx-padding: 5;"); + logoutButton.setOnAction(e -> handleLogout()); + + topBar.getChildren().addAll(title, spacer, userLabel, logoutButton); + + HBox statusBar = new HBox(20); + statusBar.setPadding(new Insets(10, 15, 10, 15)); + statusBar.setStyle("-fx-background-color: #f5f5f5;"); + + Label statusLabel = new Label("✓ Connected to: " + sessionManager.getBaseUrl()); + statusLabel.setStyle("-fx-font-size: 11; -fx-text-fill: #2e7d32;"); + + statusBar.getChildren().add(statusLabel); + + header.getChildren().addAll(topBar, statusBar); + + return header; + } + + private TabPane createTabPane() { + TabPane tabPane = new TabPane(); + tabPane.setTabClosingPolicy(TabPane.TabClosingPolicy.UNAVAILABLE); + tabPane.setStyle("-fx-font-size: 12;"); + + // Dashboard tab + dashboardController = new DashboardController(sessionManager); + Tab dashboardTab = new Tab("📊 Dashboard", dashboardController.createView()); + dashboardTab.setStyle("-fx-padding: 10;"); + + // Products tab + productsController = new ProductsController(sessionManager, dashboardController); + Tab productsTab = new Tab("📦 Products", productsController.createView()); + + // Orders tab + ordersController = new OrdersController(sessionManager, dashboardController); + Tab ordersTab = new Tab("🛒 Orders", ordersController.createView()); + + // Logs tab + logsController = new LogsController(sessionManager); + Tab logsTab = new Tab("📋 Logs", logsController.createView()); + + tabPane.getTabs().addAll(dashboardTab, productsTab, ordersTab, logsTab); + + return tabPane; + } + + private void handleLogout() { + Alert alert = new Alert(Alert.AlertType.CONFIRMATION); + alert.setTitle("Logout"); + alert.setHeaderText("Are you sure?"); + alert.setContentText("You will be logged out and returned to the login screen."); + + if (alert.showAndWait().orElse(ButtonType.CANCEL) == ButtonType.OK) { + sessionManager.logout(); + mainWindow.close(); + new LoginStage(sessionManager, primaryStage).show(); + } + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.java new file mode 100644 index 0000000..16c9c46 --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.java @@ -0,0 +1,34 @@ +package com.teterialosjuanjos.tfg.javafxclient.util; + +import javafx.scene.control.Alert; +import javafx.scene.control.ButtonType; + +/** + * Utilidades para mostrar diálogos de alerta. + */ +public class AlertUtil { + + public static void showInfo(String title, String message) { + Alert alert = new Alert(Alert.AlertType.INFORMATION); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } + + public static void showWarn(String title, String message) { + Alert alert = new Alert(Alert.AlertType.WARNING); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } + + public static void showError(String title, String message) { + Alert alert = new Alert(Alert.AlertType.ERROR); + alert.setTitle(title); + alert.setHeaderText(null); + alert.setContentText(message); + alert.showAndWait(); + } +} \ No newline at end of file diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.java new file mode 100644 index 0000000..7667a4b --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.util; + +public class FormatUtil { +} diff --git a/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.java b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.java new file mode 100644 index 0000000..77060fe --- /dev/null +++ b/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.java @@ -0,0 +1,4 @@ +package com.teterialosjuanjos.tfg.javafxclient.util; + +public class TaskExecutor { +} diff --git a/javafx-client/src/main/resources/applications.properties b/javafx-client/src/main/resources/applications.properties new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/src/main/resources/fxml/dashboard.fxml b/javafx-client/src/main/resources/fxml/dashboard.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/src/main/resources/fxml/login.fxml b/javafx-client/src/main/resources/fxml/login.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/src/main/resources/fxml/logs.fxml b/javafx-client/src/main/resources/fxml/logs.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/src/main/resources/fxml/orders.fxml b/javafx-client/src/main/resources/fxml/orders.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/src/main/resources/fxml/products.fxml b/javafx-client/src/main/resources/fxml/products.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/src/main/resources/logback.xml b/javafx-client/src/main/resources/logback.xml new file mode 100644 index 0000000..78a646b --- /dev/null +++ b/javafx-client/src/main/resources/logback.xml @@ -0,0 +1,14 @@ + + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/javafx-client/src/main/resources/styles.css b/javafx-client/src/main/resources/styles.css new file mode 100644 index 0000000..75a7960 --- /dev/null +++ b/javafx-client/src/main/resources/styles.css @@ -0,0 +1,93 @@ +/* Material Design Inspired CSS for TFG Sync Service Client */ + +* { + -fx-font-family: "Segoe UI", "Ubuntu", sans-serif; +} + +.root { + -fx-background-color: #fafafa; + -fx-font-size: 12; +} + +/* Buttons */ +.button { + -fx-padding: 8; + -fx-font-size: 12; + -fx-text-fill: #333; + -fx-background-color: #e0e0e0; + -fx-background-radius: 4; +} + +.button:hover { + -fx-background-color: #d0d0d0; +} + +/* Text Fields */ +.text-field, +.password-field { + -fx-padding: 8; + -fx-border-color: #bdbdbd; + -fx-border-width: 0 0 1 0; + -fx-font-size: 12; +} + +/* Labels */ +.label { + -fx-text-fill: #333; +} + +/* Tables */ +.table-view { + -fx-background-color: white; + -fx-border-color: #e0e0e0; + -fx-font-size: 11; +} + +.table-view .column-header { + -fx-background-color: #f5f5f5; + -fx-font-weight: bold; +} + +.table-row-cell { + -fx-font-size: 11; + -fx-background-color: white; +} + +/* ComboBox */ +.combo-box, +.combo-box-base { + -fx-background-color: white; + -fx-border-color: #bdbdbd; + -fx-padding: 5; +} + +/* TextArea */ +.text-area { + -fx-background-color: #f5f5f5; + -fx-text-fill: #333; + -fx-font-size: 10; +} + +/* Separator */ +.separator { + -fx-text-fill: #e0e0e0; +} + +/* TabPane */ +.tab-pane { + -fx-padding: 0; +} + +.tab { + -fx-padding: 10; +} + +.tab-header-background { + -fx-background-color: #f5f5f5; +} + +/* CheckBox */ +.check-box { + -fx-font-size: 11; + -fx-text-fill: #333; +} \ No newline at end of file diff --git a/javafx-client/target/classes/applications.properties b/javafx-client/target/classes/applications.properties new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.class new file mode 100644 index 0000000000000000000000000000000000000000..179aa6fa2746aa964573bde16959f8626a0d453a GIT binary patch literal 1442 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 literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.class new file mode 100644 index 0000000000000000000000000000000000000000..1a16dee040a4024536a00bfc65498e9f505ac9dc GIT binary patch literal 353 zcmbtQOHRWu5Pg$R1EG+L8?XZw_y7eY1Y#BFqNsZ(b%P_vt`Y}vEmlY@H~@!2j4LD# zz>M@}-W$zF^Y#7l3E&2^91-FS$rLFjgjp+Jq%hJpVo|lK_Jql$w%T74;?uKLhCRZ( zcAfCbt6odvhIW*;b%XGmMuhgwYi+c$UUY7)j97ao9^`hbZFA=xA-QFlP?XxL$FZwa z|0FBJ%5mvxX;zY_GXE75|EveXRr!ydb3(p!V_&O#9TpXT=Ujw?ae|EB5RBOQ5sdrH h#Et{$`8(*1g$OxgC<8GL7^k~D<#@=9aKsp)_yMAoSZDwM literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.class new file mode 100644 index 0000000000000000000000000000000000000000..a572b01d1a433fae89fc949644bbb82da0dfacbb GIT binary patch literal 347 zcmbVIOHRWu5Pg$R1EHlAH(&=W@ER2%ArPyGEZF1T zFeAO0_eS&4eE)oY0l3AYM1r(HHbssJVbRK0DXer`v8r3GB4KiEoQXGt^z3|H;DB(Y zd?%ugIv8nv-wx8Xz87(`6=VD6O~ypnb;c_#B;F^eyzCoHRfZ(I_}CqD$GAI!LB_IJwVcq&d&@B>C8c76flAv3Y( fKz{K7dS@X)$vBpQ6i1BHeV%iiF(Vu^CYbL4yFyj+ literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.class new file mode 100644 index 0000000000000000000000000000000000000000..d6104840680adab3ba9c445dcb8e4a01d63a8b32 GIT binary patch literal 353 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 literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.class new file mode 100644 index 0000000000000000000000000000000000000000..5a2fdc836ca706c8929e3e0547c46fb2d864299a GIT binary patch literal 378 zcmb_Y%Sr?>5UifZj*hFlh(F*>J(v$r1Xgn=wTgtvxp5|55b~qrWrZ!m zmGWy5b=1K~>xX`nuJ?n8t4^fns~2UBc2SH*DDPtM*6J{b#&^aoeg#6Y@Q<1>YmC#k z$WSw;GLU13ak|M%zIT}s_82qFz5xblV|f4o literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController$1.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController$1.class new file mode 100644 index 0000000000000000000000000000000000000000..141fd6d484709d19f5af13db82d70ccd081cb5e7 GIT binary patch literal 1506 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 Z8cO*vU9Cw1a0ABipSD@ssx?sQ{{vssQa1nq literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController$1.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController$1.class new file mode 100644 index 0000000000000000000000000000000000000000..19b2afb48e0324be7324fff0b1bb45d34844f2da GIT binary patch literal 1845 zcmb_d&2Jk;6#q?Z#~XL!xSKlRBMolqLcGS=I&G5>hfwOIO_1Y8aTST&HtTV+VZ9r6 z$0X$h5+CAALcpa+T&V=jflDfJK-`e{54dsU%#8zxH|s4#CE=8nX5YNG^S*xXz1g4t z`u+z1FJn1{9>kJ}t4N@iVRXlQ!))yvfyG^JSe`3<&oSrz=!(T5a6s*YRl?{1bMSl`$6$8jHykdE6LvX=; z+jP8Or(?Q1USNpUmeCEva%}Djqhmjc#NmEmtomNFV~L>JjgDmbGD9-p;tt;z4B4a3 z8^X8Utx92CfyMwC!8sLK>0Tj?hPdnS6o&Cc5>Kjl3Qsd+{tdmWO-HxQV7uxyd6@UI zg7bt#QhJuR}=jvj8VYz-w!6k;o;@aBnH3iQx zOq5#tB|!<4TDIfN<}Ob1>E@NmN>0jKlxk^@+gsaWHn-_H&5DA;F*>bl)2;IK$vVYj zbgZe#naP=xbR=LUS>lPusu}@HRb^NiuboyYsZtE_F4O}x+vW96dz1TN2Qp|i&oZ5L zlNyFPG7`qcwoRDVPA!Ifxk9y+R`ZvsOHa)%S-ZMGX*=Y2_`kzAA;*VdV9~W`sA>4RDJM~7SURmXQL%`d41<=> z>C$-=M1;)Pc;SEWShx8erJ>Y$AeOvX}PCkZ)FgdqeLRI$O_~kL7xuv z;>bfJN4`X|_!G|R>=0*k?d%~2@8fg5NL;brGZil#V(43Gwni)V*@Iu-Bs$hJlHDU( zj{W}2Z$z(`w2?!M-a|e%6~B+sk~S8y*|=l}N?JZ-dlOUfZ2TKipr;$GOb8gEb&1x5 zu00zDpy}Fpxare1oi;M_SeUs^w<+)rA@DBJcn^bkpVU9V1$>Av@DaYk$M_nb-~m1j z(aD8?9{hvO0cJu_2>{HZLhk3t-Z`Yw{eQwpVwk1VXU;3iA4p^t zz4#-BDck3duZ9{{CKdUUHRz)ZGel`6@FHz`Fprl)dYyiTpz7Sf0&azp8(1QmA|onM Gdj1A)9Mn+& literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.class new file mode 100644 index 0000000000000000000000000000000000000000..8c1546495fb10312b28c8853fb5984930f138a89 GIT binary patch literal 15178 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 literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.class new file mode 100644 index 0000000000000000000000000000000000000000..6cc3c3b167830f0595c2856057b3b60e9d095e28 GIT binary patch literal 8330 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 literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.class new file mode 100644 index 0000000000000000000000000000000000000000..eca27ecb74aa0f7cbf10f5f083d2b9e9f95a1575 GIT binary patch literal 1299 zcmbVLYflqF6g^W~mX@Uy*bvYrrA`oPbmVPLS zCjJ0_l=054;0MJt>?Sj__ue_@+_N)3et!K9po|wO#4unWZX$s}hD?Wl;Fil}+o~US zgsm6`UpmrJuNVgMg#!aa3|U=td~OA{kixP(seI41%C7KL5-AKDFingg&9HoZ<#Jfx z{B$N5hAXw*dSkELtki1^E7g8>l~OQ}(Xz)F3iyV6yAT;z*D`6Z}y3Jzn)} z?jCT$qwn9rxH@qHhOO$2*h<=1YP%wjJ%-hP7#o=o9!`y%V_@#sv_a#G8tJ(2=&u2*OdtKiayN+%?edW-Gc8|fVNGbenmj{6$1Cm!q z6iZm4i=v-6NCO6G%+r@eGov}Nb^-R87BLiP)M+4zH5$#R0P84*vHRGdh~DgDfLkn?0kTv2kU9Sw%n8YZuv|7Yi$d!xmNY(?Y zBV;_J6`ez>e1ylOE{P}C@#p&Gp9t|Xm?G3^;-4Y@*}nNVuj9YlFaI3z&r{b6#J@=V bIU1Mxa{9B#`5B>muRB^Tl z-kpIr^WHFD-`meG0C$*`NRSrDrpPfN%v$*>g_Uk27EP;FAWUwIGvStyo}R4=>=7=N z??li+_eNSjw4-#bA4FJhL~LKbDQmO~VhqNLx$ip}o;W3B_x@QEs@gdHFm{dZA7x`% zIj+5ub|rZr^ItIwFJ>TI)&E;}O(>Us?3JFIxTE^JG-BuX8TXlq9S8FB c570Xc2};IT22va_PIr0E@sJteh%rI+12zU#2mk;8 literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.class new file mode 100644 index 0000000000000000000000000000000000000000..8b73383d90d80c93c8ad63ccca018a59f1f5a97a GIT binary patch literal 349 zcmbVIyG{c!5S+b74vs(qe?SKmxJ7`F5Q6Fi1yOpRae@!WmhNnn-=adI-~;$5#5$s& zW>=csnbkhDU*8{}0Io5uks+^8j8S4ln0E423M<`GJhYuwfiSu>&V(yMesnyquthji zekFnqx;N7Lp^MUWeh^`?6sdi&R@P`2L=47?nH*kj*ILEkdqQ#JpEP087^m;!s@45W zww9HH#w%&(l1DQC6^rm}2Euvs--Q>1`q9T;={u9Q?Ek&yG#!gWRQ!Tu#LiD(+-4>= f94JrTL2oQ%s2Nik$g#sX-sC06U1o$m#tf4mD1}#_ literal 0 HcmV?d00001 diff --git a/javafx-client/target/classes/fxml/dashboard.fxml b/javafx-client/target/classes/fxml/dashboard.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/target/classes/fxml/login.fxml b/javafx-client/target/classes/fxml/login.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/target/classes/fxml/logs.fxml b/javafx-client/target/classes/fxml/logs.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/target/classes/fxml/orders.fxml b/javafx-client/target/classes/fxml/orders.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/target/classes/fxml/products.fxml b/javafx-client/target/classes/fxml/products.fxml new file mode 100644 index 0000000..e69de29 diff --git a/javafx-client/target/classes/logback.xml b/javafx-client/target/classes/logback.xml new file mode 100644 index 0000000..78a646b --- /dev/null +++ b/javafx-client/target/classes/logback.xml @@ -0,0 +1,14 @@ + + + + + %d{HH:mm:ss.SSS} [%-5level] %logger{36} - %msg%n + + + + + + + + + \ No newline at end of file diff --git a/javafx-client/target/classes/styles.css b/javafx-client/target/classes/styles.css new file mode 100644 index 0000000..75a7960 --- /dev/null +++ b/javafx-client/target/classes/styles.css @@ -0,0 +1,93 @@ +/* Material Design Inspired CSS for TFG Sync Service Client */ + +* { + -fx-font-family: "Segoe UI", "Ubuntu", sans-serif; +} + +.root { + -fx-background-color: #fafafa; + -fx-font-size: 12; +} + +/* Buttons */ +.button { + -fx-padding: 8; + -fx-font-size: 12; + -fx-text-fill: #333; + -fx-background-color: #e0e0e0; + -fx-background-radius: 4; +} + +.button:hover { + -fx-background-color: #d0d0d0; +} + +/* Text Fields */ +.text-field, +.password-field { + -fx-padding: 8; + -fx-border-color: #bdbdbd; + -fx-border-width: 0 0 1 0; + -fx-font-size: 12; +} + +/* Labels */ +.label { + -fx-text-fill: #333; +} + +/* Tables */ +.table-view { + -fx-background-color: white; + -fx-border-color: #e0e0e0; + -fx-font-size: 11; +} + +.table-view .column-header { + -fx-background-color: #f5f5f5; + -fx-font-weight: bold; +} + +.table-row-cell { + -fx-font-size: 11; + -fx-background-color: white; +} + +/* ComboBox */ +.combo-box, +.combo-box-base { + -fx-background-color: white; + -fx-border-color: #bdbdbd; + -fx-padding: 5; +} + +/* TextArea */ +.text-area { + -fx-background-color: #f5f5f5; + -fx-text-fill: #333; + -fx-font-size: 10; +} + +/* Separator */ +.separator { + -fx-text-fill: #e0e0e0; +} + +/* TabPane */ +.tab-pane { + -fx-padding: 0; +} + +.tab { + -fx-padding: 10; +} + +.tab-header-background { + -fx-background-color: #f5f5f5; +} + +/* CheckBox */ +.check-box { + -fx-font-size: 11; + -fx-text-fill: #333; +} \ No newline at end of file diff --git a/javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst new file mode 100644 index 0000000..d8a1738 --- /dev/null +++ b/javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst @@ -0,0 +1,20 @@ +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController$1.class +com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.class +com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.class +com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.class +com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.class +com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.class +com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.class +com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.class +com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController$1.class +com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.class +com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.class +com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.class +com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.class +com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController$1.class diff --git a/javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst new file mode 100644 index 0000000..ff74091 --- /dev/null +++ b/javafx-client/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst @@ -0,0 +1,17 @@ +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.java +/home/mireyasp/Descargas/PROYECTO INTERMODULAR/ProyectoIntermodular/javafx-client/src/main/java/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.java