diff --git a/src/main/java/com/imprimelibros/erp/home/HomeController.java b/src/main/java/com/imprimelibros/erp/home/HomeController.java index 572b17a..1538327 100644 --- a/src/main/java/com/imprimelibros/erp/home/HomeController.java +++ b/src/main/java/com/imprimelibros/erp/home/HomeController.java @@ -42,6 +42,8 @@ public class HomeController { model.addAttribute("pod", variableService.getValorEntero("POD")); model.addAttribute("ancho_alto_min", variableService.getValorEntero("ancho_alto_min")); model.addAttribute("ancho_alto_max", variableService.getValorEntero("ancho_alto_max")); + + model.addAttribute("appMode", "public"); } else{ // empty translations for authenticated users diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java index ac38d57..76c90b9 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoController.java @@ -5,6 +5,7 @@ import org.springframework.ui.Model; import java.util.HashMap; import java.util.Locale; import java.util.Map; +import java.util.Objects; import java.util.Optional; import java.util.List; @@ -17,6 +18,8 @@ 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.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.support.RedirectAttributes; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.ModelAttribute; @@ -35,7 +38,10 @@ import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto; import com.imprimelibros.erp.presupuesto.classes.PresupuestoMaquetacion; import com.imprimelibros.erp.presupuesto.classes.PresupuestoMarcapaginas; import com.imprimelibros.erp.presupuesto.dto.Presupuesto; +import com.imprimelibros.erp.presupuesto.service.PresupuestoService; import com.imprimelibros.erp.presupuesto.validation.PresupuestoValidationGroups; +import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper; +import com.imprimelibros.erp.presupuesto.service.PresupuestoFormDataMapper.PresupuestoFormDataDto; import jakarta.servlet.http.HttpServletRequest; import jakarta.validation.Valid; @@ -59,15 +65,17 @@ public class PresupuestoController { private final TranslationService translationService; private final PresupuestoDatatableService dtService; private final VariableService variableService; + private final PresupuestoFormDataMapper formDataMapper; public PresupuestoController(ObjectMapper objectMapper, TranslationService translationService, PresupuestoDatatableService dtService, PresupuestoRepository presupuestoRepository, - VariableService variableService) { + VariableService variableService, PresupuestoFormDataMapper formDataMapper) { this.objectMapper = objectMapper; this.translationService = translationService; this.dtService = dtService; this.presupuestoRepository = presupuestoRepository; this.variableService = variableService; + this.formDataMapper = formDataMapper; } @PostMapping("/public/validar/datos-generales") @@ -77,12 +85,14 @@ public class PresupuestoController { Map errores = new HashMap<>(); - // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); - - // errores globales (@ConsistentTiradas...) - result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); - + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + result.getGlobalErrors().forEach(error -> { + errores.put("global", error.getDefaultMessage()); + }); if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); } @@ -106,7 +116,12 @@ public class PresupuestoController { Map errores = new HashMap<>(); // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + // errores globales (@ConsistentTiradas...) result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); @@ -130,7 +145,12 @@ public class PresupuestoController { Map errores = new HashMap<>(); // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + // errores globales (@ConsistentTiradas...) result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); @@ -161,7 +181,12 @@ public class PresupuestoController { Map errores = new HashMap<>(); // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + // errores globales (@ConsistentTiradas...) result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage())); @@ -188,7 +213,12 @@ public class PresupuestoController { Map errores = new HashMap<>(); // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); @@ -200,7 +230,19 @@ public class PresupuestoController { // opciones gramaje interior resultado.putAll(presupuestoService.obtenerOpcionesGramajeInterior(presupuesto)); - List opciones = new ObjectMapper().convertValue(resultado.get("opciones_papel_interior"), + List opciones_papel = new ObjectMapper().convertValue( + presupuestoService + .obtenerOpcionesPapelInterior(presupuesto, locale) + .get("opciones_papel_interior"), + new TypeReference>() { + }); + + if (opciones_papel != null && opciones_papel.stream().noneMatch( + o -> o.getExtra_data().get("sk-id").equals(String.valueOf(presupuesto.getPapelInteriorId())))) { + presupuesto.setPapelInteriorId(Integer.valueOf(opciones_papel.get(0).getExtra_data().get("sk-id"))); + } + + List opciones = new ObjectMapper().convertValue(resultado.get("opciones_gramaje_interior"), new TypeReference>() { }); @@ -218,12 +260,17 @@ public class PresupuestoController { @PostMapping("/public/get-gramaje-interior") public ResponseEntity getGramajeInterior( @Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto, - BindingResult result) { + BindingResult result, Locale locale) { Map errores = new HashMap<>(); // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); @@ -247,12 +294,17 @@ public class PresupuestoController { @PostMapping("/public/get-max-solapas") public ResponseEntity getMaxSolapas( @Validated(PresupuestoValidationGroups.Interior.class) Presupuesto presupuesto, - BindingResult result) { + BindingResult result, Locale locale) { Map errores = new HashMap<>(); // errores de campos individuales - result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage())); + result.getFieldErrors().forEach(error -> { + String code = Objects.requireNonNullElse(error.getDefaultMessage(), "").replace("{", "").replace("}", ""); + String msg = messageSource.getMessage(code, null, locale); + errores.put(error.getField(), msg); + }); + if (!errores.isEmpty()) { return ResponseEntity.badRequest().body(errores); @@ -462,16 +514,6 @@ public class PresupuestoController { // Buscar el presupuesto Optional presupuestoOpt = presupuestoRepository.findById(id); - boolean isUser = authentication.getAuthorities().stream() - .anyMatch(a -> a.getAuthority().equals("ROLE_USER")); - - if (isUser) { - // Si es usuario, solo puede ver sus propios presupuestos - String username = authentication.getName(); - if (!presupuestoOpt.get().getUser().getUserName().equals(username)) { - presupuestoOpt = Optional.empty(); - } - } if (presupuestoOpt.isEmpty()) { // Añadir mensaje flash para mostrar alerta @@ -481,11 +523,44 @@ public class PresupuestoController { return "redirect:/presupuesto"; } - // Si existe, lo añadimos al modelo - model.addAttribute("presupuesto", presupuestoOpt.get()); + if (!presupuestoService.canAccessPresupuesto(presupuestoOpt.get(), authentication)) { + // Añadir mensaje flash para mostrar alerta + redirectAttributes.addFlashAttribute("errorMessage", + messageSource.getMessage("app.errors.403", null, locale)); + // Redirigir a la vista de lista + return "redirect:/presupuesto"; + } + + String path = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()) + .getRequest().getRequestURI(); + String mode = path.contains("/view/") ? "view" : "edit"; + if (mode.equals("view")) { + model.addAttribute("appMode", "view"); + } else { + model.addAttribute("appMode", "edit"); + } + model.addAttribute("id", presupuestoOpt.get().getId()); return "imprimelibros/presupuestos/presupuesto-form"; } + @GetMapping(value = "/api/get", produces = "application/json") + public ResponseEntity getPresupuesto( + @RequestParam("id") Long id, Authentication authentication) { + + Optional presupuestoOpt = presupuestoRepository.findById(id); + + if (!presupuestoService.canAccessPresupuesto(presupuestoOpt.get(), authentication)) { + return ResponseEntity.status(403).build(); + } + + if (presupuestoOpt.isPresent()) { + PresupuestoFormDataDto vm = formDataMapper.toFormData(presupuestoOpt.get()); + return ResponseEntity.ok(vm); + } else { + return ResponseEntity.notFound().build(); + } + } + @GetMapping(value = "/datatable/anonimos", produces = "application/json") @ResponseBody public DataTablesResponse> datatableAnonimos( diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoFormDataMapper.java b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoFormDataMapper.java new file mode 100644 index 0000000..31234bd --- /dev/null +++ b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoFormDataMapper.java @@ -0,0 +1,266 @@ +package com.imprimelibros.erp.presupuesto.service; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.imprimelibros.erp.presupuesto.dto.Presupuesto; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +@Service +public class PresupuestoFormDataMapper { + + public class PresupuestoFormDataDto { + + public DatosGenerales datosGenerales = new DatosGenerales(); + public Interior interior = new Interior(); + public Cubierta cubierta = new Cubierta(); + public Servicios servicios = new Servicios(); + public Integer selectedTirada; + + // ===== Datos Generales ===== + public static class DatosGenerales { + public String titulo = ""; + public String autor = ""; + public String isbn = ""; + public String tirada1 = ""; + public String tirada2 = ""; + public String tirada3 = ""; + public String tirada4 = ""; + public Integer ancho; + public Integer alto; + public boolean formatoPersonalizado = false; + public String paginasNegro = ""; + public String paginasColor = ""; + public String posicionPaginasColor = ""; + public String tipoEncuadernacion = "fresado"; // enum name + } + + // ===== Interior ===== + public static class Interior { + public String tipoImpresion = "negro"; // enum name + public Integer papelInteriorId = 3; + public Integer gramajeInterior = 80; + } + + // ===== Cubierta ===== + public static class Cubierta { + public String tipoCubierta = "tapaBlanda"; // enum name + public int solapasCubierta = 0; // 0/1 para tu UI + public String tamanioSolapasCubierta = "80"; + public Integer cubiertaCaras = 2; + + public Integer papelGuardasId = 3; + public Integer gramajeGuardas = 170; + public Integer guardasImpresas = 0; + public String cabezada = "WHI"; + + public Integer papelCubiertaId = 3; + public Integer gramajeCubierta = 170; + public Integer acabado = 1; + + public SobreCubierta sobrecubierta = new SobreCubierta(); + public Faja faja = new Faja(); + + public static class SobreCubierta { + public boolean activo = false; + public Integer papelSobrecubiertaId = 2; + public Integer gramajeSobrecubierta = 170; + public Integer tamanioSolapasSobrecubierta = 80; + public Integer acabado = 0; + } + + public static class Faja { + public boolean activo = false; + public Integer papelFajaId = 2; + public Integer gramajeFaja = 170; + public Integer alto = 50; + public Integer tamanioSolapasFaja = 80; + public Integer acabado = 0; + } + } + + // ===== Servicios / Extras ===== + public static class Servicios { + public List servicios = List.of(); + public DatosMarcapaginas datosMarcapaginas = new DatosMarcapaginas(); + public DatosMaquetacion datosMaquetacion = new DatosMaquetacion(); + + public static class DatosMarcapaginas { + public Integer marcapaginas_tirada = 100; + public String tamanio_marcapaginas = "_50x140_"; + public String caras_impresion = "una_cara"; + public String papel_marcapaginas = "cartulina_grafica"; + public Integer gramaje_marcapaginas = 300; + public String acabado_marcapaginas = "ninguno"; + public Resultado resultado = new Resultado(); + } + + public static class DatosMaquetacion { + public Integer num_caracteres = 200000; + public String formato_maquetacion = "A5"; + public String cuerpo_texto = "medium"; + public Integer num_columnas = 1; + public Integer num_tablas = 0; + public Integer num_fotos = 0; + public boolean correccion_ortotipografica = false; + public boolean texto_mecanografiado = false; + public boolean disenio_portada = false; + public boolean epub = false; + public Resultado resultado = new Resultado(); + } + + public static class Resultado { + public Integer num_paginas_estimadas = 0; // solo para maquetación + public Integer precio_pagina_estimado = 0; + public Number precio_unitario = 0; // solo para marcapáginas + public Number precio = 0; + } + } + } + + private final ObjectMapper om = new ObjectMapper(); + + public PresupuestoFormDataDto toFormData(Presupuesto p) { + + PresupuestoFormDataDto vm = new PresupuestoFormDataDto(); + + // ===== Datos Generales + vm.datosGenerales.titulo = nz(p.getTitulo(), ""); + vm.datosGenerales.autor = nz(p.getAutor(), ""); + vm.datosGenerales.isbn = nz(p.getIsbn(), ""); + + vm.datosGenerales.tirada1 = nzStr(p.getTirada1()); + vm.datosGenerales.tirada2 = nzStr(p.getTirada2()); + vm.datosGenerales.tirada3 = nzStr(p.getTirada3()); + vm.datosGenerales.tirada4 = nzStr(p.getTirada4()); + + vm.datosGenerales.ancho = p.getAncho(); + vm.datosGenerales.alto = p.getAlto(); + vm.datosGenerales.formatoPersonalizado = Boolean.TRUE.equals(p.getFormatoPersonalizado()); + + vm.datosGenerales.paginasNegro = nzStr(p.getPaginasNegro()); + vm.datosGenerales.paginasColor = nzStr(p.getPaginasColor()); + vm.datosGenerales.posicionPaginasColor = nz(p.getPosicionPaginasColor(), ""); + + vm.datosGenerales.tipoEncuadernacion = enumName(p.getTipoEncuadernacion(), "fresado"); + + // ===== Interior + vm.interior.tipoImpresion = enumName(p.getTipoImpresion(), "negro"); + vm.interior.papelInteriorId = nz(p.getPapelInteriorId(), 3); + vm.interior.gramajeInterior = nz(p.getGramajeInterior(), 80); + + // ===== Cubierta + vm.cubierta.tipoCubierta = enumName(p.getTipoCubierta(), "tapaBlanda"); + vm.cubierta.solapasCubierta = Boolean.TRUE.equals(p.getSolapasCubierta()) ? 1 : 0; + vm.cubierta.tamanioSolapasCubierta = String.valueOf(nz(p.getTamanioSolapasCubierta(), 80)); + vm.cubierta.cubiertaCaras = nz(p.getCubiertaCaras(), 2); + + vm.cubierta.papelGuardasId = nz(p.getPapelGuardasId(), 3); + vm.cubierta.gramajeGuardas = nz(p.getGramajeGuardas(), 170); + vm.cubierta.guardasImpresas = nz(p.getGuardasImpresas(), 0); + vm.cubierta.cabezada = nz(p.getCabezada(), "WHI"); + + vm.cubierta.papelCubiertaId = nz(p.getPapelCubiertaId(), 3); + vm.cubierta.gramajeCubierta = nz(p.getGramajeCubierta(), 170); + vm.cubierta.acabado = nz(p.getAcabado(), 1); + + vm.cubierta.sobrecubierta.activo = Boolean.TRUE.equals(p.getSobrecubierta()); + vm.cubierta.sobrecubierta.papelSobrecubiertaId = nz(p.getPapelSobrecubiertaId(), 2); + vm.cubierta.sobrecubierta.gramajeSobrecubierta = nz(p.getGramajeSobrecubierta(), 170); + vm.cubierta.sobrecubierta.tamanioSolapasSobrecubierta = nz(p.getTamanioSolapasSobrecubierta(), 80); + vm.cubierta.sobrecubierta.acabado = nz(p.getAcabadoSobrecubierta(), 0); + + vm.cubierta.faja.activo = Boolean.TRUE.equals(p.getFaja()); + vm.cubierta.faja.papelFajaId = nz(p.getPapelFajaId(), 2); + vm.cubierta.faja.gramajeFaja = nz(p.getGramajeFaja(), 170); + vm.cubierta.faja.tamanioSolapasFaja = nz(p.getTamanioSolapasFaja(), 80); + vm.cubierta.faja.acabado = nz(p.getAcabadoFaja(), 0); + vm.cubierta.faja.alto = nz(p.getAltoFaja(), 50); + + // ===== Selected tirada + vm.selectedTirada = p.getSelectedTirada(); + + // ===== Servicios desde JSONs + // servicios_json: acepta ["maquetacion","marcapaginas"] o [{id:...}, ...] + vm.servicios.servicios = parseServiciosIds(p.getServiciosJson()); + + // datos_maquetacion_json + PresupuestoFormDataDto.Servicios.DatosMaquetacion maq = parse(p.getDatosMaquetacionJson(), + PresupuestoFormDataDto.Servicios.DatosMaquetacion.class); + if (maq != null) + vm.servicios.datosMaquetacion = merge(vm.servicios.datosMaquetacion, maq); + + // datos_marcapaginas_json + PresupuestoFormDataDto.Servicios.DatosMarcapaginas mp = parse(p.getDatosMarcapaginasJson(), + PresupuestoFormDataDto.Servicios.DatosMarcapaginas.class); + if (mp != null) + vm.servicios.datosMarcapaginas = merge(vm.servicios.datosMarcapaginas, mp); + + return vm; + } + + // ===== Helpers ===== + + private static String enumName(Enum e, String def) { + return e != null ? e.name() : def; + } + + private static String nz(String v, String def) { + return v != null ? v : def; + } + + private static Integer nz(Integer v, Integer def) { + return v != null ? v : def; + } + + private static String nzStr(Integer v) { + return v == null ? "" : String.valueOf(v); + } + + private T parse(String json, Class type) { + try { + if (json == null || json.isBlank()) + return null; + return om.readValue(json, type); + } catch (Exception e) { + return null; + } + } + + private List parseServiciosIds(String json) { + if (json == null || json.isBlank()) + return new ArrayList<>(); + try { + // 1) intentar como lista de strings + List ids = om.readValue(json, new TypeReference>() { + }); + return ids != null ? ids : new ArrayList<>(); + } catch (Exception ignore) { + } + try { + // 2) intentar como lista de objetos con 'id' + List> list = om.readValue(json, new TypeReference<>() { + }); + List ids = new ArrayList<>(); + for (Map it : list) { + Object id = it.get("id"); + if (id != null) + ids.add(String.valueOf(id)); + } + return ids; + } catch ( + + Exception e) { + return new ArrayList<>(); + } + } + + private T merge(T base, T override) { + // merge muy simple: si override != null, devuélvelo; si no, base + // (si quisieras merge campo a campo, usa BeanUtils o MapStruct) + return override != null ? override : base; + } +} diff --git a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java similarity index 98% rename from src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java rename to src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java index 6a846ef..2a0b6fa 100644 --- a/src/main/java/com/imprimelibros/erp/presupuesto/PresupuestoService.java +++ b/src/main/java/com/imprimelibros/erp/presupuesto/service/PresupuestoService.java @@ -1,4 +1,4 @@ -package com.imprimelibros.erp.presupuesto; +package com.imprimelibros.erp.presupuesto.service; import java.util.ArrayList; import java.util.Arrays; @@ -12,6 +12,7 @@ import java.text.NumberFormat; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.MessageSource; +import org.springframework.security.core.Authentication; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -23,6 +24,8 @@ import java.math.BigDecimal; import java.math.RoundingMode; import com.imprimelibros.erp.configurationERP.VariableService; +import com.imprimelibros.erp.presupuesto.GeoIpService; +import com.imprimelibros.erp.presupuesto.PresupuestoRepository; import com.imprimelibros.erp.presupuesto.classes.ImagenPresupuesto; import com.imprimelibros.erp.presupuesto.classes.PresupuestadorItems; import com.imprimelibros.erp.presupuesto.classes.PresupuestoFormatter; @@ -954,6 +957,21 @@ public class PresupuestoService { return price; } + + public Boolean canAccessPresupuesto(Presupuesto presupuesto, Authentication authentication) { + + boolean isUser = authentication.getAuthorities().stream() + .anyMatch(a -> a.getAuthority().equals("ROLE_USER")); + + if (isUser) { + // Si es usuario, solo puede ver sus propios presupuestos + String username = authentication.getName(); + if (!presupuesto.getUser().getUserName().equals(username)) { + return false; + } + } + return true; + } // ======================================================================= diff --git a/src/main/resources/static/assets/css/presupuestador.css b/src/main/resources/static/assets/css/presupuestador.css index e3cf208..6d6a116 100644 --- a/src/main/resources/static/assets/css/presupuestador.css +++ b/src/main/resources/static/assets/css/presupuestador.css @@ -23,10 +23,6 @@ transition: border 0.3s ease; } -/* === Borde visible cuando está seleccionada === */ -.image-container.selected { - border-color: #92b2a7; -} /* === Imagen interna === */ .image-container img { @@ -37,6 +33,16 @@ transition: transform 0.3s ease; } +/* Borde cuando está seleccionado, sin JS */ +.image-container:has(input:checked) { + border-color: #92b2a7; +} + +/* Si quieres la animación al seleccionar */ +.image-container:has(input:checked) .image-presupuesto { + animation: zoomPop 800ms cubic-bezier(0.68,-0.55,0.27,1.55); +} + /* === Animación de zoom con rebote === */ .image-presupuesto.zoom-anim { animation: zoomPop 800ms cubic-bezier(0.68, -0.55, 0.27, 1.55); diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-presupuesto.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-presupuesto.js index e96eed7..3ac3621 100644 --- a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-presupuesto.js +++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/imagen-presupuesto.js @@ -7,17 +7,17 @@ class imagen_presupuesto { this.group = data.group || ""; // Grupo al que pertenece el radio this.texto = data.texto || ""; // Texto de la etiqueta this.selected = data.selected || false; - this.extraClass = (data.extraClass && !(data.extraClass===undefined))? (data.extraClass + ' ') : ''; + this.extraClass = (data.extraClass && !(data.extraClass === undefined)) ? (data.extraClass + ' ') : ''; this.extraData = data.extra_data || {}; // Datos extra opcionales } render() { - const contenedor = $('
', { + const contenedor = $('