mirror of
https://git.imnavajas.es/jjimenez/printhub.git
synced 2026-01-12 16:38:46 +00:00
subidos los ficheros nuevos
This commit is contained in:
42
src/main/java/com/printhub/printhub/model/Breadcrum.java
Normal file
42
src/main/java/com/printhub/printhub/model/Breadcrum.java
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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();
|
||||
|
||||
}
|
||||
}
|
||||
@ -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]));
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
@ -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("");
|
||||
}
|
||||
}
|
||||
1
src/main/resources/i18n/en/general.properties
Normal file
1
src/main/resources/i18n/en/general.properties
Normal file
@ -0,0 +1 @@
|
||||
t-paginas=Pages
|
||||
1
src/main/resources/i18n/es/general.properties
Normal file
1
src/main/resources/i18n/es/general.properties
Normal file
@ -0,0 +1 @@
|
||||
t-paginas=Páginas
|
||||
0
src/main/resources/static/assets/css/printhub.css
Normal file
0
src/main/resources/static/assets/css/printhub.css
Normal 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();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
})();
|
||||
20
src/main/resources/templates/printhub/partials/head-css.html
Normal file
20
src/main/resources/templates/printhub/partials/head-css.html
Normal 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>
|
||||
Reference in New Issue
Block a user