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 0000000..179aa6f Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/JavaFxClientApplication.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.class new file mode 100644 index 0000000..a3d3a09 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/api/SyncServiceClient.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.class new file mode 100644 index 0000000..de418b8 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/SessionManager.class differ 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 0000000..1a16dee Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/MappingDto.class differ 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 0000000..a572b01 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/OrderDto.class differ 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 0000000..d610484 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/ProductDto.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.class new file mode 100644 index 0000000..f1e1b2e Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/model/dto/SyncDto.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.class new file mode 100644 index 0000000..8356925 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/DashboardController.class differ 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 0000000..5a2fdc8 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LoginController.class differ 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 0000000..141fd6d Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController$1.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.class new file mode 100644 index 0000000..46fb1a5 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/LogsController.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController$1.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController$1.class new file mode 100644 index 0000000..43c0d7a Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController$1.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.class new file mode 100644 index 0000000..5492b9a Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/OrdersController.class differ 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 0000000..19b2afb Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController$1.class differ 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 0000000..8c15464 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/controllers/ProductsController.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.class new file mode 100644 index 0000000..242c578 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/LoginStage.class differ 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 0000000..6cc3c3b Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/ui/stages/MainStage.class differ 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 0000000..eca27ec Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/AlertUtil.class differ diff --git a/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.class b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.class new file mode 100644 index 0000000..e1b8754 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/FormatUtil.class differ 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 0000000..8b73383 Binary files /dev/null and b/javafx-client/target/classes/com/teterialosjuanjos/tfg/javafxclient/util/TaskExecutor.class differ 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