mirror of
https://git.imnavajas.es/jjimenez/erp-imprimelibros.git
synced 2026-01-13 00:48:49 +00:00
trabajando en el datatables de los presupuestos
This commit is contained in:
@ -13,7 +13,7 @@ import com.imprimelibros.erp.users.User;
|
||||
|
||||
@MappedSuperclass
|
||||
@EntityListeners(AuditingEntityListener.class)
|
||||
public abstract class AbstractAuditedSoftDeleteEntity {
|
||||
public abstract class AbstractAuditedEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@ -16,7 +16,6 @@ import org.springframework.stereotype.Controller;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
|
||||
@ -48,6 +48,6 @@ public class HomeController {
|
||||
Map<String, String> translations = Map.of();
|
||||
model.addAttribute("languageBundle", translations);
|
||||
}
|
||||
return "imprimelibros/home";
|
||||
return "imprimelibros/home/home";
|
||||
}
|
||||
}
|
||||
|
||||
@ -9,19 +9,15 @@ import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups;
|
||||
import com.imprimelibros.erp.presupuesto.validation.Tamanio;
|
||||
|
||||
import com.imprimelibros.erp.common.HtmlStripConverter;
|
||||
import com.imprimelibros.erp.common.jpa.AbstractAuditedSoftDeleteEntity;
|
||||
import com.imprimelibros.erp.common.jpa.AbstractAuditedEntity;
|
||||
|
||||
import jakarta.persistence.*;
|
||||
|
||||
import org.hibernate.annotations.SQLDelete;
|
||||
import org.hibernate.annotations.SQLRestriction;
|
||||
import org.springframework.data.annotation.CreatedBy;
|
||||
import org.springframework.data.annotation.CreatedDate;
|
||||
import org.springframework.data.annotation.LastModifiedDate;
|
||||
import org.springframework.data.jpa.domain.support.AuditingEntityListener;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
import com.imprimelibros.erp.users.User;
|
||||
|
||||
@ -38,7 +34,7 @@ import com.imprimelibros.erp.users.User;
|
||||
})
|
||||
@SQLDelete(sql = "UPDATE presupuesto SET deleted = 1, deleted_at = NOW(3) WHERE id = ?")
|
||||
@SQLRestriction("deleted = 0")
|
||||
public class Presupuesto extends AbstractAuditedSoftDeleteEntity implements Cloneable {
|
||||
public class Presupuesto extends AbstractAuditedEntity implements Cloneable {
|
||||
|
||||
public enum TipoEncuadernacion {
|
||||
fresado("presupuesto.fresado"),
|
||||
|
||||
@ -3,18 +3,23 @@ package com.imprimelibros.erp.presupuesto;
|
||||
import org.springframework.stereotype.Controller;
|
||||
import org.springframework.ui.Model;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.context.MessageSource;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.security.core.Authentication;
|
||||
import org.springframework.validation.BindingResult;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.ResponseBody;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.ModelAttribute;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
@ -23,7 +28,15 @@ import org.springframework.http.MediaType;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuesto;
|
||||
import com.imprimelibros.erp.datatables.DataTable;
|
||||
import com.imprimelibros.erp.datatables.DataTablesParser;
|
||||
import com.imprimelibros.erp.datatables.DataTablesRequest;
|
||||
import com.imprimelibros.erp.datatables.DataTablesResponse;
|
||||
import com.imprimelibros.erp.externalApi.skApiClient;
|
||||
import com.imprimelibros.erp.i18n.TranslationService;
|
||||
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoCubierta;
|
||||
import com.imprimelibros.erp.presupuesto.Presupuesto.TipoEncuadernacion;
|
||||
import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto;
|
||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion;
|
||||
import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas;
|
||||
@ -46,9 +59,14 @@ public class PresupuestoController {
|
||||
protected MessageSource messageSource;
|
||||
|
||||
private final ObjectMapper objectMapper;
|
||||
private final TranslationService translationService;
|
||||
private final PresupuestoRepository repo;
|
||||
|
||||
public PresupuestoController(ObjectMapper objectMapper) {
|
||||
public PresupuestoController(ObjectMapper objectMapper, TranslationService translationService,
|
||||
PresupuestoRepository repo) {
|
||||
this.objectMapper = objectMapper;
|
||||
this.translationService = translationService;
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
@PostMapping("/public/validar/datos-generales")
|
||||
@ -407,4 +425,68 @@ public class PresupuestoController {
|
||||
return ResponseEntity.ok(resumen);
|
||||
}
|
||||
|
||||
// =============================================
|
||||
// MÉTODOS PARA USUARIOS AUTENTICADOS
|
||||
// =============================================
|
||||
@GetMapping
|
||||
public String getPresupuestoView(Model model, Authentication authentication, Locale locale) {
|
||||
|
||||
List<String> keys = List.of();
|
||||
|
||||
Map<String, String> translations = translationService.getTranslations(locale, keys);
|
||||
model.addAttribute("languageBundle", translations);
|
||||
|
||||
return "imprimelibros/presupuestos/presupuesto-list";
|
||||
}
|
||||
|
||||
@GetMapping(value = "/datatable/{tipo}", produces = "application/json")
|
||||
@ResponseBody
|
||||
public DataTablesResponse<Map<String, Object>> datatable(@RequestParam("tipo") String tipo,
|
||||
HttpServletRequest request, Authentication authentication,
|
||||
Locale locale) {
|
||||
|
||||
DataTablesRequest dt = DataTablesParser.from(request);
|
||||
|
||||
List<String> searchable = List.of(
|
||||
"id",
|
||||
"titulo",
|
||||
"cliente",
|
||||
"tipoEncuadernacion",
|
||||
"tipoCubierta",
|
||||
"tipoImpresion",
|
||||
"tirada",
|
||||
"paginas",
|
||||
"estado", "total", "pais", "region", "ciudad", "updatedAt");
|
||||
|
||||
List<String> orderable = List.of(/*
|
||||
* "id",
|
||||
* "tipoEncuadernacion",
|
||||
* "tipoCubierta",
|
||||
* "tiradaMin",
|
||||
* "tiradaMax",
|
||||
* "margenMin",
|
||||
* "margenMax"
|
||||
*/);
|
||||
|
||||
Specification<MargenPresupuesto> base = (root, query, cb) -> cb.conjunction();
|
||||
long total = repo.count();
|
||||
|
||||
return DataTable
|
||||
.of(repo, MargenPresupuesto.class, dt, searchable) // 'searchable' en DataTable.java
|
||||
// edita columnas "reales":
|
||||
.orderable(orderable)
|
||||
.add("actions", (presupuesto) -> {
|
||||
return "<div class=\"hstack gap-3 flex-wrap\">\n" +
|
||||
" <a href=\"javascript:void(0);\" data-id=\"" + presupuesto.getId()
|
||||
+ "\" class=\"link-success btn-edit-" + tipo
|
||||
+ " fs-15\"><i class=\"ri-edit-2-line\"></i></a>\n"
|
||||
+ " <a href=\"javascript:void(0);\" data-id=\"" + presupuesto.getId()
|
||||
+ "\" class=\"link-danger btn-delete-" + tipo
|
||||
+ " fs-15\"><i class=\"ri-delete-bin-5-line\"></i></a>\n"
|
||||
+ " </div>";
|
||||
})
|
||||
.where(base)
|
||||
.toJson(total);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -1,14 +1,17 @@
|
||||
package com.imprimelibros.erp.presupuesto;
|
||||
|
||||
import org.springframework.data.jpa.repository.JpaRepository;
|
||||
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import com.imprimelibros.erp.configuracion.margenes_presupuestos.MargenPresupuesto;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@Repository
|
||||
public interface PresupuestoRepository extends JpaRepository<Presupuesto, Long> {
|
||||
public interface PresupuestoRepository extends JpaRepository<Presupuesto, Long>, JpaSpecificationExecutor<MargenPresupuesto> {
|
||||
|
||||
Optional<Presupuesto> findFirstBySessionIdAndOrigenAndEstadoInOrderByUpdatedAtDesc(
|
||||
String sessionId,
|
||||
|
||||
@ -14,5 +14,6 @@ app.mensajes=Mensajes
|
||||
app.logout=Cerrar sesión
|
||||
|
||||
app.sidebar.inicio=Inicio
|
||||
app.sidebar.usuarios=Usuarios
|
||||
app.sidebar.configuracion=Configuración
|
||||
app.sidebar.presupuestos=Presupuestos
|
||||
app.sidebar.configuracion=Configuración
|
||||
app.sidebar.usuarios=Usuarios
|
||||
@ -1,3 +1,4 @@
|
||||
presupuesto.title=Presupuestos
|
||||
presupuesto.datos-generales=Datos Generales
|
||||
presupuesto.interior=Interior
|
||||
presupuesto.cubierta=Cubierta
|
||||
@ -6,6 +7,26 @@ presupuesto.extras=Extras
|
||||
presupuesto.resumen=Resumen
|
||||
presupuesto.add-to-presupuesto=Añadir al presupuesto
|
||||
presupuesto.calcular=Calcular
|
||||
presupuesto.add=Añadir presupuesto
|
||||
|
||||
presupuesto.nav.presupuestos-cliente=Presupuestos cliente
|
||||
presupuesto.nav.presupuestos-anonimos=Presupuestos anónimos
|
||||
|
||||
presupuesto.tabla.id=ID
|
||||
presupuesto.tabla.titulo=Título
|
||||
presupuesto.tabla.cliente=Cliente
|
||||
presupuesto.tabla.encuadernacion=Encuadernación
|
||||
presupuesto.tabla.cubierta=Cubierta
|
||||
presupuesto.tabla.tipo-impresion=Tipo de impresión
|
||||
presupuesto.tabla.tirada=Tirada
|
||||
presupuesto.tabla.paginas=Páginas
|
||||
presupuesto.tabla.estado=Estado
|
||||
presupuesto.tabla.total-iva=Total (con IVA)
|
||||
presupuesto.tabla.updated-at=Actualizado el
|
||||
presupuesto.tabla.pais=País
|
||||
presupuesto.tabla.region=Región
|
||||
presupuesto.tabla.ciudad=Ciudad
|
||||
presupuesto.tabla.acciones=Acciones
|
||||
|
||||
# Pestaña datos generales de presupuesto
|
||||
presupuesto.informacion-libro=Información del libro
|
||||
|
||||
@ -928,7 +928,7 @@ File: Main Css File
|
||||
font-family: "Public Sans", sans-serif;
|
||||
}
|
||||
.navbar-menu .navbar-nav .nav-sm .nav-link:before {
|
||||
content: "";
|
||||
content: none;
|
||||
width: 6px;
|
||||
height: 1.5px;
|
||||
background-color: var(--vz-vertical-menu-sub-item-color);
|
||||
@ -6714,6 +6714,38 @@ a {
|
||||
border-left-color: #ff7f5d;
|
||||
}
|
||||
|
||||
/* ---- NAV SECUNDARIO CON PESTAÑITA OUTLINE ---- */
|
||||
/* Solo la pestaña activa: estilo outline + pestañita */
|
||||
.nav-secondary-outline.arrow-navtabs .nav-link.active {
|
||||
color: #ff7f5d;
|
||||
background: transparent;
|
||||
border: 1px solid #ff7f5d; /* como el botón Iniciar sesión */
|
||||
border-radius: .5rem;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* Si el tema ya dibuja una flecha sólida, la anulamos */
|
||||
.nav-secondary-outline.arrow-navtabs .nav-link.active::before {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Pestañita tipo outline */
|
||||
.nav-secondary-outline.arrow-navtabs .nav-link.active::after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
bottom: -7px; /* ajusta si lo necesitas */
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
transform: translateX(-50%) rotate(225deg);
|
||||
background: var(--vz-body-bg, var(--bs-body-bg, #fff)); /* color del fondo de la página */
|
||||
border-left: 1px solid #ff7f5d;
|
||||
border-top: 1px solid #ff7f5d;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.nav-success .nav-link.active {
|
||||
color: #fff;
|
||||
background-color: #3cd188;
|
||||
@ -16329,4 +16361,5 @@ span.flatpickr-weekday {
|
||||
position: absolute;
|
||||
bottom: -18px;
|
||||
left: -35px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -26,6 +26,22 @@
|
||||
pageLength: 50,
|
||||
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||
responsive: true,
|
||||
dom: 'Bfrtip',
|
||||
buttons: {
|
||||
dom: {
|
||||
button: {
|
||||
className: 'btn btn-sm btn-outline-primary me-1'
|
||||
},
|
||||
buttons: [
|
||||
{ extend: 'copy' },
|
||||
{ extend: 'csv' },
|
||||
{ extend: 'excel' },
|
||||
{ extend: 'pdf' },
|
||||
{ extend: 'print' },
|
||||
{ extend: 'colvis' }
|
||||
],
|
||||
}
|
||||
},
|
||||
ajax: {
|
||||
url: '/configuracion/margenes-presupuesto/datatable',
|
||||
method: 'GET',
|
||||
|
||||
@ -216,6 +216,7 @@ class PresupuestoCliente {
|
||||
if (tabButton) {
|
||||
bootstrap.Tab.getOrCreateInstance(tabButton).show();
|
||||
}
|
||||
window.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
}
|
||||
|
||||
#getPresupuestoData() {
|
||||
|
||||
@ -0,0 +1,75 @@
|
||||
(() => {
|
||||
// si jQuery está cargado, añade CSRF a AJAX
|
||||
const csrfToken = document.querySelector('meta[name="_csrf"]')?.getAttribute('content');
|
||||
const csrfHeader = document.querySelector('meta[name="_csrf_header"]')?.getAttribute('content');
|
||||
if (window.$ && csrfToken && csrfHeader) {
|
||||
$.ajaxSetup({
|
||||
beforeSend: function (xhr) {
|
||||
xhr.setRequestHeader(csrfHeader, csrfToken);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
const language = document.documentElement.lang || 'es-ES';
|
||||
|
||||
// Comprueba dependencias antes de iniciar
|
||||
if (!window.DataTable) {
|
||||
console.error('DataTables no está cargado aún');
|
||||
return;
|
||||
}
|
||||
|
||||
const table_anonimos = new DataTable('#presupuestos-anonimos-datatable', {
|
||||
processing: true,
|
||||
serverSide: true,
|
||||
orderCellsTop: true,
|
||||
stateSave: true,
|
||||
pageLength: 50,
|
||||
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||
responsive: true,
|
||||
dom: 'Bfrtip',
|
||||
buttons: {
|
||||
dom: {
|
||||
button: {
|
||||
className: 'btn btn-sm btn-outline-primary me-1'
|
||||
},
|
||||
buttons: [
|
||||
{ extend: 'copy' },
|
||||
{ extend: 'csv' },
|
||||
{ extend: 'excel' },
|
||||
{ extend: 'pdf' },
|
||||
{ extend: 'print' },
|
||||
{ extend: 'colvis' }
|
||||
],
|
||||
}
|
||||
},
|
||||
ajax: {
|
||||
url: '/presupuesto/datatable/anonimos',
|
||||
method: 'GET',
|
||||
data: function (d) {
|
||||
d.f_encuadernacion = $('#search-encuadernacion').val() || ''; // 'USER' | 'ADMIN' | 'SUPERADMIN' | ''
|
||||
d.f_cubierta = $('#search-cubierta').val() || ''; // 'true' | 'false' | ''
|
||||
}
|
||||
},
|
||||
order: [[0, 'asc']],
|
||||
columns: [
|
||||
{ data: 'id', name: 'id', orderable: true },
|
||||
{ data: 'cliente', name: 'cliente', orderable: true },
|
||||
{ data: 'tipoEncuadernacion', name: 'tipoEncuadernacion', orderable: true },
|
||||
{ data: 'tipoCubierta', name: 'tipoCubierta', orderable: true },
|
||||
{ data: 'tipoImpresion', name: 'tipoImpresion', orderable: true },
|
||||
{ data: 'tirada', name: 'tirada', orderable: true },
|
||||
{ data: 'paginas', name: 'paginas', orderable: true },
|
||||
{ data: 'estado', name: 'estado', orderable: true },
|
||||
{ data: 'total', name: 'total', orderable: true },
|
||||
{ data: 'pais', name: 'pais', orderable: true },
|
||||
{ data: 'region', name: 'region', orderable: true },
|
||||
{ data: 'ciudad', name: 'ciudad', orderable: true },
|
||||
{ data: 'updatedAt', name: 'updatedAt', orderable: true },
|
||||
{ data: 'actions', name: 'actions' }
|
||||
],
|
||||
columnDefs: [{ targets: -1, orderable: false, searchable: false }]
|
||||
});
|
||||
|
||||
|
||||
|
||||
})();
|
||||
@ -19,6 +19,22 @@ $(() => {
|
||||
pageLength: 50,
|
||||
language: { url: '/assets/libs/datatables/i18n/' + language + '.json' },
|
||||
responsive: true,
|
||||
dom: 'Bfrtip',
|
||||
buttons: {
|
||||
dom: {
|
||||
button: {
|
||||
className: 'btn btn-sm btn-outline-primary me-1'
|
||||
},
|
||||
buttons: [
|
||||
{ extend: 'copy' },
|
||||
{ extend: 'csv' },
|
||||
{ extend: 'excel' },
|
||||
{ extend: 'pdf' },
|
||||
{ extend: 'print' },
|
||||
{ extend: 'colvis' }
|
||||
],
|
||||
}
|
||||
},
|
||||
ajax: {
|
||||
url: '/users/datatable',
|
||||
method: 'GET',
|
||||
|
||||
5
src/main/resources/static/assets/libs/datatables/buttons.colVis.min.js
vendored
Normal file
5
src/main/resources/static/assets/libs/datatables/buttons.colVis.min.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Column visibility buttons for Buttons and DataTables.
|
||||
* © SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
!function(i){var o,e;"function"==typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(n){return i(n,window,document)}):"object"==typeof exports?(o=require("jquery"),e=function(n,t){t.fn.dataTable||require("datatables.net")(n,t),t.fn.dataTable.Buttons||require("datatables.net-buttons")(n,t)},"undefined"==typeof window?module.exports=function(n,t){return n=n||window,t=t||o(n),e(n,t),i(t,0,n.document)}:(e(window,o),module.exports=i(o,window,window.document))):i(jQuery,window,document)}(function(n,t,i){"use strict";var e=n.fn.dataTable;return n.extend(e.ext.buttons,{colvis:function(n,t){var i=null,o={extend:"collection",init:function(n,t){i=t},text:function(n){return n.i18n("buttons.colvis","Column visibility")},className:"buttons-colvis",closeButton:!1,buttons:[{extend:"columnsToggle",columns:t.columns,columnText:t.columnText}]};return n.on("column-reorder.dt"+t.namespace,function(){n.button(null,n.button(null,i).node()).collectionRebuild([{extend:"columnsToggle",columns:t.columns,columnText:t.columnText}])}),o},columnsToggle:function(n,t){return n.columns(t.columns).indexes().map(function(n){return{extend:"columnToggle",columns:n,columnText:t.columnText}}).toArray()},columnToggle:function(n,t){return{extend:"columnVisibility",columns:t.columns,columnText:t.columnText}},columnsVisibility:function(n,t){return n.columns(t.columns).indexes().map(function(n){return{extend:"columnVisibility",columns:n,visibility:t.visibility,columnText:t.columnText}}).toArray()},columnVisibility:{columns:void 0,text:function(n,t,i){return i._columnText(n,i)},className:"buttons-columnVisibility",action:function(n,t,i,o){var t=t.columns(o.columns),e=t.visible();t.visible(void 0!==o.visibility?o.visibility:!(e.length&&e[0]))},init:function(e,n,t){var u=this,l=e.column(t.columns);n.attr("data-cv-idx",t.columns),e.on("column-visibility.dt"+t.namespace,function(n,t,i,o){l.index()!==i||t.bDestroying||t.nTable!=e.settings()[0].nTable||u.active(o)}).on("column-reorder.dt"+t.namespace,function(){t.destroying||1===e.columns(t.columns).count()&&(l=e.column(t.columns),u.text(t._columnText(e,t)),u.active(l.visible()))}),this.active(l.visible())},destroy:function(n,t,i){n.off("column-visibility.dt"+i.namespace).off("column-reorder.dt"+i.namespace)},_columnText:function(n,t){var i,o;return"string"==typeof t.text?t.text:(o=n.column(t.columns).title(),i=n.column(t.columns).index(),o=o.replace(/\n/g," ").replace(/<br\s*\/?>/gi," ").replace(/<select(.*?)<\/select\s*>/gi,""),o=e.Buttons.stripHtmlComments(o),o=e.util.stripHtml(o).trim(),t.columnText?t.columnText(n,i,o):o)}},colvisRestore:{className:"buttons-colvisRestore",text:function(n){return n.i18n("buttons.colvisRestore","Restore visibility")},init:function(n,t,i){n.columns().every(function(){var n=this.init();void 0===n.__visOriginal&&(n.__visOriginal=this.visible())})},action:function(n,t,i,o){t.columns().every(function(n){var t=this.init();this.visible(t.__visOriginal)})}},colvisGroup:{className:"buttons-colvisGroup",action:function(n,t,i,o){t.columns(o.show).visible(!0,!1),t.columns(o.hide).visible(!1,!1),t.columns.adjust()},show:[],hide:[]}}),e});
|
||||
8
src/main/resources/static/assets/libs/datatables/buttons.html5.min.js
vendored
Normal file
8
src/main/resources/static/assets/libs/datatables/buttons.html5.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
5
src/main/resources/static/assets/libs/datatables/buttons.print.min.js
vendored
Normal file
5
src/main/resources/static/assets/libs/datatables/buttons.print.min.js
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/*!
|
||||
* Print button for Buttons and DataTables.
|
||||
* © SpryMedia Ltd - datatables.net/license
|
||||
*/
|
||||
!function(n){var o,r;"function"==typeof define&&define.amd?define(["jquery","datatables.net","datatables.net-buttons"],function(t){return n(t,window,document)}):"object"==typeof exports?(o=require("jquery"),r=function(t,e){e.fn.dataTable||require("datatables.net")(t,e),e.fn.dataTable.Buttons||require("datatables.net-buttons")(t,e)},"undefined"==typeof window?module.exports=function(t,e){return t=t||window,e=e||o(t),r(t,e),n(e,t,t.document)}:(r(window,o),module.exports=n(o,window,window.document))):n(jQuery,window,document)}(function(p,f,t){"use strict";function m(t){return n.href=t,-1===(t=n.host).indexOf("/")&&0!==n.pathname.indexOf("/")&&(t+="/"),n.protocol+"//"+t+n.pathname+n.search}var e=p.fn.dataTable,n=t.createElement("a");return e.ext.buttons.print={className:"buttons-print",text:function(t){return t.i18n("buttons.print","Print")},action:function(t,e,n,o,r){var a=e.buttons.exportData(p.extend({decodeEntities:!1},o.exportOptions)),i=e.buttons.exportInfo(o),s=e.columns(o.exportOptions.columns).nodes().map(function(t){return t.className}).toArray(),u='<table class="'+e.table().node().className+'">';o.header&&(u+="<thead>"+a.headerStructure.map(function(t){return"<tr>"+t.map(function(t){return t?'<th colspan="'+t.colspan+'" rowspan="'+t.rowspan+'">'+t.title+"</th>":""}).join("")+"</tr>"}).join("")+"</thead>"),u+="<tbody>";for(var c=0,d=a.body.length;c<d;c++)u+=function(t,e){for(var n="<tr>",o=0,r=t.length;o<r;o++){var a=null===t[o]||void 0===t[o]?"":t[o];n+="<"+e+" "+(s[o]?'class="'+s[o]+'"':"")+">"+a+"</"+e+">"}return n+"</tr>"}(a.body[c],"td");u+="</tbody>",o.footer&&a.footer&&(u+="<tfoot>"+a.footerStructure.map(function(t){return"<tr>"+t.map(function(t){return t?'<th colspan="'+t.colspan+'" rowspan="'+t.rowspan+'">'+t.title+"</th>":""}).join("")+"</tr>"}).join("")+"</tfoot>"),u+="</table>";var l=f.open("","");l?(l.document.close(),l.document.title=i.title,p('style, link[rel="stylesheet"]').each(function(){var t=this.cloneNode(!0);"link"===t.tagName.toLowerCase()&&(t.href=m(t.href)),l.document.head.appendChild(t)}),o.customScripts&&o.customScripts.forEach(function(t){var e=l.document.createElement("script");e.src=t,l.document.getElementsByTagName("head")[0].appendChild(e)}),l.document.body.innerHTML="<h1>"+i.title+"</h1><div>"+(i.messageTop||"")+"</div>"+u+"<div>"+(i.messageBottom||"")+"</div>",p(l.document.body).addClass("dt-print-view"),p("img",l.document.body).each(function(t,e){e.setAttribute("src",m(e.getAttribute("src")))}),o.customize&&o.customize(l,o,e),l.setTimeout(function(){o.autoPrint&&(l.print(),l.close())},1e3),r()):e.buttons.info(e.i18n("buttons.printErrorTitle","Unable to open print view"),e.i18n("buttons.printErrorMsg","Please allow popups in your browser for this site to be able to view the print view."),5e3)},async:100,title:"*",messageTop:"*",messageBottom:"*",exportOptions:{},header:!0,footer:!0,autoPrint:!0,customize:null},e});
|
||||
4
src/main/resources/static/assets/libs/datatables/dataTables.buttons.min.js
vendored
Normal file
4
src/main/resources/static/assets/libs/datatables/dataTables.buttons.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
13
src/main/resources/static/assets/libs/jszip/jszip.min.js
vendored
Normal file
13
src/main/resources/static/assets/libs/jszip/jszip.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
3
src/main/resources/static/assets/libs/pdfmake/pdfmake.min.js
vendored
Normal file
3
src/main/resources/static/assets/libs/pdfmake/pdfmake.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/main/resources/static/assets/libs/pdfmake/vfs_fonts.min.js
vendored
Normal file
1
src/main/resources/static/assets/libs/pdfmake/vfs_fonts.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@ -29,21 +29,24 @@
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{margenes-presupuesto.breadcrumb}">Márgenes de presupuesto</li>
|
||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{margenes-presupuesto.breadcrumb}">
|
||||
Márgenes de presupuesto</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<button type="button" class="btn btn-secondary mb-3" id="addButton">
|
||||
<i class="ri-add-line align-bottom me-1"></i> <span th:text="#{margenes-presupuesto.add}">Añadir</span>
|
||||
<i class="ri-add-line align-bottom me-1"></i> <span
|
||||
th:text="#{margenes-presupuesto.add}">Añadir</span>
|
||||
</button>
|
||||
|
||||
<table id="margenes-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.id}">ID</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tipo_encuadernacion}">Tipo encuadernación</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tipo_encuadernacion}">Tipo
|
||||
encuadernación</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tipo_cubierta}">Tipo cubierta</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tirada_minima}">Tirada Mín.</th>
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.tirada_maxima}">Tirada Máx.</th>
|
||||
@ -52,9 +55,11 @@
|
||||
<th scope="col" th:text="#{margenes-presupuesto.tabla.acciones}">Acciones</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th><input type="text" class="form-control form-control-sm margenes-presupuesto-filter" data-col="id" /></th>
|
||||
<th><input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="id" /></th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm margenes-presupuesto-select-filter" id="search-encuadernacion">
|
||||
<select class="form-select form-select-sm margenes-presupuesto-select-filter"
|
||||
id="search-encuadernacion">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="fresado" th:text="#{presupuesto.fresado}">Fresado</option>
|
||||
<option value="cosido" th:text="#{presupuesto.cosido}">Cosido</option>
|
||||
@ -64,24 +69,30 @@
|
||||
</select>
|
||||
</th>
|
||||
<th>
|
||||
<select class="form-select form-select-sm margenes-presupuesto-select-filter" id="search-cubierta">
|
||||
<select class="form-select form-select-sm margenes-presupuesto-select-filter"
|
||||
id="search-cubierta">
|
||||
<option value="" th:text="#{margenes-presupuesto.todos}">Todos</option>
|
||||
<option value="tapaBlanda" th:text="#{presupuesto.tapa-blanda}"></option>
|
||||
<option value="tapaDura" th:text="#{presupuesto.tapa-dura}"></option>
|
||||
<option value="tapaDuraLomoRedondo" th:text="#{presupuesto.tapa-dura-lomo-redondo}"></option>
|
||||
<option value="tapaDuraLomoRedondo" th:text="#{presupuesto.tapa-dura-lomo-redondo}">
|
||||
</option>
|
||||
</select>
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter" data-col="tiradaMin" />
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="tiradaMin" />
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter" data-col="tiradaMax" />
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="tiradaMax" />
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter" data-col="margenMax" />
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="margenMax" />
|
||||
</th>
|
||||
<th>
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter" data-col="margenMin" />
|
||||
<input type="text" class="form-control form-control-sm margenes-presupuesto-filter"
|
||||
data-col="margenMin" />
|
||||
</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
@ -101,7 +112,18 @@
|
||||
</script>
|
||||
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
|
||||
|
||||
<!-- JS de Buttons y dependencias -->
|
||||
<script th:src="@{/assets/libs/datatables/dataTables.buttons.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/jszip/jszip.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/pdfmake/pdfmake.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/pdfmake/vfs_fonts.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.html5.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||
|
||||
<script th:src="@{/assets/js/pages/imprimelibros/configuracion/margenes-presupuesto/list.js}"></script>
|
||||
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
|
||||
@ -22,8 +22,7 @@
|
||||
</span>
|
||||
</a>
|
||||
<button type="button" class="btn btn-sm p-0 fs-20 header-item float-end btn-vertical-sm-hover"
|
||||
id="vertical-hover"
|
||||
href="/#" data-bs-toggle="tooltip" data-bs-placement="right" title="Expand">
|
||||
id="vertical-hover" href="/#" data-bs-toggle="tooltip" data-bs-placement="right" title="Expand">
|
||||
<i class="ri-record-circle-line"></i>
|
||||
</button>
|
||||
</div>
|
||||
@ -40,23 +39,35 @@
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link menu-link" href="/users">
|
||||
<i class="ri-user-line"></i> <span th:text="#{app.sidebar.usuarios}">Usuarios</span>
|
||||
<a class="nav-link menu-link" href="/presupuesto">
|
||||
<i class="ri-file-paper-2-line"></i> <span th:text="#{app.sidebar.presupuestos}">Presupuestos</span>
|
||||
</a>
|
||||
</li>
|
||||
<div th:if="${#authentication.principal.role == 'SUPERADMIN'}">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link menu-link collapsed" href="#sidebarConfig" data-bs-toggle="collapse" role="button" aria-expanded="false" aria-controls="sidebarConfig">
|
||||
<i class="ri-settings-2-line"></i> <span th:text="#{app.sidebar.configuracion}">Configuración</span>
|
||||
</a>
|
||||
<a class="nav-link menu-link collapsed" href="#sidebarConfig" data-bs-toggle="collapse"
|
||||
role="button" aria-expanded="false" aria-controls="sidebarConfig">
|
||||
<i class="ri-settings-2-line"></i> <span
|
||||
th:text="#{app.sidebar.configuracion}">Configuración</span>
|
||||
</a>
|
||||
<div class="collapse menu-dropdown" id="sidebarConfig">
|
||||
<ul class="nav nav-sm flex-column">
|
||||
<li class="nav-item">
|
||||
<a href="/configuracion/margenes-presupuesto" class="nav-link" th:text="#{margenes-presupuesto.titulo}">Márgenes de presupuesto</a>
|
||||
<a class="nav-link menu-link" href="/users">
|
||||
<i class="ri-user-line"></i> <span
|
||||
th:text="#{app.sidebar.usuarios}">Usuarios</span>
|
||||
</a>
|
||||
</li>
|
||||
<div th:if="${#authentication.principal.role == 'SUPERADMIN'}">
|
||||
<li class="nav-item">
|
||||
<a href="/configuracion/margenes-presupuesto" class="nav-link">
|
||||
<i class="ri-discount-percent-line"></i>
|
||||
<span th:text="#{margenes-presupuesto.titulo}">Márgenes de presupuesto</span>
|
||||
</a>
|
||||
</li>
|
||||
</div>
|
||||
</ul>
|
||||
</li>
|
||||
</div>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
<!-- Sidebar -->
|
||||
|
||||
@ -0,0 +1,28 @@
|
||||
<div th:fragment="tabla-anonimos">
|
||||
<table id="presupuesto-anonimos-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.id}">ID</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.titulo}">Título</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.cliente}">Cliente</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.encuadernacion}">Encuadernación</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.cubierta}">Cubierta</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.tipo-impresion}">Tipo de impresión</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.tirada}">Tirada</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.paginas}">Páginas</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.estado}">Estado</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.total-iva}">Total con IVA</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.pais}">País</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.region}">Región</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.ciudad}">Ciudad</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -0,0 +1,25 @@
|
||||
<div th:fragment="tabla-cliente">
|
||||
<table id="presupuesto-clientes-datatable" class="table table-striped table-nowrap responsive w-100">
|
||||
<thead>
|
||||
<tr>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.id}">ID</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.titulo}">Título</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.cliente}">Cliente</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.encuadernacion}">Encuadernación</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.cubierta}">Cubierta</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.tipo-impresion}">Tipo de impresión</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.tirada}">Tirada</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.paginas}">Páginas</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.estado}">Estado</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.total-iva}">Total con IVA</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.updated-at}">Actualizado el</th>
|
||||
<th scope="col" th:text="#{presupuesto.tabla.acciones}">Acciones</th>
|
||||
</tr>
|
||||
<tr>
|
||||
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@ -0,0 +1,99 @@
|
||||
<!doctype html>
|
||||
<html xmlns:th="http://www.thymeleaf.org" xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout"
|
||||
layout:decorate="~{imprimelibros/layout}">
|
||||
|
||||
<head>
|
||||
<th:block layout:fragment="pagetitle" />
|
||||
<th:block th:replace="~{imprimelibros/partials/head-css :: head-css}" />
|
||||
<th:block layout:fragment="pagecss">
|
||||
<link th:href="@{/assets/libs/datatables/dataTables.bootstrap5.min.css}" rel="stylesheet" />
|
||||
</th:block>
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div th:replace="~{imprimelibros/partials/topbar :: topbar}" />
|
||||
<div th:replace="~{imprimelibros/partials/sidebar :: sidebar}"
|
||||
th:unless="${#authorization.expression('isAuthenticated()') and (#authorization.expression('hasRole('SUPERADMIN', 'ADMIN')'))}" />
|
||||
|
||||
<th:block layout:fragment="content">
|
||||
<div th:if="${#authorization.expression('isAuthenticated()')}">
|
||||
|
||||
|
||||
<nav aria-label="breadcrumb">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="/"><i class="ri-home-5-fill"></i></a></li>
|
||||
<li class="breadcrumb-item active" aria-current="page" th:text="#{presupuesto.title}">Presupuestos
|
||||
</li>
|
||||
</ol>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
|
||||
<button type="button" class="btn btn-secondary mb-3" id="addPresupuestoButton">
|
||||
<i class="ri-add-line align-bottom me-1"></i> <span th:text="#{presupuesto.add}">Añadir
|
||||
presupuesto</span>
|
||||
</button>
|
||||
|
||||
|
||||
<ul class="nav nav-pills arrow-navtabs nav-secondary-outline bg-light mb-3" role="tablist">
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link active" data-bs-toggle="tab" href="#arrow-presupuestos-cliente" role="tab"
|
||||
aria-selected="true">
|
||||
<span class="d-block d-sm-none"><i class="mdi mdi-home-variant"></i></span>
|
||||
<span class="d-none d-sm-block"
|
||||
th:text="#{presupuesto.nav.presupuestos-cliente}">Presupuestos cliente</span>
|
||||
</a>
|
||||
</li>
|
||||
<li class="nav-item" role="presentation">
|
||||
<a class="nav-link" data-bs-toggle="tab" href="#arrow-presupuestos-anonimos" role="tab"
|
||||
aria-selected="false" tabindex="-1">
|
||||
<span class="d-block d-sm-none"><i class="mdi mdi-account"></i></span>
|
||||
<span class="d-none d-sm-block"
|
||||
th:text="#{presupuesto.nav.presupuestos-anonimos}">Presupuestos anónimos</span>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Tab panes -->
|
||||
<div class="tab-content text-muted">
|
||||
<div class="tab-pane active show" id="arrow-presupuestos-cliente" role="tabpanel">
|
||||
|
||||
<div
|
||||
th:insert="~{imprimelibros/presupuestos/presupuesto-list-items/tabla-cliente :: tabla-cliente}">
|
||||
</div>
|
||||
</div>
|
||||
<div class="tab-pane" id="arrow-presupuestos-anonimos" role="tabpanel">
|
||||
|
||||
<div
|
||||
th:insert="~{imprimelibros/presupuestos/presupuesto-list-items/tabla-anonimos :: tabla-anonimos}">
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</th:block>
|
||||
|
||||
<th:block th:replace="~{theme/partials/vendor-scripts :: scripts}" />
|
||||
<th:block layout:fragment="pagejs">
|
||||
<script th:inline="javascript">
|
||||
window.languageBundle = /*[[${languageBundle}]]*/ {};
|
||||
</script>
|
||||
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
|
||||
|
||||
<!-- JS de Buttons y dependencias -->
|
||||
<script th:src="@{/assets/libs/datatables/dataTables.buttons.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/jszip/jszip.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/pdfmake/pdfmake.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/pdfmake/vfs_fonts.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.html5.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||
|
||||
<script th:src="@{/assets/js/pages/imprimelibros/presupuestos/list.js}"></script>
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@ -90,6 +90,16 @@
|
||||
</script>
|
||||
<script th:src="@{/assets/libs/datatables/datatables.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/dataTables.bootstrap5.min.js}"></script>
|
||||
|
||||
<!-- JS de Buttons y dependencias -->
|
||||
<script th:src="@{/assets/libs/datatables/dataTables.buttons.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/jszip/jszip.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/pdfmake/pdfmake.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/pdfmake/vfs_fonts.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.html5.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.print.min.js}"></script>
|
||||
<script th:src="@{/assets/libs/datatables/buttons.colVis.min.js}"></script>
|
||||
|
||||
<script th:src="@{/assets/js/pages/imprimelibros/users/list.js}"></script>
|
||||
</th:block>
|
||||
</body>
|
||||
|
||||
Reference in New Issue
Block a user