subidos los ficheros nuevos

This commit is contained in:
Jaime Jiménez
2025-06-16 09:09:10 +02:00
parent 8ed8d0bded
commit 7709138428
12 changed files with 430 additions and 0 deletions

View File

@ -0,0 +1,42 @@
package com.printhub.printhub.model;
import java.util.ArrayList;
import java.util.List;
public class Breadcrum {
public List<BreadcrumbItem> breadcrumb = new ArrayList<>();
public void addItem(String label, String dataKey, String url) {
BreadcrumbItem item = new BreadcrumbItem(label, url, dataKey);
breadcrumb.add(item);
}
public List<BreadcrumbItem> getBreadcrumb() {
return breadcrumb;
}
public static class BreadcrumbItem {
private String label;
private String url;
private String dataKey;
public BreadcrumbItem(String label, String url, String dataKey) {
this.label = label;
this.url = url;
this.dataKey = dataKey;
}
public String getLabel() {
return label;
}
public String getUrl() {
return url;
}
public String getDataKey() {
return dataKey;
}
}
}

View File

@ -0,0 +1,32 @@
package com.printhub.printhub.model.configuration;
import jakarta.persistence.*;
@Entity
@Table(name = "printers")
public class Printer {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
// --- Getters y Setters ---
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}

View File

@ -0,0 +1,13 @@
package com.printhub.printhub.repository.configuration;
import com.printhub.printhub.model.configuration.Printer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import java.util.List;
public interface PrinterRepository extends JpaRepository<Printer, Long>, JpaSpecificationExecutor<Printer> {
// Consulta simple adicional (opcional)
List<Printer> findByNameContainingIgnoreCase(String name);
}

View File

@ -0,0 +1,34 @@
package com.printhub.printhub.service.configuration;
import com.printhub.printhub.model.configuration.Printer;
import com.printhub.printhub.repository.configuration.PrinterRepository;
import com.printhub.printhub.specification.configuration.PrinterSpecification;
import com.printhub.printhub.utils.datatables.DataTableBuilder;
import com.printhub.printhub.utils.datatables.DataTableRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import java.util.Map;
@Service
public class PrinterService {
@Autowired
private PrinterRepository printerRepository;
public Map<String, Object> listDataTable(DataTableRequest dt) {
// Crear pageable con orden, si existe
Pageable pageable = dt.hasOrder()
? PageRequest.of(dt.getPage(), dt.getSize(),
Sort.by(Sort.Direction.fromString(dt.getOrderDirection()), dt.getOrderColumn()))
: PageRequest.of(dt.getPage(), dt.getSize());
Specification<Printer> spec = PrinterSpecification.buildSpec(dt);
Page<Printer> page = printerRepository.findAll(spec, pageable);
return new DataTableBuilder<>(page, dt.getDraw()).build();
}
}

View File

@ -0,0 +1,75 @@
package com.printhub.printhub.specification.configuration;
import com.printhub.printhub.model.configuration.Printer;
import com.printhub.printhub.utils.datatables.DataTableRequest;
import org.springframework.data.jpa.domain.Specification;
import jakarta.persistence.criteria.Path;
import jakarta.persistence.criteria.Predicate;
import java.util.ArrayList;
import java.util.List;
public class PrinterSpecification {
public static Specification<Printer> filterBy(String name) {
return (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null && !name.isBlank()) {
predicates.add(cb.like(cb.lower(root.get("name")), "%" + name.toLowerCase() + "%"));
}
return cb.and(predicates.toArray(new Predicate[0]));
};
}
public static Specification<Printer> buildSpec(DataTableRequest dt) {
return (root, query, cb) -> {
List<Predicate> ands = new ArrayList<>();
/* --------------- 1. Búsqueda global ---------------- */
if (!dt.getGlobalSearch().isBlank()) {
String term = "%" + dt.getGlobalSearch().toLowerCase() + "%";
// OR sobre todas las columnas marcadas como searchable=true
List<Predicate> ors = new ArrayList<>();
for (var col : dt.getSearchableColumns()) {
Path<?> path = root.get(col.getName());
if (path.getJavaType().equals(String.class)) {
ors.add(cb.like(cb.lower(path.as(String.class)), term));
}
// si quisieras buscar por id cuando el globalSearch es numérico:
else if (path.getJavaType().equals(Long.class)) {
try {
Long value = Long.valueOf(dt.getGlobalSearch());
ors.add(cb.equal(path, value));
} catch (NumberFormatException ignored) {
/* el término no es número */ }
}
}
// si hay columnas OR, añádelo al AND general
if (!ors.isEmpty()) {
ands.add(cb.or(ors.toArray(new Predicate[0])));
}
}
/* --------------- 2. Filtros individuales ------------ */
for (var col : dt.getColumns()) {
if (!col.getFilter().isBlank()) {
ands.add(cb.like(cb.lower(root.get(col.getName())),
"%" + col.getFilter().toLowerCase() + "%"));
}
}
/* --------------- 3. Resultado ---------------------- */
return ands.isEmpty()
? cb.conjunction() // sin filtros
: cb.and(ands.toArray(new Predicate[0]));
};
}
}

View File

@ -0,0 +1,63 @@
package com.printhub.printhub.utils.datatables;
import org.springframework.data.domain.Page;
import java.lang.reflect.Field;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
public class DataTableBuilder<T> {
private final int draw;
private final Page<T> page;
private final List<Function<T, Map<String, Object>>> mappers = new ArrayList<>();
private final Map<String, Function<T, Object>> addedColumns = new HashMap<>();
private final Map<String, Function<T, Object>> editedColumns = new HashMap<>();
public DataTableBuilder(Page<T> page, int draw) {
this.page = page;
this.draw = draw;
}
public DataTableBuilder<T> addColumn(String columnName, Function<T, Object> generator) {
addedColumns.put(columnName, generator);
return this;
}
public DataTableBuilder<T> editColumn(String columnName, Function<T, Object> replacer) {
editedColumns.put(columnName, replacer);
return this;
}
public Map<String, Object> build() {
List<Map<String, Object>> data = page.getContent().stream()
.map(entity -> {
Map<String, Object> row = new LinkedHashMap<>();
// Cargar propiedades por defecto
Arrays.stream(entity.getClass().getDeclaredFields()).forEach(field -> {
field.setAccessible(true);
try {
row.put(field.getName(), field.get(entity));
} catch (IllegalAccessException ignored) {}
});
// Editar columnas existentes
editedColumns.forEach((key, func) -> row.put(key, func.apply(entity)));
// Añadir columnas nuevas
addedColumns.forEach((key, func) -> row.put(key, func.apply(entity)));
return row;
}).collect(Collectors.toList());
Map<String, Object> response = new HashMap<>();
response.put("draw", draw);
response.put("recordsTotal", page.getTotalElements());
response.put("recordsFiltered", page.getTotalElements()); // cambia si aplicas filtro real
response.put("data", data);
return response;
}
}

View File

@ -0,0 +1,107 @@
package com.printhub.printhub.utils.datatables;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
/**
* Encapsula todos los parámetros que DataTables envía al backend
* y los deja listos para usarlos en la capa de servicio / specification.
*/
public class DataTableRequest {
/* ---------- sub-clase para la metadata de cada columna ---------- */
public static class ColumnInfo {
private final String name;
private final boolean searchable;
private final String filter; // columns[i][search][value]
public ColumnInfo(String name, boolean searchable, String filter) {
this.name = name;
this.searchable = searchable;
this.filter = filter == null ? "" : filter;
}
public String getName() { return name; }
public boolean isSearchable() { return searchable; }
public String getFilter() { return filter; }
}
/* ---------- campos principales ---------- */
private final int draw;
private final int start;
private final int length;
private final String globalSearch;
private final List<ColumnInfo> columns;
private final String orderColumn; // nombre de la col a ordenar (o null)
private final String orderDirection; // asc / desc
/* ---------- constructor: parsea los parámetros ---------- */
public DataTableRequest(Map<String, String> params) {
this.draw = Integer.parseInt(params.getOrDefault("draw", "0"));
this.start = Integer.parseInt(params.getOrDefault("start", "0"));
this.length = Integer.parseInt(params.getOrDefault("length", "10"));
/* --- búsqueda global --- */
this.globalSearch = params.getOrDefault("search[value]", "").trim();
/* --- columnas --- */
List<ColumnInfo> tmp = new ArrayList<>();
int i = 0;
while (params.containsKey("columns[" + i + "][data]")) {
String name = params.get("columns[" + i + "][data]");
boolean searchable = Boolean.parseBoolean(params.getOrDefault(
"columns[" + i + "][searchable]", "true"));
String filter = params.getOrDefault(
"columns[" + i + "][search][value]", "");
tmp.add(new ColumnInfo(name, searchable, filter.trim()));
i++;
}
this.columns = Collections.unmodifiableList(tmp);
/* --- orden --- */
String ordIdx = params.get("order[0][column]");
if (ordIdx != null) {
int idx = Integer.parseInt(ordIdx);
this.orderColumn = (idx < columns.size()) ? columns.get(idx).getName() : null;
this.orderDirection = params.getOrDefault("order[0][dir]", "asc");
} else {
this.orderColumn = null;
this.orderDirection = "asc";
}
}
/* ---------- getters / utilidades ---------- */
public int getDraw() { return draw; }
public int getPage() { return start / length; }
public int getSize() { return length; }
public boolean hasOrder() { return orderColumn != null; }
public String getOrderColumn(){ return orderColumn; }
public String getOrderDirection(){ return orderDirection; }
public String getGlobalSearch() { return globalSearch; }
/** Devuelve todas las columnas (incluyendo no-searchable) */
public List<ColumnInfo> getColumns() { return columns; }
/** Devuelve solo las columnas con searchable=true */
public List<ColumnInfo> getSearchableColumns() {
return columns.stream()
.filter(ColumnInfo::isSearchable)
.toList();
}
/** Obtiene el filtro individual para una columna por nombre */
public String getColumnFilter(String columnName) {
return columns.stream()
.filter(c -> c.getName().equals(columnName))
.map(ColumnInfo::getFilter)
.findFirst()
.orElse("");
}
}

View File

@ -0,0 +1 @@
t-paginas=Pages

View File

@ -0,0 +1 @@
t-paginas=Páginas

View File

@ -0,0 +1,42 @@
(() => {
const lang = document.documentElement.lang || 'en'; // “es”, “en”, etc.
let langCode;
switch (lang) {
case 'es': langCode = 'es-ES'; break;
case 'fr': langCode = 'fr-FR'; break;
case 'de': langCode = 'de-DE'; break;
default: langCode = 'en-GB';
}
$('#listOfPrinters').DataTable({
processing: true,
serverSide: true,
language: {
url: `https://cdn.datatables.net/plug-ins/1.13.8/i18n/${langCode}.json`
},
ajax: {
url: '/configuration/printers/datatable',
dataSrc: 'data'
},
columns: [
{ data: 'id' },
{ data: 'name' } // añade aquí 'actions' si la envías
],
initComplete: function () {
const api = this.api();
// para cada input de la fila 'filters'
api.columns().eq(0).each(function (colIdx) {
const input = $('.filters th').eq(colIdx).find('input');
if (input.length) {
$(input).on('keyup change', function () {
// envía el texto al backend → columns[colIdx][search][value]
api.column(colIdx).search(this.value).draw();
});
}
});
}
});
})();

View File

@ -0,0 +1,20 @@
<html>
<body>
<div th:fragment="head-css" th:remove="tag">
<!-- Layout config Js -->
<script src="/assets/js/layout.js"></script>
<!-- Bootstrap Css -->
<link href="/assets/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<!-- Icons Css -->
<link href="/assets/css/icons.min.css" rel="stylesheet" type="text/css" />
<!-- App Css-->
<link href="/assets/css/app.min.css" rel="stylesheet" type="text/css" />
<!-- custom Css-->
<link href="/assets/css/custom.min.css" rel="stylesheet" type="text/css" />
<link href="/assets/css/printhub.css" rel="stylesheet" type="text/css" />
</div>
</body>
</html>