diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..751abe9
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,25 @@
+version: '3.8'
+
+services:
+ imprimelibros-db:
+ image: mysql:8.0
+ container_name: imprimelibros-db
+ restart: unless-stopped
+ environment:
+ MYSQL_ROOT_PASSWORD: NrXz6DK6UoN
+ MYSQL_DATABASE: imprimelibros
+ MYSQL_USER: imprimelibros_user
+ MYSQL_PASSWORD: om91irrDctd
+ ports:
+ - "3306:3306"
+ volumes:
+ - db_data:/var/lib/mysql
+ networks:
+ - imprimelibros-network
+
+volumes:
+ db_data:
+
+networks:
+ imprimelibros-network:
+ driver: bridge
diff --git a/pom.xml b/pom.xml
index 3c5cb5b..9e40c4e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,10 @@
org.springframework.boot
spring-boot-starter-thymeleaf
+
+ org.springframework.boot
+ spring-boot-starter-validation
+
nz.net.ultraq.thymeleaf
thymeleaf-layout-dialect
@@ -47,18 +51,25 @@
org.springframework.boot
spring-boot-starter-web
+
+ jakarta.validation
+ jakarta.validation-api
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
org.thymeleaf.extras
thymeleaf-extras-springsecurity6
-
org.springframework.boot
spring-boot-devtools
runtime
true
-
+
org.springframework.boot
spring-boot-starter-test
diff --git a/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java b/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java
index e678349..b3fb0d8 100644
--- a/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java
+++ b/src/main/java/com/imprimelibros/erp/config/SecurityConfig.java
@@ -4,6 +4,7 @@ import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
@Configuration
public class SecurityConfig {
@@ -11,15 +12,24 @@ public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
- .authorizeHttpRequests(auth -> auth
- .requestMatchers("/", "/assets/**", "/css/**", "/js/**", "/images/**", "/public/**").permitAll()
- .anyRequest().authenticated()
- )
- .formLogin(login -> login
- //.loginPage("/login") añadir cuando se tenga login personalizado
- .permitAll()
- )
- .logout(logout -> logout.permitAll());
+ .authorizeHttpRequests(auth -> auth
+ .requestMatchers(
+ "/",
+ "/assets/**",
+ "/css/**",
+ "/js/**",
+ "/images/**",
+ "/public/**",
+ "/error",
+ "/presupuesto/validar/**")
+ .permitAll()
+ .anyRequest().authenticated())
+ .csrf(csrf -> csrf
+ .ignoringRequestMatchers("/presupuesto/validar/**"))
+ .formLogin(login -> login
+ // .loginPage("/login") añadir cuando se tenga login personalizado
+ .permitAll())
+ .logout(logout -> logout.permitAll());
return http.build();
}
diff --git a/src/main/java/com/imprimelibros/erp/config/validation/ConsistentTiradas.java b/src/main/java/com/imprimelibros/erp/config/validation/ConsistentTiradas.java
new file mode 100644
index 0000000..67009d3
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/config/validation/ConsistentTiradas.java
@@ -0,0 +1,16 @@
+package com.imprimelibros.erp.config.validation;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.*;
+
+@Documented
+@Constraint(validatedBy = ConsistentTiradasValidator.class)
+@Target({ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface ConsistentTiradas {
+ String message() default "Las tiradas deben ser todas mayores o todas menores al valor POD";
+ Class>[] groups() default {};
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/com/imprimelibros/erp/config/validation/ConsistentTiradasValidator.java b/src/main/java/com/imprimelibros/erp/config/validation/ConsistentTiradasValidator.java
new file mode 100644
index 0000000..7224a1c
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/config/validation/ConsistentTiradasValidator.java
@@ -0,0 +1,52 @@
+package com.imprimelibros.erp.config.validation;
+
+import com.imprimelibros.erp.entity.Presupuesto;
+import com.imprimelibros.erp.service.VariableService;
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.context.i18n.LocaleContextHolder;
+
+public class ConsistentTiradasValidator implements ConstraintValidator {
+
+ @Autowired
+ private VariableService variableService;
+ @Autowired
+ private MessageSource messageSource;
+
+ @Override
+ public boolean isValid(Presupuesto presupuesto, ConstraintValidatorContext context) {
+ if (presupuesto == null)
+ return true;
+
+ Integer[] tiradas = presupuesto.getTiradas();
+ Integer podValue = variableService.getValorEntero("POD");
+
+ boolean allAbove = true;
+ boolean allBelow = true;
+
+ for (Integer t : tiradas) {
+ if (t == null)
+ continue;
+ if (t <= podValue)
+ allAbove = false;
+ if (t >= podValue)
+ allBelow = false;
+ }
+
+ if (!(allAbove || allBelow)) {
+
+ String mensajeInterpolado = messageSource.getMessage(
+ "presupuesto.errores.tiradas.consistentes", // clave del mensaje
+ new Object[] { podValue }, // parámetros para {0}
+ LocaleContextHolder.getLocale() // respeta el idioma actual
+ );
+ context.disableDefaultConstraintViolation();
+ context.buildConstraintViolationWithTemplate(mensajeInterpolado)
+ .addConstraintViolation();
+ return false;
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/com/imprimelibros/erp/config/validation/Par.java b/src/main/java/com/imprimelibros/erp/config/validation/Par.java
new file mode 100644
index 0000000..7ce1400
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/config/validation/Par.java
@@ -0,0 +1,16 @@
+package com.imprimelibros.erp.config.validation;
+
+import jakarta.validation.Constraint;
+import jakarta.validation.Payload;
+
+import java.lang.annotation.*;
+
+@Documented
+@Constraint(validatedBy = ParValidator.class)
+@Target({ ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Par {
+ String message() default "El valor debe ser un número par";
+ Class>[] groups() default {};
+ Class extends Payload>[] payload() default {};
+}
diff --git a/src/main/java/com/imprimelibros/erp/config/validation/ParValidator.java b/src/main/java/com/imprimelibros/erp/config/validation/ParValidator.java
new file mode 100644
index 0000000..84b722e
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/config/validation/ParValidator.java
@@ -0,0 +1,14 @@
+package com.imprimelibros.erp.config.validation;
+
+import jakarta.validation.ConstraintValidator;
+import jakarta.validation.ConstraintValidatorContext;
+
+public class ParValidator implements ConstraintValidator {
+
+ @Override
+ public boolean isValid(Integer value, ConstraintValidatorContext context) {
+ if (value == null) return true; // se permite null, usa @NotNull aparte si lo necesitas
+ return value % 2 == 0;
+ }
+}
+
diff --git a/src/main/java/com/imprimelibros/erp/config/validation/PresupuestoValidationGroups.java b/src/main/java/com/imprimelibros/erp/config/validation/PresupuestoValidationGroups.java
new file mode 100644
index 0000000..75c6be1
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/config/validation/PresupuestoValidationGroups.java
@@ -0,0 +1,9 @@
+package com.imprimelibros.erp.config.validation;
+
+public class PresupuestoValidationGroups {
+
+ public interface DatosGenerales {}
+ public interface Interior {}
+ public interface Cubierta {}
+
+}
diff --git a/src/main/java/com/imprimelibros/erp/controller/HomeController.java b/src/main/java/com/imprimelibros/erp/controller/HomeController.java
index 9d4a9e1..92d57e0 100644
--- a/src/main/java/com/imprimelibros/erp/controller/HomeController.java
+++ b/src/main/java/com/imprimelibros/erp/controller/HomeController.java
@@ -1,24 +1,46 @@
package com.imprimelibros.erp.controller;
import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.context.MessageSource;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
+import com.imprimelibros.erp.i18n.TranslationService;
+import com.imprimelibros.erp.service.VariableService;
+
+import org.springframework.security.authentication.AnonymousAuthenticationToken;
+import org.springframework.security.core.Authentication;
+
+import java.util.List;
import java.util.Locale;
+import java.util.Map;
@Controller
public class HomeController {
@Autowired
- private MessageSource messageSource;
+ private TranslationService translationService;
+ @Autowired
+ private VariableService variableService;
@GetMapping("/")
- public String index(Model model, Locale locale) {
- //model.addAttribute("title", messageSource.getMessage("t-home", null, locale));
- model.addAttribute("title", "Inicio");
- return "imprimelibros/home";
+ public String index(Model model, Authentication authentication, Locale locale) {
+
+ boolean isAuthenticated = authentication != null && authentication.isAuthenticated()
+ && !(authentication instanceof AnonymousAuthenticationToken);
+
+ if (!isAuthenticated) {
+
+ List keys = List.of(
+ "presupuesto.plantilla-cubierta",
+ "presupuesto.plantilla-cubierta-text",
+ "presupuesto.impresion-cubierta",
+ "presupuesto.impresion-cubierta-help");
+
+ Map translations = translationService.getTranslations(locale, keys);
+ model.addAttribute("languageBundle", translations);
+ model.addAttribute("pod", variableService.getValorEntero("POD"));
+ }
+ return "imprimelibros/home";
}
}
-
diff --git a/src/main/java/com/imprimelibros/erp/controller/PresupuestoController.java b/src/main/java/com/imprimelibros/erp/controller/PresupuestoController.java
new file mode 100644
index 0000000..4e5ccc7
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/controller/PresupuestoController.java
@@ -0,0 +1,53 @@
+package com.imprimelibros.erp.controller;
+
+import org.springframework.web.bind.annotation.RestController;
+
+import com.imprimelibros.erp.service.PresupuestoService;
+
+import java.util.HashMap;
+
+import org.springframework.beans.factory.annotation.Autowired;
+
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.BindingResult;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+
+import jakarta.validation.Valid; // o javax.validation.Valid según tu versión
+
+import java.util.Map;
+
+import com.imprimelibros.erp.config.validation.PresupuestoValidationGroups;
+import com.imprimelibros.erp.entity.Presupuesto;
+
+@RestController
+@RequestMapping("/presupuesto")
+public class PresupuestoController {
+
+ @Autowired
+ protected PresupuestoService presupuestoService;
+
+ @PostMapping("/validar/datos-generales")
+ public ResponseEntity> validarDatosGenerales(
+ @Validated(PresupuestoValidationGroups.DatosGenerales.class) Presupuesto presupuesto,
+ BindingResult result) {
+
+ Map errores = new HashMap<>();
+
+ // errores de campos individuales
+ result.getFieldErrors().forEach(error -> errores.put(error.getField(), error.getDefaultMessage()));
+
+ // errores globales (como tu @ConsistentTiradas)
+ result.getGlobalErrors().forEach(error -> errores.put("global", error.getDefaultMessage()));
+
+ if (!errores.isEmpty()) {
+ return ResponseEntity.badRequest().body(errores);
+ }
+
+ return ResponseEntity.ok().build();
+ }
+
+}
diff --git a/src/main/java/com/imprimelibros/erp/entity/Presupuesto.java b/src/main/java/com/imprimelibros/erp/entity/Presupuesto.java
new file mode 100644
index 0000000..933438c
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/entity/Presupuesto.java
@@ -0,0 +1,153 @@
+package com.imprimelibros.erp.entity;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.persistence.*;
+
+import com.imprimelibros.erp.config.validation.PresupuestoValidationGroups;
+import com.imprimelibros.erp.config.validation.ConsistentTiradas;
+import com.imprimelibros.erp.config.validation.Par;
+
+
+@ConsistentTiradas(groups = PresupuestoValidationGroups.DatosGenerales.class)
+@Entity
+@Table(name = "presupuesto")
+public class Presupuesto {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ // Otros campos del presupuesto
+ @NotBlank(message = "{presupuesto.errores.titulo}", groups = PresupuestoValidationGroups.DatosGenerales.class)
+ @Column(name = "titulo")
+ private String titulo;
+
+ @Column(name = "autor")
+ private String autor;
+
+ @Column(name = "isbn")
+ private String isbn;
+
+ @NotNull(message = "{presupuesto.errores.tirada1}", groups = PresupuestoValidationGroups.DatosGenerales.class)
+ @Column(name = "tirada1")
+ private Integer tirada1;
+
+ @Column(name = "tirada2")
+ private Integer tirada2;
+
+ @Column(name = "tirada3")
+ private Integer tirada3;
+
+ @Column(name = "tirada4")
+ private Integer tirada4;
+
+ @Par(message = "{presupuesto.errores.paginasNegro.par}", groups = PresupuestoValidationGroups.DatosGenerales.class)
+ @NotNull(message = "{presupuesto.errores.paginasNegro.required}", groups = PresupuestoValidationGroups.DatosGenerales.class)
+ @Column(name = "paginas_negro")
+ private Integer paginasNegro;
+
+ @Par(message = "{presupuesto.errores.paginasColor.par}", groups = PresupuestoValidationGroups.DatosGenerales.class)
+ @NotNull(message = "{presupuesto.errores.paginasColor.required}", groups = PresupuestoValidationGroups.DatosGenerales.class)
+ @Column(name = "paginas_color")
+ private Integer paginasColor;
+
+ @NotNull(message = "El papel interior no puede estar vacío", groups = PresupuestoValidationGroups.Interior.class)
+ @Column(name = "papel_interior")
+ private Integer papelInterior;
+
+
+ // Getters y Setters
+
+ public String getAutor() {
+ return autor;
+ }
+
+ public void setAutor(String autor) {
+ this.autor = autor;
+ }
+
+ public String getIsbn() {
+ return isbn;
+ }
+
+ public void setIsbn(String isbn) {
+ this.isbn = isbn;
+ }
+
+ public Integer getTirada1() {
+ return tirada1;
+ }
+
+ public void setTirada1(Integer tirada1) {
+ this.tirada1 = tirada1;
+ }
+
+ public Integer getTirada2() {
+ return tirada2;
+ }
+
+ public void setTirada2(Integer tirada2) {
+ this.tirada2 = tirada2;
+ }
+
+ public Integer getTirada3() {
+ return tirada3;
+ }
+
+ public void setTirada3(Integer tirada3) {
+ this.tirada3 = tirada3;
+ }
+
+ public Integer getTirada4() {
+ return tirada4;
+ }
+
+ public Integer[] getTiradas() {
+ return new Integer[]{tirada1, tirada2, tirada3, tirada4};
+ }
+
+ public void setTirada4(Integer tirada4) {
+ this.tirada4 = tirada4;
+ }
+
+ public String getTitulo() {
+ return titulo;
+ }
+
+ public void setTitulo(String titulo) {
+ this.titulo = titulo;
+ }
+
+ public Long getId() {
+ return id;
+ }
+
+ public void setId(Long id) {
+ this.id = id;
+ }
+
+ public Integer getPaginasNegro() {
+ return paginasNegro;
+ }
+
+ public void setPaginasNegro(Integer paginasNegro) {
+ this.paginasNegro = paginasNegro;
+ }
+
+ public Integer getPaginasColor() {
+ return paginasColor;
+ }
+
+ public void setPaginasColor(Integer paginasColor) {
+ this.paginasColor = paginasColor;
+ }
+
+ public Integer getPapelInterior() {
+ return papelInterior;
+ }
+
+ public void setPapelInterior(Integer papelInterior) {
+ this.papelInterior = papelInterior;
+ }
+}
diff --git a/src/main/java/com/imprimelibros/erp/entity/Variable.java b/src/main/java/com/imprimelibros/erp/entity/Variable.java
new file mode 100644
index 0000000..7c908ce
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/entity/Variable.java
@@ -0,0 +1,30 @@
+package com.imprimelibros.erp.entity;
+
+import jakarta.persistence.*;
+
+@Entity
+@Table(name = "variables")
+public class Variable {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+
+ private String clave;
+
+ private String valor;
+
+ // Getters y setters
+
+ public Long getId() { return id; }
+
+ public void setId(Long id) { this.id = id; }
+
+ public String getClave() { return clave; }
+
+ public void setClave(String clave) { this.clave = clave; }
+
+ public String getValor() { return valor; }
+
+ public void setValor(String valor) { this.valor = valor; }
+}
diff --git a/src/main/java/com/imprimelibros/erp/i18n/TranslationService.java b/src/main/java/com/imprimelibros/erp/i18n/TranslationService.java
new file mode 100644
index 0000000..65f2cc6
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/i18n/TranslationService.java
@@ -0,0 +1,22 @@
+package com.imprimelibros.erp.i18n;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.MessageSource;
+import org.springframework.stereotype.Component;
+
+import java.util.*;
+
+@Component
+public class TranslationService {
+
+ @Autowired
+ private MessageSource messageSource;
+
+ public Map getTranslations(Locale locale, List keys) {
+ Map translations = new HashMap<>();
+ for (String key : keys) {
+ translations.put(key, messageSource.getMessage(key, null, locale));
+ }
+ return translations;
+ }
+}
diff --git a/src/main/java/com/imprimelibros/erp/repository/VariableRepository.java b/src/main/java/com/imprimelibros/erp/repository/VariableRepository.java
new file mode 100644
index 0000000..141642d
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/repository/VariableRepository.java
@@ -0,0 +1,10 @@
+package com.imprimelibros.erp.repository;
+
+import com.imprimelibros.erp.entity.Variable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.Optional;
+
+public interface VariableRepository extends JpaRepository {
+ Optional findByClave(String clave);
+}
diff --git a/src/main/java/com/imprimelibros/erp/service/PresupuestoService.java b/src/main/java/com/imprimelibros/erp/service/PresupuestoService.java
new file mode 100644
index 0000000..5e42aed
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/service/PresupuestoService.java
@@ -0,0 +1,18 @@
+package com.imprimelibros.erp.service;
+
+import org.springframework.stereotype.Service;
+
+@Service
+public class PresupuestoService {
+
+ public boolean validateDatosGenerales(int[] tiradas) {
+ // Implement the logic to validate the tiradas
+ // For example, check if all tiradas are positive integers
+ for (int tirada : tiradas) {
+ if (tirada <= 0) {
+ return false; // Invalid tirada found
+ }
+ }
+ return true; // All tiradas are valid
+ }
+}
diff --git a/src/main/java/com/imprimelibros/erp/service/VariableService.java b/src/main/java/com/imprimelibros/erp/service/VariableService.java
new file mode 100644
index 0000000..255fe1c
--- /dev/null
+++ b/src/main/java/com/imprimelibros/erp/service/VariableService.java
@@ -0,0 +1,20 @@
+package com.imprimelibros.erp.service;
+
+import com.imprimelibros.erp.repository.VariableRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+@Service
+public class VariableService {
+
+ @Autowired
+ private VariableRepository variableRepository;
+
+ public Integer getValorEntero(String clave) {
+ return variableRepository.findByClave(clave)
+ .map(v -> Integer.parseInt(v.getValor()))
+ .orElseThrow(
+ () -> new IllegalArgumentException("No se encontró la variable con clave '" + clave + "'"));
+ }
+
+}
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 34b0f12..4addc83 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -1,2 +1,13 @@
spring.application.name=erp
logging.level.org.springframework.security=DEBUG
+
+logging.level.root=WARN
+logging.level.org.springframework=ERROR
+
+spring.datasource.url=jdbc:mysql://localhost:3306/imprimelibros
+spring.datasource.username=imprimelibros_user
+spring.datasource.password=om91irrDctd
+spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
+
+spring.jpa.hibernate.ddl-auto=update
+spring.jpa.show-sql=true
\ No newline at end of file
diff --git a/src/main/resources/i18n/presupuesto_es.properties b/src/main/resources/i18n/presupuesto_es.properties
index 321b178..83d2430 100644
--- a/src/main/resources/i18n/presupuesto_es.properties
+++ b/src/main/resources/i18n/presupuesto_es.properties
@@ -1,7 +1,7 @@
presupuesto.datos-generales=Datos Generales
presupuesto.interior=Interior
presupuesto.cubierta=Cubierta
-presupuesto.envio=Envío
+presupuesto.extras=Extras
# Pestaña datos generales de presupuesto
presupuesto.informacion-libro=Información del libro
@@ -13,7 +13,7 @@ presupuesto.tirada1=Tirada 1*
presupuesto.tirada2=Tirada 2
presupuesto.tirada3=Tirada 3
presupuesto.tirada4=Tirada 4
-presupuesto.tiradasPODnoPOD=No puede mezclar tiradas menores de 30 unidades con mayores de 30 unidades
+presupuesto.tiradasPODnoPOD=No puede mezclar tiradas menores de {0} unidades con mayores de {0} unidades
presupuesto.formato=Formato
presupuesto.orientacion.vertical=Vertical
presupuesto.orientacion.apaisado=Apaisado
@@ -26,5 +26,70 @@ presupuesto.paginas-negro=Páginas Negro
presupuesto.paginas-color=Páginas Color
presupuesto.siempre-pares=Siempre deben ser pares
presupuesto.encuadernacion=Encuadernación
+presupuesto.fresado-descripcion=Fresado (a partir de 32 páginas)
+presupuesto.cosido-descripcion=Cosido (a partir de 32 páginas)
+presupuesto.grapado-descripcion=Grapado (entre 12 y 40 páginas)
+presupuesto.espiral-descripcion=Espiral (a partir de 20 páginas)
+presupuesto.wire-o-descripcion=Wire-O (a partir de 20 páginas)
presupuesto.encuadernacion-descripcion=Seleccione la encuadernación del libro
-presupuesto.siguiente=Siguiente
\ No newline at end of file
+presupuesto.continuar-interior=Continuar a diseño interior
+
+# Pestaña interior de presupuesto
+presupuesto.tipo-impresion=Tipo de impresión
+presupuesto.tipo-impresion-descripcion=Seleccione entre calidad estándar o premium
+presupuesto.papel-interior=Papel interior
+presupuesto.papel-interior-descripcion=Seleccione el papel para el interior
+presupuesto.gramaje-interior=Gramaje interior
+presupuesto.gramaje-interior-descripcion=Seleccione el gramaje para el interior
+presupuesto.blanco-negro=Blanco y negro
+presupuesto.blanco-negro-premium= Blanco y negro Premium
+presupuesto.color=Color
+presupuesto.color-premium=Color Premium
+presupuesto.offset-blanco=Offset Blanco
+presupuesto.offset-ahuesado=Offset Ahuesado
+presupuesto.offset-ahuesado-volumen=Offset Ahuesado Volumen
+presupuesto.estucado-mate=Estucado Mate
+presupuesto.volver-datos-generales=Volver a datos generales
+presupuesto.continuar-cubierta=Continuar a diseño cubierta
+
+# Pestaña cubierta
+presupuesto.plantilla-cubierta=Plantilla de cubierta
+presupuesto.plantilla-cubierta-text=Recuerde que la cubierta es el conjunto formado por la portada, contraportada, lomo y solapas, en caso de que las lleve.
Si tiene dudas de las medidas puede solicitarnos una plantilla cuando haga el pedido.
+presupuesto.tipo-cubierta=Tipo de cubierta
+presupuesto.tipo-cubierta-descripcion=Seleccione el tipo de cubierta y sus opciones
+presupuesto.tapa-blanda=Tapa blanda
+presupuesto.tapa-dura=Tapa dura
+presupuesto.tapa-dura-lomo-redondo=Tapa dura lomo redondo
+presupuesto.sin-solapas=Sin solapas
+presupuesto.con-solapas=Con solapas
+presupuesto.impresion-cubierta=Impresión de cubierta
+presupuesto.impresion-cubierta-help=La cubierta se puede imprimir por anverso y reverso, como en el caso de las revistas, pero para un libro normal con portada y contraportada, la impresión de cubierta es a una cara.
+presupuesto.una-cara=Una cara
+presupuesto.dos-caras=Dos caras
+presupuesto.tamanio-solapa=Tamaño solapa
+presupuesto.papel-guardas=Papel de guardas
+presupuesto.guardas-impresas=Guardas impresas
+presupuesto.no=No
+presupuesto.cabezada=Cabezada
+presupuesto.cabezada-blanca=Blanca
+presupuesto.cabezada-verde=Verde
+presupuesto.cabezada-azul=Azul
+presupuesto.cabezada-roja-amarilla=Roja-Amarilla
+presupuesto.papel-cubierta=Papel cubierta
+presupuesto.papel-cubierta-descripcion=Seleccione el papel para la cubierta
+presupuesto.cartulina-grafica-cubierta=Cartulina gráfica estucada a una cara
+presupuesto.gramaje-cubierta=Gramaje cubierta
+presupuesto.gramaje-cubierta-descripcion=Seleccione el gramaje para la cubierta
+presupuesto.volver-interior=Volver a diseño interior
+presupuesto.continuar-extras-libro=Continuar a extras del libro
+
+
+# Errores
+presupuesto.errores-title=Corrija los siguientes errores:
+presupuesto.errores.titulo=El título es obligatorio
+presupuesto.errores.tirada1=La tirada 1 es obligatoria
+presupuesto.errores.tiradas.consistentes=Las tiradas deben ser consistentes (no mezclar tiradas menores de {0} unidades con mayores de {0} unidades)
+presupuesto.errores.paginasNegro.required=El número de páginas en negro es obligatorio
+presupuesto.errores.paginasNegro.par=El número de páginas en negro debe ser par
+presupuesto.errores.paginasColor.required=El número de páginas en color es obligatorio
+presupuesto.errores.paginasColor.par=El número de páginas en color debe ser par
\ No newline at end of file
diff --git a/src/main/resources/static/assets/css/presupuestador.css b/src/main/resources/static/assets/css/presupuestador.css
index 051cc64..6f6c43e 100644
--- a/src/main/resources/static/assets/css/presupuestador.css
+++ b/src/main/resources/static/assets/css/presupuestador.css
@@ -18,6 +18,8 @@
border-radius: 8px;
padding: 8px;
+ margin-inline: 5px;
+
transition: border 0.3s ease;
}
@@ -58,3 +60,14 @@
max-width: 100%;
}
}
+
+.gramaje-radio{
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ min-width: 70px; /* Ancho mínimo */
+ min-height: 70px; /* Alto mínimo */
+ max-width: 70px; /* Ancho máximo */
+ max-height: 70px; /* Alto máximo */
+}
\ No newline at end of file
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/blancoYnegro.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/blancoYnegro.png
new file mode 100644
index 0000000..459b3cb
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/blancoYnegro.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/cartulina-grafica.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/cartulina-grafica.png
new file mode 100644
index 0000000..403b40d
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/cartulina-grafica.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/color.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/color.png
new file mode 100644
index 0000000..cd80dd7
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/color.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/colorFoto.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/colorFoto.png
new file mode 100644
index 0000000..86f0573
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/colorFoto.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/conSolapasCubierta.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/conSolapasCubierta.png
new file mode 100644
index 0000000..6ad3d93
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/conSolapasCubierta.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/estucado-mate-cubierta.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/estucado-mate-cubierta.png
new file mode 100644
index 0000000..7178e36
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/estucado-mate-cubierta.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/estucado-mate.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/estucado-mate.png
new file mode 100644
index 0000000..77d6136
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/estucado-mate.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/negroFoto.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/negroFoto.png
new file mode 100644
index 0000000..f2bc8d8
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/negroFoto.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-ahuesado-volumen.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-ahuesado-volumen.png
new file mode 100644
index 0000000..297c607
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-ahuesado-volumen.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-ahuesado.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-ahuesado.png
new file mode 100644
index 0000000..3c0f124
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-ahuesado.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-blanco.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-blanco.png
new file mode 100644
index 0000000..29dc029
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/offset-blanco.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/plantilla-cubierta.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/plantilla-cubierta.png
new file mode 100644
index 0000000..8cb3255
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/plantilla-cubierta.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/sinSolapasCubierta.png b/src/main/resources/static/assets/images/imprimelibros/presupuestador/sinSolapasCubierta.png
new file mode 100644
index 0000000..c09917c
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/sinSolapasCubierta.png differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-blanda.jpg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-blanda.jpg
new file mode 100644
index 0000000..685af27
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-blanda.jpg differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-dura-lomo-recto.jpg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-dura-lomo-recto.jpg
new file mode 100644
index 0000000..153bfa8
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-dura-lomo-recto.jpg differ
diff --git a/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-dura-lomo-redondo.jpg b/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-dura-lomo-redondo.jpg
new file mode 100644
index 0000000..f0dd640
Binary files /dev/null and b/src/main/resources/static/assets/images/imprimelibros/presupuestador/tapa-dura-lomo-redondo.jpg differ
diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/languageBundle.js b/src/main/resources/static/assets/js/pages/imprimelibros/languageBundle.js
new file mode 100644
index 0000000..a0f699d
--- /dev/null
+++ b/src/main/resources/static/assets/js/pages/imprimelibros/languageBundle.js
@@ -0,0 +1,7 @@
+window.languageBundle.get = function (key, ...params) {
+ let text = this[key] || key;
+ params.forEach((val, i) => {
+ text = text.replace(`{${i}}`, val);
+ });
+ return text;
+};
diff --git a/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js
new file mode 100644
index 0000000..4cf13dc
--- /dev/null
+++ b/src/main/resources/static/assets/js/pages/imprimelibros/presupuestador/presupuestador.js
@@ -0,0 +1,111 @@
+class PresupuestoCliente {
+
+ constructor() {
+
+ // pestaña datos generales
+ this.titulo = $('#titulo');
+ this.autor = $('#autor');
+ this.isbn = $('#isbn');
+ this.tirada1 = $('#tirada1');
+ this.tirada2 = $('#tirada2');
+ this.tirada3 = $('#tirada3');
+ this.tirada4 = $('#tirada4');
+ this.paginasNegro = $('#paginas-negro');
+ this.paginasColor = $('#paginas-color')
+ this.btn_next_datos_generales = $('#next-datos-generales');
+ this.datos_generales_alert = $('#datos-generales-alert');
+
+ // pestaña cubierta
+ this.btn_plantilla_cubierta = $('#btn-plantilla-cubierta');
+ this.btn_impresion_cubierta_help = $('#impresion-cubierta-help');
+
+
+
+ }
+
+ init() {
+
+ this.initButtonsEventListeners();
+ }
+
+ initButtonsEventListeners() {
+
+ this.btn_plantilla_cubierta.on('click', () => {
+ Swal.fire({
+ position: 'top-end',
+ icon: 'info',
+ title: window.languageBundle.get('presupuesto.plantilla-cubierta'),
+ html: `
+
+

+
+
+ ${window.languageBundle.get('presupuesto.plantilla-cubierta-text')}
+
+ `,
+ confirmButtonClass: 'btn btn-primary w-xs mt-2',
+ showConfirmButton: false,
+ showCloseButton: true
+ });
+ });
+
+ this.btn_impresion_cubierta_help.on('click', () => {
+ Swal.fire({
+ position: 'top-end',
+ icon: 'info',
+ title: window.languageBundle.get('presupuesto.impresion-cubierta'),
+ html: window.languageBundle.get('presupuesto.impresion-cubierta-help'),
+ confirmButtonClass: 'btn btn-primary w-xs mt-2',
+ showConfirmButton: false,
+ showCloseButton: true
+ });
+ });
+
+ this.btn_next_datos_generales.on('click', () => {
+ this.nextDatosGenerales();
+ });
+
+ }
+
+ nextDatosGenerales() {
+ const data = {
+ titulo: this.titulo.val(),
+ autor: this.autor.val(),
+ isbn: this.isbn.val(),
+ tirada1: this.tirada1.val(),
+ tirada2: this.tirada2.val(),
+ tirada3: this.tirada3.val(),
+ tirada4: this.tirada4.val(),
+ paginasNegro: this.paginasNegro.val(),
+ paginasColor: this.paginasColor.val()
+ }
+ $.ajax({
+ url: '/presupuesto/validar/datos-generales',
+ type: 'POST',
+ data: data,
+ success: (response) => {
+ this.datos_generales_alert.addClass('d-none');
+ },
+ error: (xhr, status, error) => {
+ this.datos_generales_alert.removeClass('d-none');
+ this.datos_generales_alert.find('#datos-generales-alert-list').empty();
+ const errors = xhr.responseJSON;
+ if (errors && typeof errors === 'object') {
+ Object.values(errors).forEach(errorMsg => {
+ this.datos_generales_alert.find('#datos-generales-alert-list').append(`${errorMsg}`);
+ });
+ } else {
+ this.datos_generales_alert.find('#datos-generales-alert-list').append('Error desconocido. Por favor, inténtelo de nuevo más tarde.');
+ }
+ $(window).scrollTop(0);
+ }
+ });
+ }
+
+}
+
+
+document.addEventListener('DOMContentLoaded', function () {
+ const presupuestoCliente = new PresupuestoCliente();
+ presupuestoCliente.init();
+});
\ No newline at end of file
diff --git a/src/main/resources/static/assets/js/pages/sweetalerts.init.js b/src/main/resources/static/assets/js/pages/sweetalerts.init.js
index 00a286a..e91cec0 100644
--- a/src/main/resources/static/assets/js/pages/sweetalerts.init.js
+++ b/src/main/resources/static/assets/js/pages/sweetalerts.init.js
@@ -247,7 +247,6 @@ if (document.getElementById("sa-position"))
icon: 'success',
title: 'Your work has been saved',
showConfirmButton: false,
- timer: 1500,
showCloseButton: true
})
});
diff --git a/src/main/resources/templates/imprimelibros/home.html b/src/main/resources/templates/imprimelibros/home.html
index 6dbf065..7591e78 100644
--- a/src/main/resources/templates/imprimelibros/home.html
+++ b/src/main/resources/templates/imprimelibros/home.html
@@ -35,7 +35,11 @@
+
+
diff --git a/src/main/resources/templates/imprimelibros/layout.html b/src/main/resources/templates/imprimelibros/layout.html
index 9fcdf28..ebdbc4a 100644
--- a/src/main/resources/templates/imprimelibros/layout.html
+++ b/src/main/resources/templates/imprimelibros/layout.html
@@ -1,18 +1,13 @@
-
+