trabajando en resumen

This commit is contained in:
Jaime Jiménez
2025-08-24 21:02:42 +02:00
parent baf4cb6ae5
commit 6b883ffab2
18 changed files with 1659 additions and 226 deletions

View File

@ -7,8 +7,8 @@
text-align: center;
/* Tamaño adaptable */
width: 100%;
max-width: 200px;
width: 100%;
max-width: 200px;
/* Para evitar que la imagen sobresalga al hacer zoom */
overflow: hidden;
@ -30,7 +30,7 @@
/* === Imagen interna === */
.image-container img {
max-width: 100%;
max-width: 100%;
max-height: 150px;
display: block;
transform-origin: center center;
@ -44,10 +44,21 @@
/* Keyframes para la animación */
@keyframes zoomPop {
0% { transform: scale(1); }
40% { transform: scale(0.85); }
80% { transform: scale(1.05); }
100% { transform: scale(1); }
0% {
transform: scale(1);
}
40% {
transform: scale(0.85);
}
80% {
transform: scale(1.05);
}
100% {
transform: scale(1);
}
}
.image-container:hover {
@ -61,15 +72,19 @@
}
}
.gramaje-radio{
.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 */
min-width: 70px;
/* Ancho mínimo */
min-height: 70px;
/* Alto mínimo */
max-width: 70px;
/* Ancho máximo */
max-height: 70px;
/* Alto máximo */
}
@keyframes fadeInUpBounce {
@ -77,13 +92,16 @@
opacity: 0;
transform: translateY(30px);
}
60% {
opacity: 1;
transform: translateY(-10px);
}
80% {
transform: translateY(5px);
}
100% {
transform: translateY(0);
}
@ -93,4 +111,240 @@
animation: fadeInUpBounce 0.6s ease-out both;
animation-delay: 0.1s;
will-change: transform;
}
.service-option {
cursor: pointer;
transition: all 0.3s ease-in-out;
border: 1px solid #d0d7e5;
border-radius: 0.5rem;
background-color: #fff;
}
.service-option.checked {
border-color: #4b7bec;
background-color: #f0f5ff;
box-shadow: 0 0 0 2px #4b7bec inset;
}
.service-option .price {
font-weight: 600;
font-size: 1rem;
margin-top: 1rem;
}
.service-desc {
min-height: 2.2rem;
/* ajusta según el tamaño de fuente que uses */
display: flex;
align-items: center;
justify-content: center;
}
.service-checkbox {
display: none;
}
.btn-check-service+.btn {
position: relative;
/* <-- Esto es lo más importante */
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
white-space: normal;
line-height: 1.2;
min-height: 180px;
padding: 1rem 0.75rem;
border-radius: 0.5rem;
color: #4b7bec;
border: 1px solid #4b7bec;
transition: all 0.3s ease-in-out;
}
/* ESTILO CUANDO ESTÁ MARCADO */
.btn-check-service:checked+.btn {
background-color: #4b7bec;
color: #ffffff !important;
border-color: #4b7bec;
}
/* Forzamos el color blanco en los subelementos */
.btn-check-service:checked+.btn .service-title,
.btn-check-service:checked+.btn .service-desc,
.btn-check-service:checked+.btn .service-price {
color: #ffffff !important;
}
/* Forzamos el color azul cuando no está marcado */
.btn-check-service+.btn .service-title,
.btn-check-service+.btn .service-desc,
.btn-check-service+.btn .service-price {
color: #4b7bec;
}
/* ribbon-service */
.ribbon-service {
position: absolute;
top: -5px;
right: -5px;
overflow: hidden;
width: 90px;
height: 90px;
z-index: 1;
}
.ribbon-service span {
position: absolute;
display: block;
width: 120px;
padding: 5px 0;
background: #f25c5c;
color: white;
font-size: 12px;
text-align: center;
font-weight: bold;
transform: rotate(45deg);
top: 20px;
right: -30px;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.2);
}
/* ===== Tiradas (pricing cards) ===== */
.tirada-card {
--il-accent: #4b7bec;
--radius: 18px;
--sel-scale-y: 1.12;
/* cuánto más alta la seleccionada (crece arriba y abajo) */
/* círculo */
--arc-w: 280px;
--arc-h: 190px;
--arc-y: -23px;
/* tu valor */
--arc-stop: 74%;
border: 1px solid #e9ecef;
border-radius: var(--radius);
background-color: #fff;
background-image: radial-gradient(var(--arc-w) var(--arc-h) at 50% var(--arc-y),
rgba(75, 126, 236, .24) 0 var(--arc-stop),
transparent calc(var(--arc-stop) + 1%));
padding: 1.25rem 1.25rem 1rem;
text-align: center;
position: relative;
height: 100%;
box-shadow: 0 4px 14px rgba(17, 24, 39, .06);
overflow: hidden;
/* integra la línea inferior */
transform-origin: center center;
/* ⟵ crecer hacia arriba y hacia abajo */
will-change: transform;
transition: transform .22s ease, box-shadow .22s ease, border-color .22s ease, background .22s ease;
}
/* sin elevación al hover */
.tirada-card:hover {
transform: none;
}
/* Tipografías */
.tirada-card .title {
font-weight: 700;
letter-spacing: .04em;
font-size: .9rem;
color: #4b7bec;
text-transform: uppercase;
}
.tirada-card .price-big {
font-size: 2rem;
font-weight: 800;
line-height: 1.1;
margin: .35rem 0 .15rem;
color: #4b7bec;
}
.tirada-card .per {
font-size: .85rem;
color: #7f8fa9;
}
.tirada-card .price-row {
margin-top: .75rem;
}
.tirada-card .muted {
color: #8e9bb3;
font-size: .9rem;
}
/* Botón */
.tirada-card .btn-select-tirada {
margin-top: 1rem;
border-radius: 999px;
padding: .6rem 1rem;
font-weight: 600;
border: 2px solid var(--il-accent);
color: var(--il-accent);
background: transparent;
width: 100%;
transition: background .2s ease, color .2s ease;
}
/* ===== Seleccionada (más alta en Y, sin elevar; crece arriba y abajo) ===== */
.tirada-card.selected {
transform: scaleY(var(--sel-scale-y));
/* ⟵ crecimiento simétrico vertical */
z-index: 2;
box-shadow: 0 18px 48px rgba(75, 126, 236, .32);
border-color: var(--il-accent);
/* círculo sólido */
background-image: radial-gradient(var(--arc-w) var(--arc-h) at 50% var(--arc-y),
#4b7bec 0 var(--arc-stop),
transparent calc(var(--arc-stop) + 1%));
}
.tirada-card.selected:hover {
/* evita que :hover base anule la escala */
transform: scaleY(var(--sel-scale-y));
}
.tirada-card.selected .btn-select-tirada {
background: var(--il-accent);
color: #fff;
}
/* texto en blanco dentro del círculo */
.tirada-card.selected .title,
.tirada-card.selected .price-big,
.tirada-card.selected .per {
color: #fff;
}
/* ===== Línea inferior integrada (siempre visible en NO seleccionadas) ===== */
.tirada-card:not(.selected)::after {
content: "";
position: absolute;
left: 1px;
right: 1px;
bottom: 1px;
/* dentro del borde */
height: 8px;
background: var(--il-accent);
border-bottom-left-radius: calc(var(--radius) - 1px);
border-bottom-right-radius: calc(var(--radius) - 1px);
pointer-events: none;
}
.tirada-card.selected::after {
content: none;
}
/* Oculta el radio */
.tirada-card input[type="radio"] {
display: none;
}

View File

@ -1,4 +1,6 @@
import imagen_presupuesto from "./imagen-presupuesto.js";
import ServiceOptionCard from "./service-option-card.js";
import TiradaCard from "./tirada-price-card.js";
class PresupuestoCliente {
@ -39,8 +41,24 @@ class PresupuestoCliente {
cabezada: 'WHI',
papelCubiertaId: 3,
gramajeCubierta: 170,
acabado: 1
}
acabado: 1,
sobrecubierta: {
activo: false,
papelSobrecubiertaId: 2,
gramajeSobrecubierta: 170,
tamanioSolapasSobrecubierta: 80,
acabado: 0
},
faja: {
activo: false,
papelFajaId: 2,
gramajeFaja: 170,
alto: 50,
tamanioSolapasFaja: 80,
acabado: 0
}
},
selectedTirada: null,
}
// pestaña datos generales
@ -85,13 +103,44 @@ class PresupuestoCliente {
this.guardasImpresas = $('#guardas-impresas');
this.cabezada = $('#cabezada');
this.divAcabadoCubierta = $('#div-acabado-cubierta');
this.divSobrecubierta = $('#div-sobrecubierta');
this.divFaja = $('#div-faja');
this.acabadoSobrecubierta = $('#sobrecubierta-acabado');
this.fajaSobrecubierta = $('#faja-acabado');
this.sobrecubierta = $('#sobrecubierta');
this.papelSobrecubierta = $('#papel-sobrecubierta');
this.solapasSobrecubierta = $('#tamanio-solapas-sobrecubierta');
this.faja = $('#faja');
this.papelFaja = $('#papel-faja');
this.altoFaja = $('#alto-faja');
this.solapasFaja = $('#tamanio-solapas-faja');
this.acabadoFaja = $('#faja-acabado');
// seleccion tirada
this.divTiradas = $('#div-tiradas');
this.divTiradasError = $('#div-tiradas-error');
// pestaña extras
this.divExtras = $('#div-extras');
}
init() {
const stored = sessionStorage.getItem("formData");
this.#initDatosGenerales();
this.#initInterior();
this.#initCubierta();
if (stored) {
this.formData = JSON.parse(stored);
this.#loadDatosGeneralesData();
this.#loadCubiertaData();
}
this.#initInterior();
this.#initSeleccionTirada();
this.#initExtras();
$(document).on('change', 'input[min][max]', function () {
const $input = $(this);
@ -108,11 +157,10 @@ class PresupuestoCliente {
}
});
const stored = sessionStorage.getItem("formData");
if (stored) {
this.formData = JSON.parse(stored);
this.#loadDatosGeneralesData();
}
$('.custom-nav .nav-link').on('click', function (e) {
e.preventDefault();
e.stopImmediatePropagation(); // frena el handler de Bootstrap
});
}
#cacheFormData() {
@ -122,22 +170,45 @@ class PresupuestoCliente {
#changeTab(idContenidoTab) {
const tabButton = document.querySelector(`[data-bs-target="#${idContenidoTab}"]`);
if (tabButton) {
const tab = new bootstrap.Tab(tabButton);
tab.show();
bootstrap.Tab.getOrCreateInstance(tabButton).show();
}
}
#getPresupuestoData() {
return {
let data = {
...this.#getDatosGeneralesData(),
...this.#getInteriorData(),
...this.#getCubiertaData()
...this.#getCubiertaData(),
selectedTirada: this.formData.selectedTirada
};
const sobrecubierta = data.sobrecubierta;
const faja = data.faja;
delete data.sobrecubierta;
delete data.faja;
data = {
...data,
sobrecubierta: sobrecubierta.activo,
papelSobrecubiertaId: sobrecubierta.papelSobrecubiertaId,
gramajeSobrecubierta: sobrecubierta.gramajeSobrecubierta,
tamanioSolapasSobrecubierta: sobrecubierta.tamanioSolapasSobrecubierta,
acabadoSobrecubierta: sobrecubierta.acabado,
faja: faja.activo,
papelFajaId: faja.papelFajaId,
gramajeFaja: faja.gramajeFaja,
altoFaja: faja.alto,
tamanioSolapasFaja: faja.tamanioSolapasFaja,
acabadoFaja: faja.acabado
}
return data;
}
#addGramaje(contenedor, gramaje, name) {
#addGramaje(contenedor, id_base, gramaje, name) {
const id = `gramaje-${gramaje}`;
const id = `${id_base}-${gramaje}`;
// Crear input
const input = document.createElement('input');
@ -178,6 +249,7 @@ class PresupuestoCliente {
else {
this.divPosicionPaginasColor.removeClass('d-none');
}
this.paginas = parseInt(this.paginasNegro.val()) + parseInt(this.paginasColor.val());
this.#updateTipoEncuadernacion();
});
@ -241,6 +313,8 @@ class PresupuestoCliente {
this.datos_generales_alert.addClass('d-none');
$('.alto-faja-max').text(`max: ${this.formData.datosGenerales.alto} mm`);
this.#loadInteriorData(data);
const interiorData = this.#getInteriorData();
@ -541,6 +615,8 @@ class PresupuestoCliente {
});
this.divAcabadoCubierta.empty();
this.acabadoSobrecubierta.empty();
this.fajaSobrecubierta.empty();
for (const opcion of data.opciones_acabados_cubierta) {
const item = `
<input type="radio" class="btn-check acabado-cubierta-check" id="acabado-cubierta-${opcion['sk-id']}" name="acabado-cubierta" sk-id="${opcion['sk-id']}">
@ -549,16 +625,20 @@ class PresupuestoCliente {
</label>
`;
this.divAcabadoCubierta.append(item);
this.acabadoSobrecubierta.append(new Option(opcion.name, opcion['sk-id']));
this.fajaSobrecubierta.append(new Option(opcion.name, opcion['sk-id']));
};
$(`input[type="radio"][name="acabado-cubierta"][sk-id="${this.formData.cubierta.acabado}"]`).prop('checked', true);
this.acabadoSobrecubierta.val(this.formData.cubierta.sobrecubierta.acabado);
this.fajaSobrecubierta.val(this.formData.cubierta.faja.acabado);
this.#loadCubiertaData(data);
this.#loadCubiertaData();
this.#changeTab('pills-cover');
const dataInterior = this.#getInteriorData();
this.#updateInteriorData(dataInterior);
const dataCubierta = this.#getCubiertaData();
this.#updateCubiertaData(dataCubierta);
this.#cacheFormData();
}
@ -628,13 +708,13 @@ class PresupuestoCliente {
for (let i = 0; i < gramajes.length; i++) {
const gramaje = gramajes[i];
this.#addGramaje(this.divGramajeInterior, gramaje, 'gramaje-interior');
this.#addGramaje(this.divGramajeInterior, 'gramaje-interior', gramaje, 'gramaje-interior');
// Seleccionar el gramaje por defecto
if (this.formData.interior.gramajeInterior === '' && i === 0) {
$(`#gramaje-${gramaje}`).prop('checked', true);
$(`#gramaje-interior-${gramaje}`).prop('checked', true);
} else if (this.formData.interior.gramajeInterior == gramaje) {
$(`#gramaje-${gramaje}`).prop('checked', true);
$(`#gramaje-interior-${gramaje}`).prop('checked', true);
}
}
if (this.divGramajeInterior.find('input[type="radio"]:checked').length === 0) {
@ -763,6 +843,15 @@ class PresupuestoCliente {
});
});
$(document).on('click', '.gramaje-cubierta', (e) => {
const inputId = $(e.currentTarget).attr('for');
const gramaje = parseInt($('#' + inputId).data('gramaje'));
this.formData.cubierta.gramajeCubierta = gramaje;
this.#cacheFormData();
});
$(document).on('change', '.datos-cubierta', (e) => {
const dataToStore = this.#getCubiertaData();
@ -775,21 +864,63 @@ class PresupuestoCliente {
this.formData.cubierta.acabado = parseInt($(e.currentTarget).attr('sk-id')) || 1;
this.#cacheFormData();
});
$('.btn-change-tab-cubierta').on('click', () => {
this.#changeTab('pills-inside');
/*const data = this.#getPresupuestoData();
$(document).on('click', '.custom-toggle', (e) => {
const $target = $(e.currentTarget);
if ($target.hasClass('active')) {
if ($target.attr('id') === 'sobrecubierta') {
this.divSobrecubierta.removeClass('d-none');
}
else if ($target.attr('id') === 'faja') {
this.divFaja.removeClass('d-none');
}
} else {
if ($target.attr('id') === 'sobrecubierta') {
this.divSobrecubierta.addClass('d-none');
}
else if ($target.attr('id') === 'faja') {
this.divFaja.addClass('d-none');
}
}
const dataToStore = this.#getCubiertaData();
this.#updateCubiertaData(dataToStore);
this.#cacheFormData();
});
$('.btn-change-tab-cubierta').on('click', (e) => {
const data = this.#getPresupuestoData();
const id = e.currentTarget.id;
$.ajax({
url: '/presupuesto/public/validar/cubierta',
type: 'POST',
data: data,
url: '/presupuesto/public/validar/cubierta',
type: 'POST',
data: data,
success: (data) => {
this.#changeTab('pills-inside');
if (id === 'btn-prev-cubierta') {
this.#changeTab('pills-inside');
}
else {
$('#btn-next-seleccion-tirada').removeClass('d-none');
this.#loadSeleccionTiradaData(data);
this.#changeTab('pills-seleccion-tirada');
}
},
error: (xhr, status, error) => {
console.error("Error al validar los datos de cubierta: ", xhr.responseText);
if (id === 'btn-prev-cubierta') {
this.#changeTab('pills-inside');
return;
}
this.divTiradas.empty();
this.divTiradasError.empty();
this.divTiradas.addClass('d-none');
this.divTiradasError.removeClass('d-none');
$('#btn-next-seleccion-tirada').addClass('d-none');
this.divTiradasError.append('<div class="alert alert-danger" role="alert">' +
'<h4>' + xhr.responseText + '</h4></div>');
this.#changeTab('pills-seleccion-tirada');
}
});*/
});
});
}
@ -833,20 +964,7 @@ class PresupuestoCliente {
this.divPapelCubierta.find('.image-container').first().data('sk-id') || 3;
}
const gramajesCubierta = data.opciones_gramaje_cubierta;
for (let i = 0; i < gramajesCubierta.length; i++) {
const gramaje = gramajesCubierta[i];
this.#addGramaje(this.divGramajeCubierta, gramaje, 'gramaje-cubierta');
if (this.formData.cubierta.gramajeCubierta === '' && i === 0) {
$(`#gramaje-${gramaje}`).prop('checked', true);
} else if (this.formData.cubierta.gramajeCubierta == gramaje) {
$(`#gramaje-${gramaje}`).prop('checked', true);
}
}
if (this.divGramajeCubierta.find('input[type="radio"]:checked').length === 0) {
this.divGramajeCubierta.find('input[type="radio"]').first().prop('checked', true);
}
this.#addGramajesCubierta(data.opciones_gramaje_cubierta);
const dataToStore = this.#getCubiertaData();
this.#updateCubiertaData(dataToStore);
@ -870,9 +988,20 @@ class PresupuestoCliente {
const guardasGramaje = parseInt($('#papel-guardas option:selected').data('gramaje')) || 170;
const guardasImpresas = parseInt(this.guardasImpresas) || 0;
const cabezada = this.cabezada.val() || 'WHI';
const papelCubiertaId = $('#div-papel-cubierta .image-container.selected').data('sk-id') || 3;
const gramajeCubierta = $('input[name="gramaje-cubierta"]:checked').data('gramaje') || 240;
const papelCubiertaId = $('#div-papel-cubierta .image-container.selected').data('sk-id') || this.formData.cubierta.papelCubiertaId || 3;
const gramajeCubierta = $('input[name="gramaje-cubierta"]:checked').data('gramaje') || this.formData.cubierta.gramajeCubierta || 170;
const acabado = parseInt($(`input[name="acabado-cubierta"]:checked`).attr('sk-id')) || 1;
const sobrecubierta = this.sobrecubierta.hasClass('active');
const papelSobrecubiertaId = $('#papel-sobrecubierta option:selected').data('papel-id') || 2;
const gramajeSobrecubierta = parseInt($('#papel-sobrecubierta option:selected').data('gramaje')) || 170;
const solapasSobrecubierta = parseInt(this.solapasSobrecubierta.val()) || 0;
const acabadoSobrecubierta = this.acabadoSobrecubierta.val() || 0;
const faja = this.faja.hasClass('active');
const papelFajaId = $('#papel-faja option:selected').data('papel-id') || 2;
const gramajeFaja = parseInt($('#papel-faja option:selected').data('gramaje')) || 170;
const fajaAlto = parseInt(this.altoFaja.val()) || 50;
const solapasFaja = parseInt(this.solapasFaja.val()) || 0;
const acabadoFaja = this.acabadoFaja.val() || 0;
return {
tipoCubierta: tipoCubierta,
@ -885,7 +1014,22 @@ class PresupuestoCliente {
cabezada: cabezada,
papelCubiertaId: papelCubiertaId,
gramajeCubierta: gramajeCubierta,
acabadado: acabado
acabado: acabado,
sobrecubierta: {
activo: sobrecubierta,
papelSobrecubiertaId: papelSobrecubiertaId,
gramajeSobrecubierta: gramajeSobrecubierta,
acabado: acabadoSobrecubierta,
tamanioSolapasSobrecubierta: solapasSobrecubierta
},
faja: {
activo: faja,
papelFajaId: papelFajaId,
gramajeFaja: gramajeFaja,
alto: fajaAlto,
acabado: acabadoFaja,
tamanioSolapasFaja: solapasFaja
}
};
}
@ -902,19 +1046,34 @@ class PresupuestoCliente {
this.formData.cubierta.papelCubiertaId = data.papelCubiertaId;
this.formData.cubierta.gramajeCubierta = data.gramajeCubierta;
this.formData.cubierta.acabado = data.acabado;
this.formData.cubierta.sobrecubierta = {
activo: data.sobrecubierta.activo,
papelSobrecubiertaId: data.sobrecubierta.papelSobrecubiertaId,
gramajeSobrecubierta: data.sobrecubierta.gramajeSobrecubierta,
tamanioSolapasSobrecubierta: data.sobrecubierta.tamanioSolapasSobrecubierta,
acabado: data.sobrecubierta.acabado
};
this.formData.cubierta.faja = {
activo: data.faja.activo,
papelFajaId: data.faja.papelFajaId,
gramajeFaja: data.faja.gramajeFaja,
tamanioSolapasFaja: data.faja.tamanioSolapasFaja,
acabado: data.faja.acabado,
alto: data.faja.alto
};
}
#addGramajesCubierta(gramajes) {
for (let i = 0; i < gramajes.length; i++) {
const gramaje = gramajes[i];
this.#addGramaje(this.divGramajeCubierta, gramaje, 'gramaje-cubierta');
this.#addGramaje(this.divGramajeCubierta, 'gramaje-cubierta', gramaje, 'gramaje-cubierta datos-cubierta');
// Seleccionar el gramaje por defecto
if (this.formData.interior.gramajeCubierta === '' && i === 0) {
$(`#gramaje-${gramaje}`).prop('checked', true);
} else if (this.formData.interior.gramajeCubierta == gramaje) {
$(`#gramaje-${gramaje}`).prop('checked', true);
if (this.formData.cubierta.gramajeCubierta === '' && i === 0) {
$(`#gramaje-cubierta-${gramaje}`).prop('checked', true);
} else if (this.formData.cubierta.gramajeCubierta == gramaje) {
$(`#gramaje-cubierta-${gramaje}`).prop('checked', true);
}
}
if (this.divGramajeCubierta.find('input[type="radio"]:checked').length === 0) {
@ -940,7 +1099,6 @@ class PresupuestoCliente {
this.formData.cubierta.guardasGramaje + '"]').prop('selected', true).trigger('change');
this.guardasImpresas.val(this.formData.cubierta.guardasImpresas);
this.cabezada.val(this.formData.cubierta.cabezada);
$(`input[type="radio"][name="acabado-cubierta"][sk-id="${this.formData.cubierta.acabado}"]`).prop('checked', true);
}
$(`#${this.formData.cubierta.tipoCubierta}`).trigger('click');
@ -958,13 +1116,153 @@ class PresupuestoCliente {
}
this.carasImpresionCubierta.val(this.formData.cubierta.cubiertaCaras);
$(`input[type="radio"][name="acabado-cubierta"][sk-id="${this.formData.cubierta.acabado}"]`).prop('checked', true);
if (this.formData.cubierta.sobrecubierta.activo) {
this.sobrecubierta.addClass('active');
this.divSobrecubierta.removeClass('d-none');
}
else {
this.sobrecubierta.removeClass('active');
this.divSobrecubierta.addClass('d-none');
}
$('#papel-sobrecubierta option[data-papel-id="' +
this.formData.cubierta.sobrecubierta.papelSobrecubiertaId + '"][data-gramaje="' +
this.formData.cubierta.sobrecubierta.gramajeSobrecubierta + '"]').prop('selected', true);
this.solapasSobrecubierta.val(this.formData.cubierta.sobrecubierta.tamanioSolapasSobrecubierta);
this.acabadoSobrecubierta.val(this.formData.cubierta.sobrecubierta.acabado);
if (this.formData.cubierta.faja.activo) {
this.faja.addClass('active');
this.divFaja.removeClass('d-none');
}
else {
this.faja.removeClass('active');
this.divFaja.addClass('d-none');
}
$('#papel-faja option[data-papel-id="' +
this.formData.cubierta.faja.papelFajaId + '"][data-gramaje="' +
this.formData.cubierta.faja.gramajeFaja + '"]').prop('selected', true);
this.solapasFaja.val(this.formData.cubierta.faja.tamanioSolapasFaja);
this.altoFaja.val(this.formData.cubierta.faja.alto);
this.acabadoFaja.val(this.formData.cubierta.faja.acabado);
}
/******************************
* END CUBIERTA
******************************/
/******************************
* SELECCION TIRADA
******************************/
#initSeleccionTirada() {
$(document).on('click', '.btn-change-tab-seleccion-tirada', (e) => {
const id = e.currentTarget.id;
if (id === 'btn-prev-seleccion-tirada') {
this.#changeTab('pills-cover');
} else {
const data = this.#getPresupuestoData();
const id = e.currentTarget.id;
$.ajax({
url: '/presupuesto/public/validar/seleccion-tirada',
type: 'POST',
data: data,
success: (data) => {
this.#loadExtrasData(data.servicios_extra);
this.#changeTab('pills-extras');
},
error: (xhr, status, error) => {
console.error("Error al validar los datos de selección de tirada: ", xhr.responseText);
}
});
}
});
this.divTiradas.off('tirada:changed.cache') // por si re-bindeas
.on('tirada:changed.cache', (e, detail) => {
// detail = { id, name, unidades, precioUnidad, precioTotal }
this.formData.selectedTirada = detail.unidades;
this.#cacheFormData();
});
}
#loadSeleccionTiradaData(data) {
this.divTiradasError.addClass('d-none');
this.divTiradas.empty();
this.divTiradas.removeClass('d-none');
this.divTiradas.removeClass('animate-fadeInUpBounce');
const labels = {
perUnit: this.divTiradas.data('perUnit'),
total: this.divTiradas.data('total'),
select: this.divTiradas.data('select'),
selected: this.divTiradas.data('selected'),
units: this.divTiradas.data('units')
};
this.divTiradas.addClass('animate-fadeInUpBounce');
for (let i = 0; i < data.tiradas.length; i++) {
const item = new TiradaCard({
id: "tirada-" + data.tiradas[i],
unidades: data.tiradas[i],
precioUnidad: data.precios[i],
precioTotal: data.precios[i] * data.tiradas[i],
moneda: "€",
labels: labels,
selected: this.formData.selectedTirada == data.tiradas[i]
});
this.divTiradas.append(item.renderCol(this.divTiradas));
}
if (this.divTiradas.find('.tirada-card input[type="radio"]:checked').length === 0) {
this.divTiradas.find('.tirada-card input[type="radio"]').first().prop('checked', true).trigger('change');
this.formData.selectedTirada = this.divTiradas.find('.tirada-card input[type="radio"]').first().data('unidades') || data.tiradas[0];
}
}
/******************************
* END SELECCION TIRADA
******************************/
/******************************
* EXTRAS
******************************/
#initExtras() {
$(document).on('click', '.btn-change-tab-extras', (e) => {
const id = e.currentTarget.id;
if (id === 'btn-prev-extras') {
this.#changeTab('pills-seleccion-tirada');
} else {
//this.#changeTab('pills-finalizar');
}
});
}
#loadExtrasData(servicios) {
this.divExtras.empty();
this.divExtras.removeClass('animate-fadeInUpBounce');
this.divExtras.addClass('animate-fadeInUpBounce');
for (const extra of servicios) {
const item = new ServiceOptionCard(extra);
this.divExtras.append(item.render());
}
}
/******************************
* END EXTRAS
******************************/
}

View File

@ -0,0 +1,46 @@
class ServiceOptionCard {
constructor({ id, title, description = '', price = '', priceUnit = '', checked = false, allowChange = true, ribbonText }) {
this.id = id;
this.title = title;
this.description = description ? description : '&nbsp;';
this.price = price;
this.priceUnit = priceUnit;
this.checked = checked;
this.allowChange = !(String(allowChange).toLowerCase() === "false");
this.ribbonText = ribbonText;
}
render() {
const ribbonHtml = this.ribbonText
? `<div class="ribbon-service"><span>${this.ribbonText}</span></div>`
: '';
const $card = $(`
<div class="col-lg-2 col-md-3 col-sm-6 mb-3">
<input type="checkbox" class="service-checkbox data-price=${this.price} btn-check-service" id="${this.id}" name="services[]" value="${this.id}" autocomplete="off" ${this.checked ? 'checked' : ''} />
<label class="btn btn-outline-primary w-100 text-center py-3 px-2 d-flex flex-column align-items-center justify-content-center h-100" for="${this.id}">
${ribbonHtml}
<h5 class="service-title mb-1">${this.title}</h5>
<p class="service-desc mb-1 small">${this.description}</p>
<h4 class="service-price fw-semibold mt-2 h4 mb-0">${this.price} ${this.priceUnit}</h4>
</label>
</div>
`);
const $checkbox = $card.find('input[type="checkbox"]');
if (!this.allowChange) {
// Deshabilita el cambio de estado visual
$checkbox.on('click', (e) => {
e.preventDefault();
e.stopImmediatePropagation();
return false;
});
}
return $card;
}
}
export default ServiceOptionCard;

View File

@ -0,0 +1,123 @@
// ===== Clase =====
class TiradaCard {
constructor({ id, titulo, unidades, precioUnidad, precioTotal, selected = false, moneda = '€', name = 'tirada',
labels = {}, locale = (document.documentElement.lang || navigator.language || 'es-ES')
}) {
this.id = id;
this.titulo = titulo;
this.unidades = unidades;
this.precioUnidad = precioUnidad;
this.precioTotal = precioTotal;
this.selected = selected;
this.moneda = moneda;
this.name = name;
this.locale = locale;
this.labels = Object.assign({
perUnit: 'Precio por unidad',
total: 'Total',
select: 'Seleccionar tirada',
selected: 'Seleccionada',
units: 'UNIDADES'
}, labels);
this.$container = null; // se establece al renderizar
}
#formatMoneyES(value, decimals = 2) {
return new Intl.NumberFormat(this.locale, {
minimumFractionDigits: decimals,
maximumFractionDigits: decimals
}).format(Number(value));
}
#title() {
if (this.titulo) return this.titulo;
if (this.unidades != null) return `${this.unidades} ${this.labels.units}`;
return '';
}
// pásale el contenedor donde van todas las tarjetas de este grupo
renderCol($container) {
this.$container = $container ? $($container) : $(document.body);
const col = $(`
<div class="col d-flex">
<label class="tirada-card ${this.selected ? 'selected' : ''} w-100 h-100" for="tirada-${this.id}">
<input type="radio" name="${this.name}" id="tirada-${this.id}" value="${this.unidades}" ${this.selected ? 'checked' : ''}>
<div class="title">${this.#title()}</div>
<div class="per muted">${this.labels.perUnit}</div>
<div class="price-big">${this.#formatMoneyES(this.precioUnidad, 4)} ${this.moneda}</div>
<div class="price-row">
<div class="muted">${this.labels.total}</div>
<div class="fw-bold">${this.#formatMoneyES(this.precioTotal, 2)} ${this.moneda}</div>
</div>
<button type="button" class="btn btn-select-tirada">${this.labels.select}</button>
</label>
</div>
`);
// --- Delegación de eventos en el contenedor (una sola vez por grupo) ---
const boundKey = `tirada-bound-${this.name}`;
const groupName = this.name;
const $group = this.$container;
const labels = this.labels;
if (!$group.data(boundKey)) {
$group.on('change', `input[type="radio"][name="${groupName}"]`, function () {
// radios del grupo
const $groupRadios = $group.find(`input[type="radio"][name="${groupName}"]`);
// resetear todas las tarjetas del grupo
$groupRadios
.closest('.tirada-card')
.removeClass('selected')
.find('.btn-select-tirada')
.html(labels.select || 'Seleccionar tirada');
// marcar la tarjeta seleccionada y su botón
const $card = $(this).closest('.tirada-card');
$card.find('.btn-select-tirada')
.html(labels.selected + ' <i class="mdi mdi-check-circle ms-1"></i>');
$card.addClass('selected');
const $input = $(this);
const detail = {
id: $input.attr('id'),
name: $input.attr('name'),
unidades: Number($input.data('unidades') ?? $input.val()),
precioUnidad: Number($input.data('precioUnidad')),
precioTotal: Number($input.data('precioTotal'))
};
// jQuery: pasa 'detail' como segundo argumento del handler
$group.trigger('tirada:changed', [detail]);
});
$group.data(boundKey, true);
}
const $btnLocal = col.find('.btn-select-tirada');
if (this.selected) {
$btnLocal.html(labels.selected + ' <i class="mdi mdi-check-circle ms-1"></i>');
}
// --- Comportamiento de la tarjeta/botón (local a esta tarjeta) ---
const card = col.find('.tirada-card');
col.find('.btn-select-tirada').on('click', (e) => {
e.preventDefault();
card.find('input[type="radio"]').prop('checked', true).trigger('change');
});
card.on('click', function (e) {
if ($(e.target).is('.btn-select-tirada')) return;
$(this).find('input[type="radio"]').prop('checked', true).trigger('change');
});
return col;
}
}
export default TiradaCard;