mejorado tema de idiomas y terminado datatable

This commit is contained in:
Jaime Jiménez
2025-06-15 16:01:04 +02:00
parent 276cf2ad7c
commit 8ed8d0bded
19 changed files with 181 additions and 119 deletions

2
.env
View File

@ -3,6 +3,6 @@ MYSQL_DATABASE=printhub
MYSQL_ROOT_PASSWORD=8FOc7XQrUWEtwgiDKppfcv2LWo MYSQL_ROOT_PASSWORD=8FOc7XQrUWEtwgiDKppfcv2LWo
# Spring Boot datasource # Spring Boot datasource
SPRING_DATASOURCE_URL=jdbc:mysql://mysql:3306/printhub?useSSL=false&allowPublicKeyRetrieval=true&serverTimezone=UTC SPRING_DATASOURCE_URL=jdbc:mysql://localhost:3306/printhub
SPRING_DATASOURCE_USERNAME=printhub SPRING_DATASOURCE_USERNAME=printhub
SPRING_DATASOURCE_PASSWORD=DsomyTF4NjTtwzGTTWaxoUEvIt SPRING_DATASOURCE_PASSWORD=DsomyTF4NjTtwzGTTWaxoUEvIt

11
.vscode/launch.json vendored
View File

@ -3,12 +3,11 @@
"configurations": [ "configurations": [
{ {
"type": "java", "type": "java",
"name": "Debug Spring Boot in Docker", "name": "Debug Spring Boot App",
"request": "attach", "request": "launch",
"hostName": "localhost", "mainClass": "com.printhub.printhub.PrintHubApplication",
"port": 5005, "projectName": "printhub",
"preLaunchTask": "Start Spring Boot in Docker (Debug)", "vmArgs": "-Dspring.profiles.active=dev"
"postDebugTask": "Stop Spring Boot in Docker"
} }
] ]
} }

View File

@ -1,3 +1,4 @@
{ {
"java.compile.nullAnalysis.mode": "automatic" "java.compile.nullAnalysis.mode": "automatic",
"java.configuration.updateBuildConfiguration": "interactive"
} }

32
.vscode/tasks.json vendored
View File

@ -1,32 +0,0 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Start Spring Boot in Docker (Debug)",
"type": "process",
"command": "docker",
"args": [
"exec",
"-i",
"springboot-dev",
"sh",
"-c",
"./mvnw spring-boot:run -o -Dmaven.resources.skip=true -Dmaven.test.skip=true -Dspring-boot.run.jvmArguments=-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005"
],
"isBackground": true,
"problemMatcher": {
"background": {
"activeOnStart": true,
"beginsPattern": ".*",
"endsPattern": "Listening for transport dt_socket at address: 5005"
}
}
},
{
"label": "Stop Spring Boot in Docker",
"type": "shell",
"command": "docker exec springboot-dev pkill -f jdwp=transport",
"problemMatcher": []
}
]
}

View File

@ -1,8 +0,0 @@
FROM eclipse-temurin:24-jdk
# Instala Maven si lo necesitas (si usas ./mvnw, esto es opcional)
RUN apt-get update && apt-get install -y maven
WORKDIR /app
EXPOSE 8080

View File

@ -13,26 +13,5 @@ services:
volumes: volumes:
- mysql-data:/var/lib/mysql - mysql-data:/var/lib/mysql
app:
build:
context: .
dockerfile: Dockerfile.dev
container_name: springboot-dev
#command: ./mvnw spring-boot:run
command: tail -f /dev/null
volumes:
- ./:/app
- ./m2:/root/.m2
- ./target:/app/target
ports:
- "8080:8080"
- "5005:5005"
depends_on:
- mysql
environment:
SPRING_DATASOURCE_URL: ${SPRING_DATASOURCE_URL}
SPRING_DATASOURCE_USERNAME: ${SPRING_DATASOURCE_USERNAME}
SPRING_DATASOURCE_PASSWORD: ${SPRING_DATASOURCE_PASSWORD}
volumes: volumes:
mysql-data: mysql-data:

13
pom.xml
View File

@ -61,6 +61,19 @@
<artifactId>spring-boot-starter-test</artifactId> <artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope> <scope>test</scope>
</dependency> </dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies> </dependencies>
<build> <build>

View File

@ -3,7 +3,10 @@ package com.printhub.printhub;
import org.springframework.boot.SpringApplication; import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
@SpringBootApplication @SpringBootApplication
@EnableJpaRepositories("com.printhub.printhub.repository")
public class PrintHubApplication { public class PrintHubApplication {
public static void main(String[] args) { public static void main(String[] args) {

View File

@ -6,8 +6,15 @@ import org.springframework.stereotype.Controller;
import org.springframework.ui.Model; import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import com.printhub.printhub.model.Breadcrum;
import com.printhub.printhub.service.configuration.PrinterService;
import com.printhub.printhub.utils.datatables.DataTableRequest;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
@Controller @Controller
@RequestMapping("/configuration/printers") @RequestMapping("/configuration/printers")
@ -15,10 +22,34 @@ public class PrintersController {
@Autowired @Autowired
private MessageSource messageSource; private MessageSource messageSource;
@Autowired
private PrinterService printerService;
@GetMapping @GetMapping
public String index(Model model, Locale locale) { public String index(Model model, Locale locale) {
model.addAttribute("title", messageSource.getMessage("t-home", null, locale));
Breadcrum breadcrumb = new Breadcrum();
breadcrumb.addItem(
messageSource.getMessage("t-printers", null, locale),
"t-printers",
"/configuration/printers");
breadcrumb.addItem(
messageSource.getMessage("t-printers-list", null, locale),
"t-printers-list",
null);
model.addAttribute("breadcrumb", breadcrumb.getBreadcrumb());
model.addAttribute("keyTitle", "t-printers");
model.addAttribute("title", messageSource.getMessage("t-printers", null, locale));
return "printhub/configuration/printers"; return "printhub/configuration/printers";
} }
@GetMapping("/datatable")
@ResponseBody
public Map<String, Object> datatable(@RequestParam Map<String, String> params) {
DataTableRequest dt = new DataTableRequest(params);
return printerService.listDataTable(dt);
}
} }

View File

@ -1,9 +1,10 @@
spring.application.name=printhub spring.application.name=printhub
spring.thymeleaf.cache=false spring.thymeleaf.cache=false
spring.datasource.url=${SPRING_DATASOURCE_URL} spring.datasource.url=jdbc:mysql://localhost:3306/printhub
spring.datasource.username=${SPRING_DATASOURCE_USERNAME} spring.datasource.username=printhub
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD} spring.datasource.password=DsomyTF4NjTtwzGTTWaxoUEvIt
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true spring.jpa.show-sql=true

View File

@ -1,3 +1,4 @@
t-printers=Printers t-printers=Printers
t-printers-list=Printers List t-printers-list=Printers List
t-printers-id=ID
t-printers-name=Name t-printers-name=Name

View File

@ -1,3 +1,4 @@
t-printers=Impresoras t-printers=Impresoras
t-printers-list=Lista de Impresoras t-printers-list=Lista de Impresoras
t-printers-id=ID
t-printers-name=Nombre t-printers-name=Nombre

View File

@ -15,45 +15,40 @@ File: Main Js File
*/ */
var navbarMenuHTML = document.querySelector(".navbar-menu").innerHTML; var navbarMenuHTML = document.querySelector(".navbar-menu").innerHTML;
var horizontalMenuSplit = 7; // after this number all horizontal menus will be moved in More menu options var horizontalMenuSplit = 7; // after this number all horizontal menus will be moved in More menu options
var default_lang = "sp"; // set Default Language var default_lang = "es"; // set Default Language
var language = localStorage.getItem("language"); var language = localStorage.getItem("language");
function initLanguage() { function initLanguage() {
// Set new language const saved = localStorage.getItem("language") || default_lang;
(language === null) ? setLanguage(default_lang) : setLanguage(language); setLanguage(saved, false); // ← NO redirige
var languages = document.getElementsByClassName("language"); document.querySelectorAll('.language').forEach(a => {
languages && Array.from(languages).forEach(function (dropdown) { a.addEventListener('click', () => setLanguage(a.dataset.lang, true));
dropdown.addEventListener("click", function (event) {
setLanguage(dropdown.getAttribute("data-lang"));
});
}); });
} }
function setLanguage(lang) { function setLanguage(lang, redirect = true) {
if (document.getElementById("header-lang-img")) {
if (lang == "en") { const already = document.documentElement.lang === lang;
document.getElementById("header-lang-img").src = "assets/images/flags/gb.svg";
} else if (lang == "es") { // 1. Actualiza bandera, <html lang> y localStorage
document.getElementById("header-lang-img").src = "assets/images/flags/spain.svg"; document.documentElement.lang = lang;
} else if (lang == "gr") { document.getElementById("header-lang-img").src =
document.getElementById("header-lang-img").src = "assets/images/flags/germany.svg"; lang === "en" ? "/assets/images/flags/gb.svg"
} else if (lang == "it") { : "/assets/images/flags/spain.svg";
document.getElementById("header-lang-img").src = "assets/images/flags/italy.svg"; localStorage.setItem("language", lang);
} else if (lang == "ru") {
document.getElementById("header-lang-img").src = "assets/images/flags/russia.svg"; // 2. Carga traducciones (o al menos quita el veil)
} else if (lang == "ch") { getLanguage(); // ← SIEMPRE llamar
document.getElementById("header-lang-img").src = "assets/images/flags/china.svg";
} else if (lang == "fr") { // 3. Redirige solo si realmente cambiamos de idioma
document.getElementById("header-lang-img").src = "assets/images/flags/french.svg"; if (!already && redirect) {
} else if (lang == "ar") { const url = new URL(location.href);
document.getElementById("header-lang-img").src = "assets/images/flags/ae.svg"; url.searchParams.set("lang", lang);
} location.href = url.toString();
localStorage.setItem("language", lang);
language = localStorage.getItem("language");
getLanguage();
} }
} }
// Multi language setting // Multi language setting
function getLanguage() { function getLanguage() {
const lang = language || default_lang; const lang = language || default_lang;
@ -67,6 +62,17 @@ File: Main Js File
elem.textContent = data[key]; elem.textContent = data[key];
}); });
}); });
}).finally(() => {
// 1 Quita el velo “cargando i18n”
document.documentElement.removeAttribute('data-i18n-loading');
// 2 Si la URL tiene ?lang=xx, quítalo sin recargar
const url = new URL(window.location.href);
if (url.searchParams.has('lang')) {
url.searchParams.delete('lang'); // borra el parámetro
history.replaceState(null, "", // sustituye entrada actual
url.pathname + url.search + url.hash); // misma ruta, sin ?lang
}
}); });
} }

View File

@ -10,6 +10,6 @@ File: Common Plugins Js File
//Common plugins //Common plugins
if(document.querySelectorAll("[toast-list]") || document.querySelectorAll('[data-choices]') || document.querySelectorAll("[data-provider]")){ if(document.querySelectorAll("[toast-list]") || document.querySelectorAll('[data-choices]') || document.querySelectorAll("[data-provider]")){
document.writeln("<script type='text/javascript' src='https://cdn.jsdelivr.net/npm/toastify-js'></script>"); document.writeln("<script type='text/javascript' src='https://cdn.jsdelivr.net/npm/toastify-js'></script>");
document.writeln("<script type='text/javascript' src='assets/libs/choices.js/public/assets/scripts/choices.min.js'></script>"); document.writeln("<script type='text/javascript' src='/assets/libs/choices.js/public/assets/scripts/choices.min.js'></script>");
document.writeln("<script type='text/javascript' src='assets/libs/flatpickr/flatpickr.min.js'></script>"); document.writeln("<script type='text/javascript' src='/assets/libs/flatpickr/flatpickr.min.js'></script>");
} }

View File

@ -7,17 +7,62 @@
<div th:replace="~{printhub/partials/title-meta :: title-meta(${title})}"></div> <div th:replace="~{printhub/partials/title-meta :: title-meta(${title})}"></div>
</th:block> </th:block>
<th:block layout:fragment="pagecss">
<!-- DataTable CSS en el lugar correcto -->
<link rel="stylesheet" href="https://cdn.datatables.net/1.11.5/css/dataTables.bootstrap5.min.css" />
<link rel="stylesheet" href="https://cdn.datatables.net/responsive/2.2.9/css/responsive.bootstrap.min.css" />
<link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.2.2/css/buttons.dataTables.min.css">
</th:block>
<head> <head>
</head> </head>
<body> <body>
<div layout:fragment="content"> <div layout:fragment="content">
<!-- start page title --> <!-- start page title -->
<div th:replace="~{printhub/partials/page-title :: page-title(${title},'Pages')}"></div> <div th:replace="~{printhub/partials/page-title :: page-title(${title},${currentPage})}"></div>
<!-- Striped Rows -->
<table id="listOfPrinters" class="table table-striped">
<thead>
<tr>
<th scope="col" data-key="t-printers-id">Id</th>
<th scope="col" data-key="t-printers-name">Nombre</th>
</tr>
<!-- fila de inputs -->
<tr class="filters">
<th></th>
<th>
<input type="text" title="name-filter" class="form-control form-control-sm" />
</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div> </div>
<th:block layout:fragment="pagejs"> <th:block layout:fragment="pagejs">
<script src="https://code.jquery.com/jquery-3.6.0.min.js"
integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<!--datatable js-->
<script src="https://cdn.datatables.net/1.11.5/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.11.5/js/dataTables.bootstrap5.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.2.9/js/dataTables.responsive.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.2.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.2.2/js/buttons.print.min.js"></script>
<script src="https://cdn.datatables.net/buttons/2.2.2/js/buttons.html5.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/vfs_fonts.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdfmake/0.1.53/pdfmake.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.1.3/jszip.min.js"></script>
<script th:src="@{/assets/js/pages/printhub/configuration/printerList.js}"></script>
</th:block> </th:block>
</body> </body>

View File

@ -1,9 +1,26 @@
<html lang="en" data-layout="semibox" data-sidebar-visibility="show" data-topbar="light" data-sidebar="light" data-sidebar-size="lg" data-sidebar-image="none" data-preloader="disable" xmlns="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"> <html th:lang="${#locale.language}" data-layout="semibox" data-sidebar-visibility="show" data-topbar="light" data-sidebar="light"
data-sidebar-size="lg" data-sidebar-image="none" data-preloader="disable" xmlns="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout">
<head> <head>
<style>
html[data-i18n-loading] {
opacity: 0;
}
</style>
<script>
document.documentElement.setAttribute('data-i18n-loading', '');
</script>
<th:block layout:fragment="pagetitle" /> <th:block layout:fragment="pagetitle" />
<!-- Page CSS --> <!-- Page CSS -->
<div th:replace="~{theme/partials/head-css :: head-css}"></div> <div th:replace="~{printhub/partials/head-css :: head-css}"></div>
<th:block layout:fragment="pagecss" />
</head> </head>
<body data-sidebar="dark" data-layout-mode="light"> <body data-sidebar="dark" data-layout-mode="light">

View File

@ -1,4 +1,4 @@
<html xmlns:th="http://www.thymeleaf.org"> <html xmlns:th="http://www.thymeleaf.org">
<body> <body>
<div th:fragment="page-title(title,pagetitle)" th:remove="tag"> <div th:fragment="page-title(title,pagetitle)" th:remove="tag">
@ -6,12 +6,19 @@
<div class="row"> <div class="row">
<div class="col-12"> <div class="col-12">
<div class="page-title-box d-sm-flex align-items-center justify-content-between"> <div class="page-title-box d-sm-flex align-items-center justify-content-between">
<h4 data-key="t-home" class="mb-sm-0" th:text="${title}"></h4> <h4 th:data-key="${keyTitle}" class="mb-sm-0" th:text="${currentPage}"></h4>
<div class="page-title-right"> <div class="page-title-right">
<ol class="breadcrumb m-0"> <ol class="breadcrumb m-0">
<li class="breadcrumb-item"><a href="javascript: void(0);" th:text="${pagetitle}"></a></li> <li th:each="item : ${breadcrumb}" class="breadcrumb-item"
<li class="breadcrumb-item active" data-key="t-home" th:text="${title}"></li> th:classappend="${item.url == null}? 'active' : ''">
<a th:if="${item.url != null}" th:data-key="${item.dataKey}" th:href="${item.url}"
th:text="${item.label}"></a>
<span th:if="${item.url == null}" th:data-key="${item.dataKey}"
th:text="${item.label}"></span>
</li>
</ol> </ol>
</div> </div>

View File

@ -3,7 +3,7 @@
<body> <body>
<div th:fragment="title-meta(title)" th:remove="tag"> <div th:fragment="title-meta(title)" th:remove="tag">
<meta charset="utf-8" /> <meta charset="utf-8" />
<title th:text="${title} + ' | PrintHub'"></title> <title text="PrintHub'"></title>
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta content="Servicion de impresión personal" name="description" /> <meta content="Servicion de impresión personal" name="description" />
<!-- App favicon --> <!-- App favicon -->

View File

@ -38,11 +38,9 @@
<div class="d-flex align-items-center"> <div class="d-flex align-items-center">
<div class="dropdown ms-1 topbar-head-dropdown header-item"> <div class="dropdown ms-1 topbar-head-dropdown header-item">
<button type="button" class="btn btn-icon btn-topbar btn-ghost-secondary rounded-circle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <button type="button" class="btn btn-icon btn-topbar btn-ghost-secondary rounded-circle" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<img id="header-lang-img" src="/assets/images/flags/us.svg" alt="Header Language" height="20" class="rounded"> <img id="header-lang-img" src="" alt="Header Language" height="20" class="rounded">
</button> </button>
<div class="dropdown-menu dropdown-menu-end"> <div class="dropdown-menu dropdown-menu-end">