From 4ccd100936043000968e111e61381db19184950b Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 16 Apr 2025 13:44:29 -0500 Subject: [PATCH 01/72] =?UTF-8?q?Validaci=C3=B3n=20m=C3=BAltiples=20de=20p?= =?UTF-8?q?ixeles=20en=20imagenes=20subidas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.php | 5 - main.js | 271 +++++++++++++++++++++++++------------------- styles.css | 16 +++ web-form-plugin.php | 6 +- 4 files changed, 176 insertions(+), 122 deletions(-) diff --git a/index.php b/index.php index 2e44706..57770ae 100644 --- a/index.php +++ b/index.php @@ -60,11 +60,6 @@ - -
diff --git a/main.js b/main.js index 8e18f88..77d783d 100644 --- a/main.js +++ b/main.js @@ -20,7 +20,7 @@ document.addEventListener('DOMContentLoaded', function () { initializeSizePicker(); initializeFramePicker(); setupEventListeners(); - + // Initialize image counter const counterElement = document.createElement('div'); counterElement.id = 'images-counter'; @@ -48,8 +48,9 @@ function initializeSizePicker() { items: sizeOptions, initialValue: '10X15', onChange: (value) => { - // console.log('Size picker changed to:', value); - updateTotalPrice(); + console.log('Size picker changed to:', value); + revalidateImagesWithNewSize(value); // Revalidar las imágenes con el nuevo tamaño + updateTotalPrice(); // Actualizar el precio total } }); } @@ -73,7 +74,33 @@ function initializeFramePicker() { } function setupEventListeners() { - document.querySelector('.quantity-field input').addEventListener('input', updateTotalPrice); + const quantityInput = document.querySelector('.quantity-field input'); + + quantityInput.addEventListener('focus', function () { + if (this.value === '1') { + this.value = ''; + } + }); + + quantityInput.addEventListener('input', function () { + let value = parseInt(this.value); + + // Si es mayor a 100, vuelve al mismo valor + if (value > 100) { + this.value = 100; + } + + updateTotalPrice(); + }); + + quantityInput.addEventListener('blur', function () { + let value = parseInt(this.value); + + if (isNaN(value) || value < 1) { + this.value = 1; + } + }); + document.getElementById("custom-size__width").addEventListener("input", handleCustomSize); document.getElementById("custom-size__height").addEventListener("input", handleCustomSize); document.querySelector('.add-to-cart-btn').addEventListener('click', addToCart); @@ -283,40 +310,39 @@ function getMinimumPixelsForImage(size) { return { minWidth, minHeight }; } -// Función para analizar los píxeles de la imagen -function analyzeImagePixels(img, imageSize, onSuccess, onError) { +// Modificado para incluir el nombre del archivo +function analyzeImagePixels(img, imageSize, fileName) { const { minWidth, minHeight } = getMinimumPixelsForImage(imageSize); if (img.naturalWidth === 0 || img.naturalHeight === 0) { - console.log("⚠️ La imagen aún no se ha cargado completamente."); - onError("La imagen no se cargó correctamente."); - return; + return `La imagen "${fileName}" no se cargó correctamente.`; } - // Validar dimensiones mínimas dinámicamente if (img.naturalWidth < minWidth || img.naturalHeight < minHeight) { - console.log(`❌ La imagen no cumple con las dimensiones mínimas: ${img.naturalWidth}x${img.naturalHeight}`); - onError(`Tu imagen debe tener al menos ${minWidth}x${minHeight} píxeles para un tamaño de ${imageSize.replace("X", "x")} cm.`); - return; + return `La imagen "${fileName}" no tiene suficiente calidad para imprimir en ${imageSize.replace("X", "x")} cm. Debe tener al menos ${minWidth}x${minHeight} píxeles.`; } - console.log(`✅ Imagen válida: ${img.naturalWidth}x${img.naturalHeight}`); - onSuccess(); + return null; // No hay error } -// Modificar el flujo de subida de imágenes para incluir la validación dinámica + function handleFiles(e) { const files = e.target.files; + const selectedSize = sizePicker.getValue(); + const lowQualityIndexes = []; if (files.length > 0) { uploadElements.classList.add('has-images'); } + let pending = files.length; + for (let i = 0; i < files.length; i++) { const file = files[i]; - - // Verificar que sea una imagen - if (!file.type.match('image.*')) continue; + if (!file.type.match('image.*')) { + pending--; + continue; + } const reader = new FileReader(); @@ -325,63 +351,68 @@ function handleFiles(e) { img.src = event.target.result; img.onload = function () { - // Obtener el tamaño de la imagen seleccionado en el picker - const selectedSize = sizePicker.getValue(); // Ejemplo: "10X15" - - // Validar la calidad de la imagen según el tamaño seleccionado - analyzeImagePixels( - img, - selectedSize, - () => { - // Si la imagen es válida, agregarla al array y mostrarla - const imageId = 'img_' + Date.now() + '_' + i; - - uploadedImagesData.push({ - id: imageId, - file: file, - }); - - console.log("Imagen añadida:", imageId); - - const imageItem = document.createElement('div'); - imageItem.className = 'image-item'; - imageItem.dataset.imageId = imageId; - - const imgElement = document.createElement('img'); - imgElement.src = event.target.result; - imgElement.alt = 'Imagen subida'; - - const removeBtn = document.createElement('button'); - removeBtn.className = 'remove-image-btn'; - removeBtn.textContent = 'X'; - removeBtn.addEventListener('click', function (e) { - e.stopPropagation(); - - uploadedImagesData = uploadedImagesData.filter(img => img.id !== imageId); - console.log("Imagen eliminada:", imageId); - - imageItem.remove(); - - if (imagesGrid.children.length === 0) { - uploadElements.classList.remove('has-images'); - } - - updateImageCounter(); - updateTotalPrice(); - }); - - imageItem.appendChild(imgElement); - imageItem.appendChild(removeBtn); - imagesGrid.appendChild(imageItem); - - updateImageCounter(); - updateTotalPrice(); - }, - (errorMessage) => { - // Si la imagen no es válida, mostrar un mensaje de error - showNotification(errorMessage, 'error'); + const { minWidth, minHeight } = getMinimumPixelsForImage(selectedSize); + const isLowQuality = img.naturalWidth < minWidth || img.naturalHeight < minHeight; + + const imageId = 'img_' + Date.now() + '_' + i; + + uploadedImagesData.push({ + id: imageId, + file: file, + lowQuality: isLowQuality, + }); + + const imageItem = document.createElement('div'); + imageItem.className = 'image-item'; + imageItem.dataset.imageId = imageId; + + if (isLowQuality) { + imageItem.classList.add('low-quality'); + lowQualityIndexes.push(i + 1); // Posición humana + } + + const imgElement = document.createElement('img'); + imgElement.src = event.target.result; + imgElement.alt = 'Imagen subida'; + + const removeBtn = document.createElement('button'); + removeBtn.className = 'remove-image-btn'; + removeBtn.textContent = 'X'; + removeBtn.addEventListener('click', function (e) { + e.stopPropagation(); + uploadedImagesData = uploadedImagesData.filter(img => img.id !== imageId); + imageItem.remove(); + if (imagesGrid.children.length === 0) { + uploadElements.classList.remove('has-images'); } - ); + updateImageCounter(); + updateTotalPrice(); + }); + + const warningLabel = document.createElement('div'); + warningLabel.className = 'quality-warning'; + warningLabel.textContent = '⚠️ Resolución baja'; + if (!isLowQuality) warningLabel.style.display = 'none'; + + imageItem.appendChild(imgElement); + imageItem.appendChild(warningLabel); + imageItem.appendChild(removeBtn); + imagesGrid.appendChild(imageItem); + + updateImageCounter(); + updateTotalPrice(); + + pending--; + + if (pending === 0 && lowQualityIndexes.length > 0) { + const imagesStr = lowQualityIndexes.join(', '); + const plural = lowQualityIndexes.length > 1; + const message = plural + ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.` + : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.`; + + showNotification(message, 'error'); + } }; }; @@ -391,6 +422,52 @@ function handleFiles(e) { fileInput.value = ''; } +function revalidateImagesWithNewSize(newSize) { + const { minWidth, minHeight } = getMinimumPixelsForImage(newSize); + const lowQualityIndexes = []; + + // Recorre las imágenes en el orden en que aparecen en el grid + const imageItems = Array.from(imagesGrid.querySelectorAll('.image-item')); + + imageItems.forEach((item, index) => { + const imageId = item.dataset.imageId; + const data = uploadedImagesData.find(img => img.id === imageId); + const imgElement = item.querySelector('img'); + const warningLabel = item.querySelector('.quality-warning'); + + const tempImg = new Image(); + tempImg.src = imgElement.src; + + tempImg.onload = () => { + const isLowQuality = tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; + + // Actualiza en el array de datos + data.lowQuality = isLowQuality; + + // Modifica clases visuales y etiqueta + if (isLowQuality) { + item.classList.add('low-quality'); + if (warningLabel) warningLabel.style.display = 'block'; + lowQualityIndexes.push(index + 1); // Índice basado en el orden del grid + } else { + item.classList.remove('low-quality'); + if (warningLabel) warningLabel.style.display = 'none'; + } + + // Después de terminar todas, mostrar advertencia si hay + if (index === imageItems.length - 1 && lowQualityIndexes.length > 0) { + const plural = lowQualityIndexes.length > 1; + const imagesStr = lowQualityIndexes.join(', '); + const message = plural + ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.` + : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.`; + + showNotification(message, 'error'); + } + }; + }); +} + // Función para añadir al carrito - Modificada para procesar múltiples imágenes de forma secuencial function addToCart() { console.log("🚀 addToCart() fue llamada correctamente"); @@ -561,11 +638,11 @@ function showNotification(message, type = 'info') { // Agregar el mensaje al log de la consola console.log(`[${type.toUpperCase()}] ${message}`); - + setTimeout(() => notification.classList.add('show'), 10); setTimeout(() => { notification.classList.remove('show'); - setTimeout(() => document.body.removeChild(notification), 300); + setTimeout(() => document.body.removeChild(notification), 500); }, 3000); } @@ -603,10 +680,10 @@ function updateTotalPrice() { } const quantity = parseInt(document.querySelector('.quantity-field input').value) || 1; - + // Use the sizePicker instance to get the value instead of DOM selection const selectedSize = sizePicker.getValue(); - + let pricePerUnit = 0; if (selectedSize) { @@ -624,38 +701,4 @@ function updateTotalPrice() { } // Exponer la función al ámbito global para evitar problemas con `onclick` -window.addToCart = addToCart; - -//SCROLL DE CANTIDAD -function setupEventListeners() { - const quantityInput = document.querySelector('.quantity-field input'); - - quantityInput.addEventListener('focus', function () { - if (this.value === '1') { - this.value = ''; - } - }); - - quantityInput.addEventListener('input', function () { - let value = parseInt(this.value); - - // Si es mayor a 100, vuelve al mismo valor - if (value > 100) { - this.value = 100; - } - - updateTotalPrice(); - }); - - quantityInput.addEventListener('blur', function () { - let value = parseInt(this.value); - - if (isNaN(value) || value < 1) { - this.value = 1; - } - }); - - document.getElementById("custom-size__width").addEventListener("input", handleCustomSize); - document.getElementById("custom-size__height").addEventListener("input", handleCustomSize); - document.querySelector('.add-to-cart-btn').addEventListener('click', addToCart); -} \ No newline at end of file +window.addToCart = addToCart; \ No newline at end of file diff --git a/styles.css b/styles.css index 7646713..6fdf544 100644 --- a/styles.css +++ b/styles.css @@ -342,6 +342,22 @@ body { background-color: #2196F3; } +.image-item.low-quality { + border: 2px dashed #ff9800; + position: relative; +} + +.quality-warning { + position: absolute; + bottom: 4px; + left: 4px; + background-color: #ffeb3b; + color: #000; + padding: 2px 6px; + font-size: 12px; + border-radius: 4px; +} + /* Estilos para indicador de carga */ .loading-overlay { position: fixed; diff --git a/web-form-plugin.php b/web-form-plugin.php index a85b890..8f1b553 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 2.7 +Version: 2.8 Author: Enmanuel */ @@ -37,7 +37,7 @@ function web_form_shortcode() $picker_path = plugin_dir_url(__FILE__) . 'ios-picker.js'; // Cargar CSS - wp_enqueue_style('web-form-styles', $css_path, array(), '4.9'); + wp_enqueue_style('web-form-styles', $css_path, array(), '5.0'); // Cargar Swiper CSS desde CDN wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); @@ -46,7 +46,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.2', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.3', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From a2ed4fb00d6c2d4762dc0577f25eda3a317508f2 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 16 Apr 2025 21:00:44 -0500 Subject: [PATCH 02/72] =?UTF-8?q?Validaci=C3=B3n=20de=20campos=20personali?= =?UTF-8?q?zado=20y=20a=C3=B1adir=20comentarios=20en=20codigo=20de=20valid?= =?UTF-8?q?acion=20de=20pixeles?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 324 ++++++++++++++++++++------------------------ web-form-plugin.php | 6 +- 2 files changed, 153 insertions(+), 177 deletions(-) diff --git a/main.js b/main.js index 77d783d..6ac9394 100644 --- a/main.js +++ b/main.js @@ -107,9 +107,54 @@ function setupEventListeners() { } function handleCustomSize() { - if (document.querySelector('input[name="size-type"]:checked').value === 'custom') { + const widthInput = document.getElementById("custom-size__width"); + const heightInput = document.getElementById("custom-size__height"); + + // ANCHO + widthInput.addEventListener('focus', function () { + if (this.value === '10') this.value = ''; + }); + + widthInput.addEventListener('input', function () { + let value = parseInt(this.value); + if (value > 60) { + this.value = 60; + showNotification("El ancho no puede ser mayor a 60 cm.", 'error'); + } updateTotalPrice(); - } + }); + + widthInput.addEventListener('blur', function () { + let value = parseInt(this.value); + if (isNaN(value) || value < 10) { + this.value = 10; + showNotification("El ancho no puede ser menor a 10 cm.", 'error'); + } + }); + + // ALTO + heightInput.addEventListener('focus', function () { + if (this.value === '15') this.value = ''; + }); + + heightInput.addEventListener('input', function () { + let value = parseInt(this.value); + if (value > 100) { + this.value = 100; + showNotification("El alto no puede ser mayor a 100 cm.", 'error'); + } + updateTotalPrice(); + }); + + heightInput.addEventListener('blur', function () { + let value = parseInt(this.value); + if (isNaN(value) || value < 15) { + this.value = 15; + showNotification("El alto no puede ser menor a 15 cm.", 'error'); + } + }); + + document.querySelector('.add-to-cart-btn').addEventListener('click', addToCart); } function toggleSizeOptions(type) { @@ -197,83 +242,108 @@ uploadArea.addEventListener('drop', (e) => { // Función para manejar los archivos - Modificada para soportar múltiples imágenes function handleFiles(e) { - const files = e.target.files; + const files = e.target.files; // Obtiene los archivos seleccionados + const selectedSize = sizePicker.getValue(); // Obtiene el tamaño seleccionado + const lowQualityIndexes = []; // Array para almacenar índices de imágenes de baja calidad - // Si hay archivos, cambiamos el estado visual if (files.length > 0) { - uploadElements.classList.add('has-images'); + uploadElements.classList.add('has-images'); // Añadir clase para mostrar que hay imágenes } + let pending = files.length; // Contador de archivos pendientes de procesar + for (let i = 0; i < files.length; i++) { const file = files[i]; - // Verificar que sea una imagen - if (!file.type.match('image.*')) continue; + // Verificar si el archivo es una imagen + if (!file.type.match('image.*')) { + pending--; // Decrementar el contador de pendientes si no es una imagen + continue; + } - // Generar un ID único para esta imagen - const imageId = 'img_' + Date.now() + '_' + i; + const reader = new FileReader(); // Crear un nueva instancia FileReader para cada archivo - // Almacenar la imagen en la variable global con su ID - uploadedImagesData.push({ - id: imageId, - file: file - }); + reader.onload = function (event) { + const img = new Image(); + img.src = event.target.result; // Asignar la URL de la imagen al objeto Image - console.log("Imagen añadida:", imageId); + img.onload = function () { + const { minWidth, minHeight } = getMinimumPixelsForImage(selectedSize); // Calcular los píxeles mínimos por imagen + const isLowQuality = img.naturalWidth < minWidth || img.naturalHeight < minHeight; // Verificar si la imagen es de baja calidad - const reader = new FileReader(); + const imageId = 'img_' + Date.now() + '_' + i; // Generar un ID único para la imagen - reader.onload = function (event) { - // Crear el elemento de imagen - const imageItem = document.createElement('div'); - imageItem.className = 'image-item'; - imageItem.dataset.imageId = imageId; // Almacenar el ID para referencia - - const img = document.createElement('img'); - img.src = event.target.result; - img.alt = 'Imagen subida'; - - const removeBtn = document.createElement('button'); - removeBtn.className = 'remove-image-btn'; - removeBtn.textContent = 'X'; - removeBtn.addEventListener('click', function (e) { - e.stopPropagation(); // Evitar que el clic se propague al área de upload - - // Eliminar la imagen del array global - uploadedImagesData = uploadedImagesData.filter(img => img.id !== imageId); - console.log("Imagen eliminada:", imageId); - - // Eliminar la vista previa de la imagen - imageItem.remove(); - - // Si no quedan imágenes, restauramos el estado visual - if (imagesGrid.children.length === 0) { - uploadElements.classList.remove('has-images'); + // Almacenar los datos de la imagen en el array global + uploadedImagesData.push({ + id: imageId, + file: file, + lowQuality: isLowQuality, + }); + + // Crear el elemento de imagen en el DOM + const imageItem = document.createElement('div'); + imageItem.className = 'image-item'; + imageItem.dataset.imageId = imageId; // Asignar el ID al elemento + + if (isLowQuality) { + imageItem.classList.add('low-quality'); // Añadir clase para baja calidad + lowQualityIndexes.push(i + 1); // Guarda el índice (basado en 1) de la imagen Posición humana } - // Actualizar el contador de imágenes - updateImageCounter(); + // Crear el elemento de imagen y botón de eliminar + const imgElement = document.createElement('img'); + imgElement.src = event.target.result; + imgElement.alt = 'Imagen subida'; - // Actualizar el precio total - updateTotalPrice(); - }); + // Crea el botón de eliminar imagen + const removeBtn = document.createElement('button'); + removeBtn.className = 'remove-image-btn'; + removeBtn.textContent = 'X'; + removeBtn.addEventListener('click', function (e) { + e.stopPropagation(); // Evitar que el evento de clic se propague al contenedor + uploadedImagesData = uploadedImagesData.filter(img => img.id !== imageId); // Eliminar la imagen del array + imageItem.remove(); // Eliminar el elemento del DOM + if (imagesGrid.children.length === 0) { + uploadElements.classList.remove('has-images'); // Remover clase si no hay imágenes + } + updateImageCounter(); // Actualizar el contador de imágenes + updateTotalPrice(); // Actualizar el precio total + }); + + // Crear la etiqueta de advertencia de baja calidad + const warningLabel = document.createElement('div'); + warningLabel.className = 'quality-warning'; + warningLabel.textContent = '⚠️ Resolución baja'; + if (!isLowQuality) warningLabel.style.display = 'none'; // Ocultar si no es baja calidad + + // Añadir los elementos al contenedor de imágenes + imageItem.appendChild(imgElement); + imageItem.appendChild(warningLabel); + imageItem.appendChild(removeBtn); + imagesGrid.appendChild(imageItem); - imageItem.appendChild(img); - imageItem.appendChild(removeBtn); - imagesGrid.appendChild(imageItem); + updateImageCounter(); // Actualizar el contador de imágenes + updateTotalPrice(); // Actualizar el precio total - // Actualizar el contador de imágenes - updateImageCounter(); + pending--; // Decrementar el contador de pendientes - // Actualizar el precio total - updateTotalPrice(); + // Si todas las imágenes han sido procesadas, mostrar advertencia si hay imágenes de baja calidad + if (pending === 0 && lowQualityIndexes.length > 0) { + const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes + const plural = lowQualityIndexes.length > 1; // Determina si es plural + const message = plural + ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.` + : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.`; + + showNotification(message, 'error'); // Mostrar advertencia al usuario + } + }; }; - reader.readAsDataURL(file); + reader.readAsDataURL(file); // Leer el archivo como URL de datos } - // Resetear el input para permitir subir el mismo archivo otra vez - fileInput.value = ''; + fileInput.value = ''; // Resetea el input para permitir subir el mismo archivo otra vez } // Función para actualizar el contador de imágenes @@ -305,19 +375,21 @@ const PIXELS_PER_CM = 30; // Función para calcular los requisitos mínimos de píxeles según el tamaño de la imagen function getMinimumPixelsForImage(size) { const [widthCm, heightCm] = size.split('X').map(Number); // Convierte "10X15" a [10, 15] - const minWidth = widthCm * PIXELS_PER_CM; - const minHeight = heightCm * PIXELS_PER_CM; - return { minWidth, minHeight }; + const minWidth = widthCm * PIXELS_PER_CM; // Calcula el ancho mínimo en píxeles + const minHeight = heightCm * PIXELS_PER_CM; // Calcula la altura mínima en píxeles + return { minWidth, minHeight }; // Devuelve un objeto con los valores mínimos } // Modificado para incluir el nombre del archivo function analyzeImagePixels(img, imageSize, fileName) { - const { minWidth, minHeight } = getMinimumPixelsForImage(imageSize); + const { minWidth, minHeight } = getMinimumPixelsForImage(imageSize); // Obtener los requisitos mínimos de píxeles - if (img.naturalWidth === 0 || img.naturalHeight === 0) { + // Verificar si la imagen se ha cargado correctamente + if (img.naturalWidth === 0 || img.naturalHeight === 0) { return `La imagen "${fileName}" no se cargó correctamente.`; } + // Verificar si la imagen cumple con los requisitos mínimos if (img.naturalWidth < minWidth || img.naturalHeight < minHeight) { return `La imagen "${fileName}" no tiene suficiente calidad para imprimir en ${imageSize.replace("X", "x")} cm. Debe tener al menos ${minWidth}x${minHeight} píxeles.`; } @@ -325,144 +397,48 @@ function analyzeImagePixels(img, imageSize, fileName) { return null; // No hay error } - -function handleFiles(e) { - const files = e.target.files; - const selectedSize = sizePicker.getValue(); - const lowQualityIndexes = []; - - if (files.length > 0) { - uploadElements.classList.add('has-images'); - } - - let pending = files.length; - - for (let i = 0; i < files.length; i++) { - const file = files[i]; - if (!file.type.match('image.*')) { - pending--; - continue; - } - - const reader = new FileReader(); - - reader.onload = function (event) { - const img = new Image(); - img.src = event.target.result; - - img.onload = function () { - const { minWidth, minHeight } = getMinimumPixelsForImage(selectedSize); - const isLowQuality = img.naturalWidth < minWidth || img.naturalHeight < minHeight; - - const imageId = 'img_' + Date.now() + '_' + i; - - uploadedImagesData.push({ - id: imageId, - file: file, - lowQuality: isLowQuality, - }); - - const imageItem = document.createElement('div'); - imageItem.className = 'image-item'; - imageItem.dataset.imageId = imageId; - - if (isLowQuality) { - imageItem.classList.add('low-quality'); - lowQualityIndexes.push(i + 1); // Posición humana - } - - const imgElement = document.createElement('img'); - imgElement.src = event.target.result; - imgElement.alt = 'Imagen subida'; - - const removeBtn = document.createElement('button'); - removeBtn.className = 'remove-image-btn'; - removeBtn.textContent = 'X'; - removeBtn.addEventListener('click', function (e) { - e.stopPropagation(); - uploadedImagesData = uploadedImagesData.filter(img => img.id !== imageId); - imageItem.remove(); - if (imagesGrid.children.length === 0) { - uploadElements.classList.remove('has-images'); - } - updateImageCounter(); - updateTotalPrice(); - }); - - const warningLabel = document.createElement('div'); - warningLabel.className = 'quality-warning'; - warningLabel.textContent = '⚠️ Resolución baja'; - if (!isLowQuality) warningLabel.style.display = 'none'; - - imageItem.appendChild(imgElement); - imageItem.appendChild(warningLabel); - imageItem.appendChild(removeBtn); - imagesGrid.appendChild(imageItem); - - updateImageCounter(); - updateTotalPrice(); - - pending--; - - if (pending === 0 && lowQualityIndexes.length > 0) { - const imagesStr = lowQualityIndexes.join(', '); - const plural = lowQualityIndexes.length > 1; - const message = plural - ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.` - : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.`; - - showNotification(message, 'error'); - } - }; - }; - - reader.readAsDataURL(file); - } - - fileInput.value = ''; -} - +// Functión para revalidar imágenes con el nuevo tamaño seleccionado function revalidateImagesWithNewSize(newSize) { - const { minWidth, minHeight } = getMinimumPixelsForImage(newSize); - const lowQualityIndexes = []; + const { minWidth, minHeight } = getMinimumPixelsForImage(newSize); // Obtener los nuevos requisitos mínimos + const lowQualityIndexes = []; // Array para almacenar índices de imágenes de baja calidad // Recorre las imágenes en el orden en que aparecen en el grid const imageItems = Array.from(imagesGrid.querySelectorAll('.image-item')); imageItems.forEach((item, index) => { - const imageId = item.dataset.imageId; - const data = uploadedImagesData.find(img => img.id === imageId); - const imgElement = item.querySelector('img'); - const warningLabel = item.querySelector('.quality-warning'); + const imageId = item.dataset.imageId; // Obtener el ID de la imagen + const data = uploadedImagesData.find(img => img.id === imageId); // Buscar en el array de datos + const imgElement = item.querySelector('img'); // Obtener el elemento de imagen + const warningLabel = item.querySelector('.quality-warning'); // Obtener la etiqueta de advertencia - const tempImg = new Image(); - tempImg.src = imgElement.src; + const tempImg = new Image(); // Crear un nuevo objeto Image para cargar la imagen + tempImg.src = imgElement.src; // Asignar la URL de la imagen al objeto Image tempImg.onload = () => { const isLowQuality = tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; - // Actualiza en el array de datos + // Actualiza el estado de calidad en el array global data.lowQuality = isLowQuality; // Modifica clases visuales y etiqueta if (isLowQuality) { item.classList.add('low-quality'); - if (warningLabel) warningLabel.style.display = 'block'; + if (warningLabel) warningLabel.style.display = 'block'; // Mostrar advertencia lowQualityIndexes.push(index + 1); // Índice basado en el orden del grid } else { item.classList.remove('low-quality'); - if (warningLabel) warningLabel.style.display = 'none'; + if (warningLabel) warningLabel.style.display = 'none'; // Ocultar advertencia } - // Después de terminar todas, mostrar advertencia si hay + // Si es la última imagen y hay imágenes de baja calidad, muestra una notificación if (index === imageItems.length - 1 && lowQualityIndexes.length > 0) { - const plural = lowQualityIndexes.length > 1; - const imagesStr = lowQualityIndexes.join(', '); + const plural = lowQualityIndexes.length > 1; // Determina si es plural + const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes const message = plural ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.` : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.`; - showNotification(message, 'error'); + showNotification(message, 'error'); // Mostrar advertencia al usuario } }; }); diff --git a/web-form-plugin.php b/web-form-plugin.php index 8f1b553..da2b4bf 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 2.8 +Version: 2.9 Author: Enmanuel */ @@ -37,7 +37,7 @@ function web_form_shortcode() $picker_path = plugin_dir_url(__FILE__) . 'ios-picker.js'; // Cargar CSS - wp_enqueue_style('web-form-styles', $css_path, array(), '5.0'); + wp_enqueue_style('web-form-styles', $css_path, array(), '5.1'); // Cargar Swiper CSS desde CDN wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); @@ -46,7 +46,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.3', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.4', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From d737c284fc578755de4c1a4408b9cc2c44530198 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 17 Apr 2025 09:51:49 -0500 Subject: [PATCH 03/72] =?UTF-8?q?integraci=C3=B3n=20de=20zoom=20a=20marcos?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.php | 16 ++++++++++++---- main.js | 5 +++++ web-form-plugin.php | 4 ++-- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/index.php b/index.php index 57770ae..89597f6 100644 --- a/index.php +++ b/index.php @@ -87,16 +87,24 @@
- marco1 +
+ marco1 +
- marco2 +
+ marco2 +
- marco3 +
+ marco3 +
- marco4 +
+ marco4 +
diff --git a/main.js b/main.js index 6ac9394..8fce4ee 100644 --- a/main.js +++ b/main.js @@ -175,6 +175,11 @@ function toggleFrameOptions(show) { if (show) { if (!window.mySwiper) { window.mySwiper = new Swiper('.mySwiper', { + zoom: true, + zoom: { + maxRatio: 2, + minRatio: 1, + }, effect: 'coverflow', grabCursor: true, centeredSlides: true, diff --git a/web-form-plugin.php b/web-form-plugin.php index da2b4bf..1fe3139 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 2.9 +Version: 3.0.1 Author: Enmanuel */ @@ -46,7 +46,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.4', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.1', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From 8625b960c9517b321f8266ecedf801136f09d935 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 17 Apr 2025 11:46:15 -0500 Subject: [PATCH 04/72] Alerta personalizada de limite de comentarios --- index.php | 2 +- ios-picker.js | 17 ++++++++--------- main.js | 14 ++++++++++++++ web-form-plugin.php | 6 +++--- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/index.php b/index.php index 89597f6..7d1e58c 100644 --- a/index.php +++ b/index.php @@ -67,7 +67,7 @@ Comentarios adicionales
- +
diff --git a/ios-picker.js b/ios-picker.js index 9d31832..b37d5c1 100644 --- a/ios-picker.js +++ b/ios-picker.js @@ -70,14 +70,13 @@ pointer-events: none; } - - .picker-title { - text-align: center; - margin-bottom: 20px; - font-size: 20px; - font-weight: 600; - color: var(--text-color); - } + // .picker-title { + // text-align: center; + // margin-bottom: 20px; + // font-size: 20px; + // font-weight: 600; + // color: var(--text-color); + // } .picker-wrapper { display: 20px; @@ -140,7 +139,7 @@ this.carouselContainer.appendChild(highlight); this.carouselContainer.appendChild(this.carouselTrack); - this.pickerWrapper.appendChild(this.titleElement); + // this.pickerWrapper.appendChild(this.titleElement); this.pickerWrapper.appendChild(this.carouselContainer); if (this.options.container) { diff --git a/main.js b/main.js index 8fce4ee..f89d1c3 100644 --- a/main.js +++ b/main.js @@ -20,6 +20,7 @@ document.addEventListener('DOMContentLoaded', function () { initializeSizePicker(); initializeFramePicker(); setupEventListeners(); + setupCommentLengthChecker(); // Initialize image counter const counterElement = document.createElement('div'); @@ -73,6 +74,19 @@ function initializeFramePicker() { }); } +function setupCommentLengthChecker() { + const commentArea = document.querySelector(".comment-area"); + + commentArea.addEventListener('input', function () { + const maxLength = 255; + const currentLength = this.value.length; + + if (currentLength >= maxLength) { + showNotification('Has alcanzado el límite máximo de 255 caracteres en los comentarios.', 'error'); + } + }); +} + function setupEventListeners() { const quantityInput = document.querySelector('.quantity-field input'); diff --git a/web-form-plugin.php b/web-form-plugin.php index 1fe3139..9f37e79 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.1 +Version: 3.0.2 Author: Enmanuel */ @@ -43,10 +43,10 @@ function web_form_shortcode() wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); // Cargar IOS-Picker - wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0', true); + wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.1', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.2', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From c25bd13240e4bed2c27cc4f128f733c6bc0d1b09 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 17 Apr 2025 20:45:17 -0500 Subject: [PATCH 05/72] Ajustes responsive de contenedores --- styles.css | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/styles.css b/styles.css index 6fdf544..9d6b431 100644 --- a/styles.css +++ b/styles.css @@ -158,7 +158,7 @@ body { } .comment-area { - width: calc(100% - 10px); + width: calc(95% - 10px); min-height: 80px; padding: 20px; border: 1px solid #dee2e6; @@ -259,7 +259,7 @@ body { } .custom-size { - width: 100%; + width: 110%; padding: 0; margin: 0 auto; } @@ -294,6 +294,13 @@ body { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); } + .custom-size { + width: 110%; + padding: 0; + margin: 0 auto; + } + + .frames { padding: 0 30px; } From fceaaa144709c2b46377a987814b531ecc52e2c1 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 18 Apr 2025 14:07:05 -0500 Subject: [PATCH 06/72] Mostrar precio real en carrito de Woocommerce --- index.php | 16 ++--- main.js | 157 ++++++++++++++++++++++++++++++++++++++++---- web-form-plugin.php | 30 ++++++++- 3 files changed, 176 insertions(+), 27 deletions(-) diff --git a/index.php b/index.php index 7d1e58c..81ddeb5 100644 --- a/index.php +++ b/index.php @@ -87,24 +87,16 @@
-
- marco1 -
+ marco1
-
- marco2 -
+ marco2
-
- marco3 -
+ marco3
-
- marco4 -
+ marco4
diff --git a/main.js b/main.js index f89d1c3..2f7087c 100644 --- a/main.js +++ b/main.js @@ -15,7 +15,6 @@ const sizePrices = { "30X60": 45.00, }; - document.addEventListener('DOMContentLoaded', function () { initializeSizePicker(); initializeFramePicker(); @@ -49,7 +48,6 @@ function initializeSizePicker() { items: sizeOptions, initialValue: '10X15', onChange: (value) => { - console.log('Size picker changed to:', value); revalidateImagesWithNewSize(value); // Revalidar las imágenes con el nuevo tamaño updateTotalPrice(); // Actualizar el precio total } @@ -63,10 +61,11 @@ function initializeFramePicker() { const pickerItems = framePicker.querySelectorAll('.swiper-slide'); pickerItems.forEach(item => { item.addEventListener('click', function () { - if (uploadedImagesData.length === 0) { - showNotification('Sube una imagen primero', 'error'); - return; - } + // Verificar si hay imágenes subidas antes de permitir la selección del marco + // if (uploadedImagesData.length === 0) { + // showNotification('Sube una imagen primero', 'error'); + // return; + // } pickerItems.forEach(i => i.classList.remove('selected')); item.classList.add('selected'); currentSelectedFrame = item.dataset.value; @@ -80,7 +79,7 @@ function setupCommentLengthChecker() { commentArea.addEventListener('input', function () { const maxLength = 255; const currentLength = this.value.length; - + if (currentLength >= maxLength) { showNotification('Has alcanzado el límite máximo de 255 caracteres en los comentarios.', 'error'); } @@ -90,6 +89,7 @@ function setupCommentLengthChecker() { function setupEventListeners() { const quantityInput = document.querySelector('.quantity-field input'); + quantityInput.addEventListener('focus', function () { if (this.value === '1') { this.value = ''; @@ -102,6 +102,7 @@ function setupEventListeners() { // Si es mayor a 100, vuelve al mismo valor if (value > 100) { this.value = 100; + showNotification('La cantidad no puede ser mayor a 100 unidades', 'error'); } updateTotalPrice(); @@ -112,6 +113,7 @@ function setupEventListeners() { if (isNaN(value) || value < 1) { this.value = 1; + } }); @@ -171,6 +173,7 @@ function handleCustomSize() { document.querySelector('.add-to-cart-btn').addEventListener('click', addToCart); } +// Función para alternar entre opciones de tamaño estándar y personalizado function toggleSizeOptions(type) { if (type === 'standard') { document.getElementById('standard-sizes').style.display = 'block'; @@ -183,17 +186,49 @@ function toggleSizeOptions(type) { } } +// Función para inicializar el visor de imágenes (Viewer.js) para las imágenes del Swiper +function initializeViewerForSwiper() { + // Selecciona todas las imágenes dentro de las diapositivas del Swiper + const swiperImages = document.querySelectorAll('.swiper-slide img'); + + // Agrega un evento de clic a cada imagen + swiperImages.forEach((img) => { + img.addEventListener('click', function () { + // Inicializa Viewer.js para la imagen seleccionada + const viewer = new Viewer(img, { + inline: false, // Mostrar como modal + navbar: false, // Ocultar barra de navegación + toolbar: { + zoomIn: true, // Habilitar zoom in + zoomOut: true, // Habilitar zoom out + oneToOne: false, // Deshabilitar botón "1:1" + reset: false, // Deshabilitar botón "Reset" + prev: false, // Deshabilitar botón "Anterior" + play: false, // Deshabilitar botón "Play" + next: false, // Deshabilitar botón "Siguiente" + rotateLeft: false, // Deshabilitar botón "Rotar izquierda" + rotateRight: false, // Deshabilitar botón "Rotar derecha" + flipHorizontal: false, // Deshabilitar botón "Voltear horizontal" + flipVertical: false, // Deshabilitar botón "Voltear vertical" + }, + title: false, // Ocultar título + viewed() { + viewer.zoomTo(1); // Ajustar el zoom inicial + }, + }); + + // Abre el visor + viewer.show(); + }); + }); +} + function toggleFrameOptions(show) { document.getElementById('frame-ios-picker-wrapper').style.display = show ? 'block' : 'none'; if (show) { if (!window.mySwiper) { window.mySwiper = new Swiper('.mySwiper', { - zoom: true, - zoom: { - maxRatio: 2, - minRatio: 1, - }, effect: 'coverflow', grabCursor: true, centeredSlides: true, @@ -209,6 +244,9 @@ function toggleFrameOptions(show) { }); } } + + // Inicializa Viewer.js para las imágenes del Swiper + initializeViewerForSwiper(); } // Variables globales para almacenar las imágenes @@ -262,7 +300,22 @@ uploadArea.addEventListener('drop', (e) => { // Función para manejar los archivos - Modificada para soportar múltiples imágenes function handleFiles(e) { const files = e.target.files; // Obtiene los archivos seleccionados + const isCustomSize = selectedOptionSize === 'custom'; // Verifica si el tamaño es personalizado const selectedSize = sizePicker.getValue(); // Obtiene el tamaño seleccionado + + // Si es personalizado, obtiene los valores actuales de ancho y alto + let customWidth, customHeight; + if (isCustomSize) { + customWidth = parseInt(document.getElementById('custom-size__width').value); + customHeight = parseInt(document.getElementById('custom-size__height').value); + + // Si los valores personalizados no son válidos, muestra una advertencia y detiene el proceso + if (isNaN(customWidth) || isNaN(customHeight)) { + showNotification("Por favor, ingresa un ancho y alto válidos para el tamaño personalizado.", "error"); + return; + } + } + const lowQualityIndexes = []; // Array para almacenar índices de imágenes de baja calidad if (files.length > 0) { @@ -399,12 +452,77 @@ function getMinimumPixelsForImage(size) { return { minWidth, minHeight }; // Devuelve un objeto con los valores mínimos } +// Nueva función para validar imágenes con tamaños personalizados +function validateImagesWithCustomSize(customWidth, customHeight) { + const minWidth = customWidth * PIXELS_PER_CM; // Ancho mínimo en píxeles + console.log(`Ancho mínimo: ${minWidth}`); + const minHeight = customHeight * PIXELS_PER_CM; // Alto mínimo en píxeles + console.log(`Alto mínimo: ${minHeight}`); + const lowQualityIndexes = []; // Índices de imágenes de baja calidad + + // Recorre las imágenes subidas + uploadedImagesData.forEach((imageData, index) => { + const imgElement = imagesGrid.querySelector(`[data-image-id="${imageData.id}"] img`); + const warningLabel = imagesGrid.querySelector(`[data-image-id="${imageData.id}"] .quality-warning`); + + if (imgElement) { + const tempImg = new Image(); + tempImg.src = imgElement.src; + + tempImg.onload = () => { + const isLowQuality = tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; + + // Actualiza el estado de calidad en el array global + imageData.lowQuality = isLowQuality; + + // Modifica clases visuales y etiqueta + if (isLowQuality) { + lowQualityIndexes.push(index + 1); // Índice basado en 1 + warningLabel.style.display = 'block'; // Mostrar advertencia + } else { + warningLabel.style.display = 'none'; // Ocultar advertencia + } + }; + } + }); + + // Mostrar notificación si hay imágenes de baja calidad + if (lowQualityIndexes.length > 0) { + const imagesStr = lowQualityIndexes.join(', '); + const plural = lowQualityIndexes.length > 1; + const message = plural + ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.` + : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.`; + + showNotification(message, 'error'); + } +} + +// Modificar el evento de entrada para tamaños personalizados +document.getElementById("custom-size__width").addEventListener("input", function () { + const customWidth = parseInt(this.value); + const customHeight = parseInt(document.getElementById("custom-size__height").value); + + if (!isNaN(customWidth) && !isNaN(customHeight)) { + validateImagesWithCustomSize(customWidth, customHeight); + } +}); + +document.getElementById("custom-size__height").addEventListener("input", function () { + const customWidth = parseInt(document.getElementById("custom-size__width").value); + const customHeight = parseInt(this.value); + + if (!isNaN(customWidth) && !isNaN(customHeight)) { + validateImagesWithCustomSize(customWidth, customHeight); + } +}); + // Modificado para incluir el nombre del archivo function analyzeImagePixels(img, imageSize, fileName) { const { minWidth, minHeight } = getMinimumPixelsForImage(imageSize); // Obtener los requisitos mínimos de píxeles // Verificar si la imagen se ha cargado correctamente - if (img.naturalWidth === 0 || img.naturalHeight === 0) { + if (img.naturalWidth === 0 || img.naturalHeight === 0) { return `La imagen "${fileName}" no se cargó correctamente.`; } @@ -463,6 +581,14 @@ function revalidateImagesWithNewSize(newSize) { }); } +function getSelectedSizePrice() { + if (initializeSizePicker) { + console.log("🚀 initializeSizePicker() fue llamada correctamente"); + } + const selectedSize = sizePicker.getValue(); // Obtener el tamaño seleccionado + return sizePrices[selectedSize] || 0; // Retornar el precio correspondiente +} + // Función para añadir al carrito - Modificada para procesar múltiples imágenes de forma secuencial function addToCart() { console.log("🚀 addToCart() fue llamada correctamente"); @@ -489,8 +615,12 @@ function addToCart() { size = `${customWidth}x${customHeight}`; } else { size = sizePicker.getValue(); + pricePerUnit = getSelectedSizePrice(); // Obtener el precio del tamaño seleccionado } + // const totalPrice = pricePerUnit * quantity * uploadedImagesData.length; // Calcular el precio total + // console.log(`💰 Precio total: S/. ${totalPrice.toFixed(2)}`); + const comments = document.querySelector('.comment-area').value; const withFrame = document.querySelector('input[name="frame"]:checked').value !== 'sin-marco'; let frame = 'sin-marco'; @@ -535,6 +665,7 @@ function addToCart() { formData.append('image_data', imageData.file); formData.append('quantity', quantity); formData.append('size', size); + formData.append('price', pricePerUnit); // Precio unitario formData.append('is_custom_size', isCustomSize); formData.append('custom_width', customWidth || 0); formData.append('custom_height', customHeight || 0); diff --git a/web-form-plugin.php b/web-form-plugin.php index 9f37e79..faec275 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.2 +Version: 3.0.4 Author: Enmanuel */ @@ -42,15 +42,21 @@ function web_form_shortcode() // Cargar Swiper CSS desde CDN wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); + // Cargar Viewer CSS desde CDN + wp_enqueue_style('viewer-css', 'https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.7/viewer.css', array(), '1.11.7'); + // Cargar IOS-Picker wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.2', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.6', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); + // Cargar Viewer JS desde CDN + wp_enqueue_script('viewer-js', 'https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.7/viewer.min.js', array('jquery'), '1.11.7', true); + // Aumentar límites para POST y uploads @ini_set('post_max_size', '64M'); @ini_set('upload_max_filesize', '32M'); @@ -88,6 +94,7 @@ function process_print_image_to_cart() $quantity = isset($_POST['quantity']) ? intval($_POST['quantity']) : 1; $size = isset($_POST['size']) ? sanitize_text_field($_POST['size']) : ''; + $price = isset($_POST['price']) ? floatval($_POST['price']) : 0.0; $comments = isset($_POST['comments']) ? sanitize_textarea_field($_POST['comments']) : ''; $frame = isset($_POST['frame']) ? sanitize_text_field($_POST['frame']) : 'sin-marco'; $custom_width = isset($_POST['custom_width']) ? intval($_POST['custom_width']) : 0; @@ -154,6 +161,7 @@ function process_print_image_to_cart() 'print_image_data' => array( 'image_url' => esc_url($image_url), 'size' => $is_custom_size ? "{$custom_width}x{$custom_height}" : $size, + 'price' => $price, 'is_custom_size' => $is_custom_size, 'frame' => $frame, 'comments' => $comments, @@ -233,6 +241,24 @@ function display_cart_product($product_images, $cart_item, $cart_item_key) add_filter('woocommerce_store_api_cart_item_images', 'display_cart_product', 10, 3); +// Actualizar el precio del producto en el carrito +function update_cart_item_price($cart) +{ + // Evitar que se ejecute varias veces + if (did_action('woocommerce_before_calculate_totals') >= 2) { + return; + } + + // Iterar sobre los productos en el carrito + foreach ($cart->get_cart() as $cart_item) { + if (isset($cart_item['print_image_data']['price'])) { + $custom_price = floatval($cart_item['print_image_data']['price']); // Precio personalizado + $cart_item['data']->set_price($custom_price); // Actualizar el precio del producto + } + } +} +add_action('woocommerce_before_calculate_totals', 'update_cart_item_price', 10, 1); + function display_print_image_cart_item_data($item_data, $cart_item) { if (isset($cart_item['print_image_data'])) { From 8588a36dd524a4977618922c7db7adbd859492c7 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Mon, 21 Apr 2025 12:00:08 -0500 Subject: [PATCH 07/72] =?UTF-8?q?Cambio=20de=20thumbnail=20en=20admin=20or?= =?UTF-8?q?der=20y=20asignar=20un=20id=20unico=20a=20la=20imagen,=20adem?= =?UTF-8?q?=C3=A1s=20de=20mostrar=20la=20URL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- web-form-plugin.php | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/web-form-plugin.php b/web-form-plugin.php index faec275..d5cc4f1 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -159,6 +159,7 @@ function process_print_image_to_cart() // Guardar la imagen en los metadatos del carrito $cart_item_data = array( 'print_image_data' => array( + 'id'=> $filename, // ID único para la imagen 'image_url' => esc_url($image_url), 'size' => $is_custom_size ? "{$custom_width}x{$custom_height}" : $size, 'price' => $price, @@ -240,6 +241,31 @@ function display_cart_product($product_images, $cart_item, $cart_item_key) } add_filter('woocommerce_store_api_cart_item_images', 'display_cart_product', 10, 3); +function reemplazar_thumbnail_por_print_image($thumbnail, $item) { + // Si $item es un integer, se asume que es el ID del item, por lo que cargamos el objeto + if (is_int($item)) { + error_log('ℹ️ Recibido ID de item: ' . $item); + $item = new WC_Order_Item_Product($item); + } + + // Verificar que ahora sí es un objeto válido + if ($item instanceof WC_Order_Item_Product) { + $print_image_url = $item->get_meta('URL', true); + error_log('🖼️ Metadato _print_image_url: ' . $print_image_url); + + if (!empty($print_image_url)) { + $thumbnail = 'Imagen personalizada'; + } + } else { + // Aún no es el objeto esperado, log más detallado + $tipo = is_object($item) ? get_class($item) : gettype($item); + error_log('⚠️ Tipo de $item inesperado después de conversión: ' . $tipo); + } + + return $thumbnail; +} +add_filter('woocommerce_admin_order_item_thumbnail', 'reemplazar_thumbnail_por_print_image', 10, 2); + // Actualizar el precio del producto en el carrito function update_cart_item_price($cart) @@ -308,7 +334,10 @@ function save_print_image_order_item_meta($item, $cart_item_key, $values, $order } // Guardar la URL de la imagen para referencia - $item->add_meta_data('_print_image_url', $print_data['image_url'], true); + $item->add_meta_data('URL', $print_data['image_url'], true); + + // Guardar el ID único de la imagen + $item->add_meta_data('ID', $print_data['id'], true); } } add_action('woocommerce_checkout_create_order_line_item', 'save_print_image_order_item_meta', 10, 4); From ebeab70ce4bd4af825b7606b43297f6c30d2534b Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Tue, 22 Apr 2025 10:50:43 -0500 Subject: [PATCH 08/72] =?UTF-8?q?Cambio=20de=20dise=C3=B1o=20de=20toolbar?= =?UTF-8?q?=20de=20viewerjs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 12 ++++++++++-- styles.css | 12 ++++++++++++ web-form-plugin.php | 6 +++--- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/main.js b/main.js index 2f7087c..eafd083 100644 --- a/main.js +++ b/main.js @@ -199,8 +199,16 @@ function initializeViewerForSwiper() { inline: false, // Mostrar como modal navbar: false, // Ocultar barra de navegación toolbar: { - zoomIn: true, // Habilitar zoom in - zoomOut: true, // Habilitar zoom out + zoomIn: { + show: true, // Habilitar zoom in + title: 'Zoom In', + size: 'large', + }, // Habilitar zoom in + zoomOut: { + show: true, // Habilitar zoom out + title: 'Zoom Out', + size: 'large', + }, // Habilitar zoom out oneToOne: false, // Deshabilitar botón "1:1" reset: false, // Deshabilitar botón "Reset" prev: false, // Deshabilitar botón "Anterior" diff --git a/styles.css b/styles.css index 9d6b431..3288631 100644 --- a/styles.css +++ b/styles.css @@ -572,4 +572,16 @@ input.error, textarea.error { font-size: 14px; max-width: 80%; } +} + +.viewer-zoom-in { + background-color: #f7703a !important; +} + +.viewer-zoom-out { + background-color: #f7703a !important; +} + +.viewer-close { + background-color: #af1b1b !important; } \ No newline at end of file diff --git a/web-form-plugin.php b/web-form-plugin.php index d5cc4f1..8f26a23 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.4 +Version: 3.0.5 Author: Enmanuel */ @@ -37,7 +37,7 @@ function web_form_shortcode() $picker_path = plugin_dir_url(__FILE__) . 'ios-picker.js'; // Cargar CSS - wp_enqueue_style('web-form-styles', $css_path, array(), '5.1'); + wp_enqueue_style('web-form-styles', $css_path, array(), '5.1.4'); // Cargar Swiper CSS desde CDN wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); @@ -49,7 +49,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.6', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.9', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From 79f0a52b94a084ccd1908c126b90452df431f030 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 23 Apr 2025 10:50:50 -0500 Subject: [PATCH 09/72] Descuentos por cantidades de imagenes --- main.js | 91 ++++++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 74 insertions(+), 17 deletions(-) diff --git a/main.js b/main.js index eafd083..27c21c9 100644 --- a/main.js +++ b/main.js @@ -3,6 +3,8 @@ var selectedOptionSize; let currentSelectedFrame = 'frame1'; // Valor por defecto para el marco let uploadedImagesData = []; // Array para almacenar múltiples imágenes let sizePicker; +let selectedSize; +let currentSizePrices; const sizePrices = { "10X15": 5.00, @@ -15,6 +17,30 @@ const sizePrices = { "30X60": 45.00, }; +// Precios para cantidades mayores o iguales a 4 +const bulkSizePrices = { + "10X15": 3.00, + "13X18": 5.00, + "15X21": 6.00, + "20X25": 13.00, + "20X30": 15.00, + "25X38": 20.00, + "30X45": 30.00, + "30X60": 40.00, +}; + +// Precios para cantidades mayores o iguales a 12 +const bulkSizePrices12 = { + "10X15": 2.50, + "13X18": 4.00, + "15X21": 5.00, + "20X25": 10.00, + "20X30": 12.00, + "25X38": 18.00, + "30X45": 25.00, + "30X60": 35.00, +}; + document.addEventListener('DOMContentLoaded', function () { initializeSizePicker(); initializeFramePicker(); @@ -38,22 +64,53 @@ document.addEventListener('DOMContentLoaded', function () { }); function initializeSizePicker() { - const sizeOptions = Object.keys(sizePrices).map(value => ({ - value: value, - label: `${value.replace("X", "cm Ancho X ")}cm Alto => (S/.${sizePrices[value].toFixed(2)})` - })); - - sizePicker = new iOSPicker({ - container: document.getElementById('size-picker'), - items: sizeOptions, - initialValue: '10X15', - onChange: (value) => { - revalidateImagesWithNewSize(value); // Revalidar las imágenes con el nuevo tamaño - updateTotalPrice(); // Actualizar el precio total + const quantityInput = document.querySelector('.quantity-field input'); + const sizePickerContainer = document.getElementById('size-picker'); + + function getCurrentSizePrices(quantity) { + return quantity >= 12 ? bulkSizePrices12 : (quantity >= 4 ? bulkSizePrices : sizePrices); + } + + function updateSizeOptions(quantity) { + currentSizePrices = getCurrentSizePrices(quantity); + const sizeOptions = Object.keys(currentSizePrices).map(value => ({ + value: value, + label: `${value.replace("X", "cm Ancho X ")}cm Alto => (S/.${currentSizePrices[value].toFixed(2)})` + })); + + // Guardar el valor actual antes de destruir + const previousValue = selectedSize; + + // Si ya existe, destruir el picker + if (sizePicker) { + sizePicker.destroy(); } + + // Crear el picker con el valor anterior si existe, si no, usar el inicial + sizePicker = new iOSPicker({ + container: sizePickerContainer, + items: sizeOptions, + initialValue: previousValue, + onChange: (value) => { + selectedSize = value; // Guardar nuevo valor seleccionado + revalidateImagesWithNewSize(value); + updateTotalPrice(); + } + }); + } + + // Inicializar con cantidad 1 + updateSizeOptions(1); + + // Escuchar cambios en cantidad + quantityInput.addEventListener('input', function () { + const quantity = parseInt(this.value) || 1; + console.log(`Cantidad ingresada: ${quantity}`); + updateSizeOptions(quantity); }); } + function initializeFramePicker() { const framePicker = document.getElementById('frame-picker'); if (!framePicker) return; @@ -309,7 +366,7 @@ uploadArea.addEventListener('drop', (e) => { function handleFiles(e) { const files = e.target.files; // Obtiene los archivos seleccionados const isCustomSize = selectedOptionSize === 'custom'; // Verifica si el tamaño es personalizado - const selectedSize = sizePicker.getValue(); // Obtiene el tamaño seleccionado + selectedSize = sizePicker.getValue(); // Obtiene el tamaño seleccionado // Si es personalizado, obtiene los valores actuales de ancho y alto let customWidth, customHeight; @@ -593,8 +650,8 @@ function getSelectedSizePrice() { if (initializeSizePicker) { console.log("🚀 initializeSizePicker() fue llamada correctamente"); } - const selectedSize = sizePicker.getValue(); // Obtener el tamaño seleccionado - return sizePrices[selectedSize] || 0; // Retornar el precio correspondiente + selectedSize = sizePicker.getValue(); // Obtener el tamaño seleccionado + return currentSizePrices[selectedSize] || 0; // Retornar el precio correspondiente } // Función para añadir al carrito - Modificada para procesar múltiples imágenes de forma secuencial @@ -816,12 +873,12 @@ function updateTotalPrice() { const quantity = parseInt(document.querySelector('.quantity-field input').value) || 1; // Use the sizePicker instance to get the value instead of DOM selection - const selectedSize = sizePicker.getValue(); + selectedSize = sizePicker.getValue(); let pricePerUnit = 0; if (selectedSize) { - pricePerUnit = sizePrices[selectedSize] || 0; + pricePerUnit = currentSizePrices[selectedSize] || 0; } // Calculate the total price considering all images From 52a3491e6b709ce810e5e158779756343d4dc190 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 23 Apr 2025 11:59:48 -0500 Subject: [PATCH 10/72] Icono para zoom en marcos y desactivacion total de alertas de validacion de pixeles --- index.php | 20 ++++++ main.js | 154 +++++++++++++++++++++++++------------------- styles.css | 13 ++++ web-form-plugin.php | 4 +- 4 files changed, 123 insertions(+), 68 deletions(-) diff --git a/index.php b/index.php index 81ddeb5..28b33ad 100644 --- a/index.php +++ b/index.php @@ -88,15 +88,35 @@
marco1 +
marco2 +
marco3 +
marco4 +
diff --git a/main.js b/main.js index 27c21c9..cf670a3 100644 --- a/main.js +++ b/main.js @@ -245,46 +245,68 @@ function toggleSizeOptions(type) { // Función para inicializar el visor de imágenes (Viewer.js) para las imágenes del Swiper function initializeViewerForSwiper() { - // Selecciona todas las imágenes dentro de las diapositivas del Swiper - const swiperImages = document.querySelectorAll('.swiper-slide img'); - - // Agrega un evento de clic a cada imagen - swiperImages.forEach((img) => { - img.addEventListener('click', function () { - // Inicializa Viewer.js para la imagen seleccionada - const viewer = new Viewer(img, { - inline: false, // Mostrar como modal - navbar: false, // Ocultar barra de navegación - toolbar: { - zoomIn: { - show: true, // Habilitar zoom in - title: 'Zoom In', - size: 'large', - }, // Habilitar zoom in - zoomOut: { - show: true, // Habilitar zoom out - title: 'Zoom Out', - size: 'large', - }, // Habilitar zoom out - oneToOne: false, // Deshabilitar botón "1:1" - reset: false, // Deshabilitar botón "Reset" - prev: false, // Deshabilitar botón "Anterior" - play: false, // Deshabilitar botón "Play" - next: false, // Deshabilitar botón "Siguiente" - rotateLeft: false, // Deshabilitar botón "Rotar izquierda" - rotateRight: false, // Deshabilitar botón "Rotar derecha" - flipHorizontal: false, // Deshabilitar botón "Voltear horizontal" - flipVertical: false, // Deshabilitar botón "Voltear vertical" - }, - title: false, // Ocultar título - viewed() { - viewer.zoomTo(1); // Ajustar el zoom inicial - }, - }); + const slides = document.querySelectorAll('.swiper-slide'); - // Abre el visor - viewer.show(); - }); + slides.forEach((slide) => { + const img = slide.querySelector('img'); // Busca la imagen dentro del slide + const btn = slide.querySelector('.zoom-icon-btn'); //Busca el botón con clase .zoom-icon-btn dentro del slide + + if (btn && img) { + let viewerInstance = null; + // Agrega un evento de clic al botón de zoom + btn.addEventListener('click', function (event) { + event.stopPropagation(); + + // Evita crear múltiples viewers + if (viewerInstance) { + viewerInstance.destroy(); + } + + // Crea un contenedor temporal solo para zoom + const clone = img.cloneNode(true); + const tempContainer = document.createElement('div'); + tempContainer.style.display = 'none'; + tempContainer.appendChild(clone); + document.body.appendChild(tempContainer); + + viewerInstance = new Viewer(clone, { + inline: false, + navbar: false, + title: false, + toolbar: { + zoomIn: { // Habilitar zoom in + show: true, + title: 'Zoom In', + size: 'large' + }, + zoomOut: { // Habilitar zoom out + show: true, + title: 'Zoom Out', + size: 'large' + }, + oneToOne: false, + reset: false, + prev: false, + play: false, + next: false, + rotateLeft: false, + rotateRight: false, + flipHorizontal: false, + flipVertical: false, + }, + viewed() { + viewerInstance.zoomTo(1); + }, + hidden() { + viewerInstance.destroy(); + tempContainer.remove(); + viewerInstance = null; + } + }); + // Muestra el visor + viewerInstance.show(); + }); + } }); } @@ -465,15 +487,15 @@ function handleFiles(e) { pending--; // Decrementar el contador de pendientes // Si todas las imágenes han sido procesadas, mostrar advertencia si hay imágenes de baja calidad - if (pending === 0 && lowQualityIndexes.length > 0) { - const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes - const plural = lowQualityIndexes.length > 1; // Determina si es plural - const message = plural - ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.` - : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.`; - - showNotification(message, 'error'); // Mostrar advertencia al usuario - } + // if (pending === 0 && lowQualityIndexes.length > 0) { + // const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes + // const plural = lowQualityIndexes.length > 1; // Determina si es plural + // const message = plural + // ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.` + // : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.`; + + // showNotification(message, 'error'); // Mostrar advertencia al usuario + // } }; }; @@ -552,15 +574,15 @@ function validateImagesWithCustomSize(customWidth, customHeight) { }); // Mostrar notificación si hay imágenes de baja calidad - if (lowQualityIndexes.length > 0) { - const imagesStr = lowQualityIndexes.join(', '); - const plural = lowQualityIndexes.length > 1; - const message = plural - ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.` - : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.`; - - showNotification(message, 'error'); - } + // if (lowQualityIndexes.length > 0) { + // const imagesStr = lowQualityIndexes.join(', '); + // const plural = lowQualityIndexes.length > 1; + // const message = plural + // ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.` + // : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.`; + + // showNotification(message, 'error'); + // } } // Modificar el evento de entrada para tamaños personalizados @@ -633,15 +655,15 @@ function revalidateImagesWithNewSize(newSize) { } // Si es la última imagen y hay imágenes de baja calidad, muestra una notificación - if (index === imageItems.length - 1 && lowQualityIndexes.length > 0) { - const plural = lowQualityIndexes.length > 1; // Determina si es plural - const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes - const message = plural - ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.` - : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.`; - - showNotification(message, 'error'); // Mostrar advertencia al usuario - } + // if (index === imageItems.length - 1 && lowQualityIndexes.length > 0) { + // const plural = lowQualityIndexes.length > 1; // Determina si es plural + // const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes + // const message = plural + // ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.` + // : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.`; + + // showNotification(message, 'error'); // Mostrar advertencia al usuario + // } }; }); } diff --git a/styles.css b/styles.css index 3288631..959920d 100644 --- a/styles.css +++ b/styles.css @@ -584,4 +584,17 @@ input.error, textarea.error { .viewer-close { background-color: #af1b1b !important; +} + +.zoom-icon-btn { + position: absolute; + bottom: 10px; + right: 10px; + background: transparent; + border: none; + cursor: pointer; + width: 30px; + height: 30px; + padding: 0; + top: 10px; } \ No newline at end of file diff --git a/web-form-plugin.php b/web-form-plugin.php index 8f26a23..b7727bd 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -37,7 +37,7 @@ function web_form_shortcode() $picker_path = plugin_dir_url(__FILE__) . 'ios-picker.js'; // Cargar CSS - wp_enqueue_style('web-form-styles', $css_path, array(), '5.1.4'); + wp_enqueue_style('web-form-styles', $css_path, array(), '5.1.5'); // Cargar Swiper CSS desde CDN wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); @@ -49,7 +49,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.5.9', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From 100dd026da0ade46f9d9010dc51adedf7009d4a3 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 24 Apr 2025 09:03:27 -0500 Subject: [PATCH 11/72] Ajustes responsive de contenedor de comentarios --- main.js | 22 +++++++++++----------- styles.css | 5 +++-- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/main.js b/main.js index cf670a3..e523a28 100644 --- a/main.js +++ b/main.js @@ -253,7 +253,7 @@ function initializeViewerForSwiper() { if (btn && img) { let viewerInstance = null; - // Agrega un evento de clic al botón de zoom + // Agrega un evento de clic al botón de zoom btn.addEventListener('click', function (event) { event.stopPropagation(); @@ -275,15 +275,15 @@ function initializeViewerForSwiper() { title: false, toolbar: { zoomIn: { // Habilitar zoom in - show: true, - title: 'Zoom In', - size: 'large' - }, + show: true, + title: 'Zoom In', + size: 'large' + }, zoomOut: { // Habilitar zoom out - show: true, - title: 'Zoom Out', - size: 'large' - }, + show: true, + title: 'Zoom Out', + size: 'large' + }, oneToOne: false, reset: false, prev: false, @@ -303,7 +303,7 @@ function initializeViewerForSwiper() { viewerInstance = null; } }); - // Muestra el visor + // Muestra el visor viewerInstance.show(); }); } @@ -707,7 +707,7 @@ function addToCart() { // const totalPrice = pricePerUnit * quantity * uploadedImagesData.length; // Calcular el precio total // console.log(`💰 Precio total: S/. ${totalPrice.toFixed(2)}`); - + const comments = document.querySelector('.comment-area').value; const withFrame = document.querySelector('input[name="frame"]:checked').value !== 'sin-marco'; let frame = 'sin-marco'; diff --git a/styles.css b/styles.css index 959920d..35a59d7 100644 --- a/styles.css +++ b/styles.css @@ -170,8 +170,9 @@ body { /* Frame picker carrousel */ .container { - max-width: 1200px; + max-width: 1000px; margin: 0 auto; + width: 90%; } .swiper { @@ -259,7 +260,7 @@ body { } .custom-size { - width: 110%; + width: 10%; padding: 0; margin: 0 auto; } From 51649e5d3c5b565d4a21cf38191e847373db4484 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 24 Apr 2025 10:23:04 -0500 Subject: [PATCH 12/72] Ajustes responsive de contenedor de marcos --- styles.css | 40 +++++++++++++++++++--------------------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/styles.css b/styles.css index 35a59d7..8771510 100644 --- a/styles.css +++ b/styles.css @@ -170,9 +170,12 @@ body { /* Frame picker carrousel */ .container { - max-width: 1000px; + max-width: 1200px; margin: 0 auto; - width: 90%; +} + +.frames { + padding: 0 30px; } .swiper { @@ -283,29 +286,24 @@ body { @media (max-width: 480px) { - .desktop-upload-text { - display: none; - } - - .mobile-upload-text { - display: block; - } - - .images-grid { - grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); - } + .desktop-upload-text { + display: none; + } - .custom-size { - width: 110%; - padding: 0; - margin: 0 auto; - } + .mobile-upload-text { + display: block; + } + .images-grid { + grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); + } - .frames { - padding: 0 30px; + .custom-size { + width: 110%; + padding: 0; + margin: 0 auto; } - + .add-to-cart-btn { padding: 8px; width: 100%; From 6e2ef541dc3d94421098d511bbbe8c48bda1e0d4 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 24 Apr 2025 12:48:39 -0500 Subject: [PATCH 13/72] Aplicar descuentos de acuerdo a cantidad en carrito de compras --- web-form-plugin.php | 70 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 59 insertions(+), 11 deletions(-) diff --git a/web-form-plugin.php b/web-form-plugin.php index b7727bd..dcd2839 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.5 +Version: 3.0.6 Author: Enmanuel */ @@ -268,22 +268,70 @@ function reemplazar_thumbnail_por_print_image($thumbnail, $item) { // Actualizar el precio del producto en el carrito -function update_cart_item_price($cart) -{ - // Evitar que se ejecute varias veces - if (did_action('woocommerce_before_calculate_totals') >= 2) { +function update_cart_item_price($cart) { + if (did_action('woocommerce_before_calculate_totals') >= 2 || is_admin() && !defined('DOING_AJAX')) { return; } - // Iterar sobre los productos en el carrito - foreach ($cart->get_cart() as $cart_item) { - if (isset($cart_item['print_image_data']['price'])) { - $custom_price = floatval($cart_item['print_image_data']['price']); // Precio personalizado - $cart_item['data']->set_price($custom_price); // Actualizar el precio del producto + // Definir precios por tamaño y cantidad + $size_prices = [ + "10X15" => 5.00, + "13X18" => 7.00, + "15X21" => 8.00, + "20X25" => 15.00, + "20X30" => 18.00, + "25X38" => 25.00, + "30X45" => 35.00, + "30X60" => 45.00, + ]; + // Descuentos por cantidad mayor a 4 + $bulk_size_prices = [ + "10X15" => 3.00, + "13X18" => 5.00, + "15X21" => 6.00, + "20X25" => 13.00, + "20X30" => 15.00, + "25X38" => 20.00, + "30X45" => 30.00, + "30X60" => 40.00, + ]; + // Descuentos por cantidad mayor a 12 + $bulk_size_prices_12 = [ + "10X15" => 2.50, + "13X18" => 4.00, + "15X21" => 5.00, + "20X25" => 10.00, + "20X30" => 12.00, + "25X38" => 18.00, + "30X45" => 25.00, + "30X60" => 35.00, + ]; + // Recorrer los artículos del carrito + foreach ($cart->get_cart() as $cart_item_key => $cart_item) { + $product = $cart_item['data']; + $quantity = $cart_item['quantity']; + + // Obtener el tamaño del producto (asumiendo que se guarda en un campo personalizado) + $size = isset($cart_item['print_image_data']['size']) ? $cart_item['print_image_data']['size'] : ''; + + // Aplicar precio según tamaño y cantidad + if (!empty($size)) { + if ($quantity >= 12 && isset($bulk_size_prices_12[$size])) { + $precio_final = $bulk_size_prices_12[$size]; + } elseif ($quantity >= 4 && isset($bulk_size_prices[$size])) { + $precio_final = $bulk_size_prices[$size]; + } elseif (isset($size_prices[$size])) { + $precio_final = $size_prices[$size]; + } + + // Asignar el precio final al producto + if (isset($precio_final)) { + $product->set_price($precio_final); + } } } } -add_action('woocommerce_before_calculate_totals', 'update_cart_item_price', 10, 1); +add_action('woocommerce_before_calculate_totals', 'update_cart_item_price', 20, 1); function display_print_image_cart_item_data($item_data, $cart_item) { From 1432619e0ba2da0e5449cc230510354595e65172 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Tue, 29 Apr 2025 13:23:26 -0500 Subject: [PATCH 14/72] Cantidad implementada para unico pedido, y cambio de limite de imagenes a 1000 --- index.php | 4 ++-- main.js | 40 ++++++++++++++++++++-------------------- web-form-plugin.php | 6 +++--- 3 files changed, 25 insertions(+), 25 deletions(-) diff --git a/index.php b/index.php index 28b33ad..d50b549 100644 --- a/index.php +++ b/index.php @@ -30,7 +30,7 @@
- +
@@ -64,7 +64,7 @@
- Comentarios adicionales + Comentarios adicionales para tu diseño
diff --git a/main.js b/main.js index e523a28..366b30a 100644 --- a/main.js +++ b/main.js @@ -48,17 +48,17 @@ document.addEventListener('DOMContentLoaded', function () { setupCommentLengthChecker(); // Initialize image counter - const counterElement = document.createElement('div'); - counterElement.id = 'images-counter'; - counterElement.className = 'images-counter'; - counterElement.textContent = '0'; - counterElement.style.display = 'none'; // Initially hidden - - // Insert counter after upload area - const uploadAreaParent = document.getElementById('upload-area').parentElement; - if (uploadAreaParent) { - uploadAreaParent.insertBefore(counterElement, document.getElementById('upload-area').nextSibling); - } + // const counterElement = document.createElement('div'); + // counterElement.id = 'images-counter'; + // counterElement.className = 'images-counter'; + // counterElement.textContent = '0'; + // counterElement.style.display = 'none'; // Initially hidden + + // // Insert counter after upload area + // const uploadAreaParent = document.getElementById('upload-area').parentElement; + // if (uploadAreaParent) { + // uploadAreaParent.insertBefore(counterElement, document.getElementById('upload-area').nextSibling); + // } updateTotalPrice(); }); @@ -156,11 +156,11 @@ function setupEventListeners() { quantityInput.addEventListener('input', function () { let value = parseInt(this.value); - // Si es mayor a 100, vuelve al mismo valor - if (value > 100) { - this.value = 100; - showNotification('La cantidad no puede ser mayor a 100 unidades', 'error'); - } + // Si es mayor a 1000, vuelve al mismo valor + if (value > 1000) { + this.value = 1000; + showNotification('La cantidad no puede ser mayor a 1000 unidades', 'error'); + } updateTotalPrice(); }); @@ -523,9 +523,9 @@ function updateImageCounter() { } // Mostrar u ocultar contador según tengamos imágenes - if (counter) { - counter.style.display = uploadedImagesData.length > 0 ? 'block' : 'none'; - } + // if (counter) { + // counter.style.display = uploadedImagesData.length > 0 ? 'block' : 'none'; + // } } // Factor de conversión de cm a píxeles (ajusta según la calidad requerida) @@ -904,7 +904,7 @@ function updateTotalPrice() { } // Calculate the total price considering all images - const totalPrice = pricePerUnit * quantity * uploadedImagesData.length; + const totalPrice = pricePerUnit * quantity; // Round const roundedPrice = totalPrice.toFixed(2); diff --git a/web-form-plugin.php b/web-form-plugin.php index dcd2839..46efe75 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.6 +Version: 3.0.7 Author: Enmanuel */ @@ -37,7 +37,7 @@ function web_form_shortcode() $picker_path = plugin_dir_url(__FILE__) . 'ios-picker.js'; // Cargar CSS - wp_enqueue_style('web-form-styles', $css_path, array(), '5.1.5'); + wp_enqueue_style('web-form-styles', $css_path, array(), '5.1.7'); // Cargar Swiper CSS desde CDN wp_enqueue_style('swiper-css', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css', array(), '11.0.0'); @@ -49,7 +49,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.2', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From 2767a071205fc83822f97ff9a410f3a70fbf79eb Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 30 Apr 2025 09:01:55 -0500 Subject: [PATCH 15/72] =?UTF-8?q?Eliminaci=C3=B3n=20de=20c=C3=B3digo=20inu?= =?UTF-8?q?tilizado=20de=20conteo=20de=20im=C3=A1genes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main.js | 80 --------------------------------------------- web-form-plugin.php | 4 +-- 2 files changed, 2 insertions(+), 82 deletions(-) diff --git a/main.js b/main.js index 366b30a..8f59a29 100644 --- a/main.js +++ b/main.js @@ -47,19 +47,6 @@ document.addEventListener('DOMContentLoaded', function () { setupEventListeners(); setupCommentLengthChecker(); - // Initialize image counter - // const counterElement = document.createElement('div'); - // counterElement.id = 'images-counter'; - // counterElement.className = 'images-counter'; - // counterElement.textContent = '0'; - // counterElement.style.display = 'none'; // Initially hidden - - // // Insert counter after upload area - // const uploadAreaParent = document.getElementById('upload-area').parentElement; - // if (uploadAreaParent) { - // uploadAreaParent.insertBefore(counterElement, document.getElementById('upload-area').nextSibling); - // } - updateTotalPrice(); }); @@ -118,11 +105,6 @@ function initializeFramePicker() { const pickerItems = framePicker.querySelectorAll('.swiper-slide'); pickerItems.forEach(item => { item.addEventListener('click', function () { - // Verificar si hay imágenes subidas antes de permitir la selección del marco - // if (uploadedImagesData.length === 0) { - // showNotification('Sube una imagen primero', 'error'); - // return; - // } pickerItems.forEach(i => i.classList.remove('selected')); item.classList.add('selected'); currentSelectedFrame = item.dataset.value; @@ -465,7 +447,6 @@ function handleFiles(e) { if (imagesGrid.children.length === 0) { uploadElements.classList.remove('has-images'); // Remover clase si no hay imágenes } - updateImageCounter(); // Actualizar el contador de imágenes updateTotalPrice(); // Actualizar el precio total }); @@ -481,21 +462,9 @@ function handleFiles(e) { imageItem.appendChild(removeBtn); imagesGrid.appendChild(imageItem); - updateImageCounter(); // Actualizar el contador de imágenes updateTotalPrice(); // Actualizar el precio total pending--; // Decrementar el contador de pendientes - - // Si todas las imágenes han sido procesadas, mostrar advertencia si hay imágenes de baja calidad - // if (pending === 0 && lowQualityIndexes.length > 0) { - // const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes - // const plural = lowQualityIndexes.length > 1; // Determina si es plural - // const message = plural - // ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.` - // : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${selectedSize.replace("X", "x")} cm.`; - - // showNotification(message, 'error'); // Mostrar advertencia al usuario - // } }; }; @@ -505,29 +474,6 @@ function handleFiles(e) { fileInput.value = ''; // Resetea el input para permitir subir el mismo archivo otra vez } -// Función para actualizar el contador de imágenes -function updateImageCounter() { - const counter = document.getElementById('images-counter'); - if (counter) { - counter.textContent = uploadedImagesData.length; - } else { - // Si no existe el contador, lo creamos - const counterElement = document.createElement('div'); - counterElement.id = 'images-counter'; - counterElement.className = 'images-counter'; - counterElement.textContent = uploadedImagesData.length; - - // Insertamos el contador cerca del área de subida - const uploadAreaParent = uploadArea.parentElement; - uploadAreaParent.insertBefore(counterElement, uploadArea.nextSibling); - } - - // Mostrar u ocultar contador según tengamos imágenes - // if (counter) { - // counter.style.display = uploadedImagesData.length > 0 ? 'block' : 'none'; - // } -} - // Factor de conversión de cm a píxeles (ajusta según la calidad requerida) const PIXELS_PER_CM = 30; @@ -572,17 +518,6 @@ function validateImagesWithCustomSize(customWidth, customHeight) { }; } }); - - // Mostrar notificación si hay imágenes de baja calidad - // if (lowQualityIndexes.length > 0) { - // const imagesStr = lowQualityIndexes.join(', '); - // const plural = lowQualityIndexes.length > 1; - // const message = plural - // ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.` - // : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${customWidth}x${customHeight} cm.`; - - // showNotification(message, 'error'); - // } } // Modificar el evento de entrada para tamaños personalizados @@ -653,17 +588,6 @@ function revalidateImagesWithNewSize(newSize) { item.classList.remove('low-quality'); if (warningLabel) warningLabel.style.display = 'none'; // Ocultar advertencia } - - // Si es la última imagen y hay imágenes de baja calidad, muestra una notificación - // if (index === imageItems.length - 1 && lowQualityIndexes.length > 0) { - // const plural = lowQualityIndexes.length > 1; // Determina si es plural - // const imagesStr = lowQualityIndexes.join(', '); // Crear una cadena con los índices de las imágenes - // const message = plural - // ? `Las imágenes ${imagesStr} no tienen resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.` - // : `La imagen ${imagesStr} no tiene resolución suficiente para una impresión óptima en ${newSize.replace("X", "x")} cm.`; - - // showNotification(message, 'error'); // Mostrar advertencia al usuario - // } }; }); } @@ -705,9 +629,6 @@ function addToCart() { pricePerUnit = getSelectedSizePrice(); // Obtener el precio del tamaño seleccionado } - // const totalPrice = pricePerUnit * quantity * uploadedImagesData.length; // Calcular el precio total - // console.log(`💰 Precio total: S/. ${totalPrice.toFixed(2)}`); - const comments = document.querySelector('.comment-area').value; const withFrame = document.querySelector('input[name="frame"]:checked').value !== 'sin-marco'; let frame = 'sin-marco'; @@ -831,7 +752,6 @@ function addToCart() { uploadedImagesData = []; imagesGrid.innerHTML = ''; uploadElements.classList.remove('has-images'); - updateImageCounter(); } } else if (failedProcesses > 0) { showNotification('Error al procesar las imágenes. Por favor intenta de nuevo.', 'error'); diff --git a/web-form-plugin.php b/web-form-plugin.php index 46efe75..6c88d8e 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.7 +Version: 3.0.8 Author: Enmanuel */ @@ -49,7 +49,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.2', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.4', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); From ad82ebd6cc3b0f928d68621bfb6b85ae894e4ad9 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 30 Apr 2025 09:51:25 -0500 Subject: [PATCH 16/72] Procesamiento de imagenes en un solo pedido --- main.js | 151 ++++++++------------------- web-form-plugin.php | 246 ++++++++++++++++++++------------------------ 2 files changed, 154 insertions(+), 243 deletions(-) diff --git a/main.js b/main.js index 8f59a29..86b7f6b 100644 --- a/main.js +++ b/main.js @@ -604,18 +604,16 @@ function getSelectedSizePrice() { function addToCart() { console.log("🚀 addToCart() fue llamada correctamente"); - // Verificar si hay imágenes subidas if (uploadedImagesData.length === 0) { showNotification('Por favor, sube al menos una imagen primero.', 'error'); console.log("⚠️ No hay imágenes subidas."); return; } - // Obtener los valores del formulario const quantity = document.querySelector('.quantity-field input').value; const isCustomSize = selectedOptionSize == 'custom'; - let size, customWidth, customHeight; + let size, customWidth, customHeight, pricePerUnit; if (isCustomSize) { customWidth = document.getElementById('custom-size__width').value; customHeight = document.getElementById('custom-size__height').value; @@ -624,9 +622,10 @@ function addToCart() { return; } size = `${customWidth}x${customHeight}`; + pricePerUnit = getSelectedSizePrice(); } else { size = sizePicker.getValue(); - pricePerUnit = getSelectedSizePrice(); // Obtener el precio del tamaño seleccionado + pricePerUnit = getSelectedSizePrice(); } const comments = document.querySelector('.comment-area').value; @@ -638,128 +637,60 @@ function addToCart() { frame = selectedFrameElement ? currentSelectedFrame : 'frame1'; } - // Verificar si `ajax_object` está definido antes de usarlo if (typeof ajax_object === 'undefined') { console.error("❌ ERROR: ajax_object no está definido. Verifica que el script de WordPress está cargando correctamente."); showNotification("Error de configuración. Contacte con el administrador.", "error"); return; } - // Mostrar indicador de carga showLoadingIndicator(); - // Variables para seguimiento del proceso - let processedImages = 0; - let successfulProcesses = 0; - let failedProcesses = 0; - let cartUrl = ''; - - // Función para procesar una imagen específica - const processImage = async (index) => { - if (index >= uploadedImagesData.length) { - // Hemos terminado de procesar todas las imágenes - finishProcessing(); - return; - } - - const imageData = uploadedImagesData[index]; - console.log(`📦 Procesando imagen ${index + 1}/${uploadedImagesData.length}...`); - - try { - // Preparar los datos para enviar - const formData = new FormData(); - formData.append('action', 'process_print_image'); - formData.append('nonce', ajax_object.nonce); - formData.append('image_data', imageData.file); - formData.append('quantity', quantity); - formData.append('size', size); - formData.append('price', pricePerUnit); // Precio unitario - formData.append('is_custom_size', isCustomSize); - formData.append('custom_width', customWidth || 0); - formData.append('custom_height', customHeight || 0); - formData.append('comments', comments); - formData.append('frame', frame); - formData.append('image_index', index); // Añadir el índice para seguimiento - - // Enviar al servidor - const response = await fetch(ajax_object.ajax_url, { - method: 'POST', - body: formData, - credentials: 'same-origin' - }); - - const data = await response.json(); - processedImages++; + // --- NUEVO: Enviar todas las imágenes en un solo FormData --- + const formData = new FormData(); + formData.append('action', 'process_print_image'); + formData.append('nonce', ajax_object.nonce); + formData.append('quantity', quantity); + formData.append('size', size); + formData.append('price', pricePerUnit); + formData.append('is_custom_size', isCustomSize); + formData.append('custom_width', customWidth || 0); + formData.append('custom_height', customHeight || 0); + formData.append('comments', comments); + formData.append('frame', frame); + + // Agregar todas las imágenes como image_data[] + uploadedImagesData.forEach((imageData, idx) => { + formData.append('image_data[]', imageData.file, imageData.file.name); + }); + fetch(ajax_object.ajax_url, { + method: 'POST', + body: formData, + credentials: 'same-origin' + }) + .then(response => response.json()) + .then(data => { + hideLoadingIndicator(); if (data.success) { - successfulProcesses++; - if (!cartUrl && data.data && data.data.cart_url) { - cartUrl = data.data.cart_url; + showNotification('¡Imágenes añadidas al carrito!', 'success'); + if (data.data && data.data.cart_url) { + setTimeout(() => { + window.location.href = data.data.cart_url; + }, 1500); } - console.log(`✅ Imagen ${index + 1} procesada correctamente`); - } else { - failedProcesses++; - console.error(`❌ Error al procesar la imagen ${index + 1}:`, data.data); - } - - // Actualizar el indicador de carga con el progreso - updateLoadingStatus(processedImages, uploadedImagesData.length); - - // Procesar la siguiente imagen - await processImage(index + 1); - - } catch (error) { - processedImages++; - failedProcesses++; - console.error(`❌ Error al procesar la imagen ${index + 1}:`, error); - - // Continuar con la siguiente imagen a pesar del error - await processImage(index + 1); - } - }; - - // Función para mostrar el estado de carga actualizado - const updateLoadingStatus = (processed, total) => { - const loadingOverlay = document.querySelector('.loading-overlay p'); - if (loadingOverlay) { - loadingOverlay.textContent = `Procesando imagen ${processed}/${total}...`; - } - }; - - // Función para finalizar el proceso - const finishProcessing = () => { - hideLoadingIndicator(); - - console.log(`✅ Procesadas ${processedImages}/${uploadedImagesData.length} imágenes. Éxitos: ${successfulProcesses}, Fallos: ${failedProcesses}`); - - if (successfulProcesses > 0) { - if (failedProcesses > 0) { - showNotification(`Se añadieron ${successfulProcesses} imágenes al carrito, pero ${failedProcesses} no pudieron procesarse.`, 'warning'); - } else { - showNotification(`¡${successfulProcesses} ${successfulProcesses > 1 ? 'imágenes añadidas' : 'imagen añadida'} al carrito!`, 'success'); - } - - // Redirigir al carrito después de un breve retraso - if (cartUrl) { - setTimeout(() => { - window.location.href = cartUrl; - }, 1500); - } - - // Limpiar las imágenes procesadas exitosamente - if (successfulProcesses === uploadedImagesData.length) { - // Todas las imágenes se procesaron con éxito, limpiar todo + // Limpiar imágenes uploadedImagesData = []; imagesGrid.innerHTML = ''; uploadElements.classList.remove('has-images'); + } else { + showNotification(data.data || 'Error al procesar las imágenes.', 'error'); } - } else if (failedProcesses > 0) { + }) + .catch(error => { + hideLoadingIndicator(); showNotification('Error al procesar las imágenes. Por favor intenta de nuevo.', 'error'); - } - }; - - // Iniciar el procesamiento secuencial comenzando por la primera imagen - processImage(0); + console.error(error); + }); } // Función para mostrar notificaciones diff --git a/web-form-plugin.php b/web-form-plugin.php index 6c88d8e..6831fdf 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.0.8 +Version: 3.1.0 Author: Enmanuel */ @@ -49,7 +49,7 @@ function web_form_shortcode() wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.4', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.6', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); @@ -82,16 +82,13 @@ function process_print_image_to_cart() { check_ajax_referer('web_form_nonce', 'nonce'); - // Verificamos si tenemos una imagen + // Verificamos si tenemos imágenes if (empty($_FILES['image_data'])) { error_log('Error: No se ha recibido ningún archivo de imagen'); wp_send_json_error('No se ha subido ninguna imagen'); return; } - // Información de debug - error_log('🔍 Procesando imagen: ' . print_r($_FILES['image_data'], true)); - $quantity = isset($_POST['quantity']) ? intval($_POST['quantity']) : 1; $size = isset($_POST['size']) ? sanitize_text_field($_POST['size']) : ''; $price = isset($_POST['price']) ? floatval($_POST['price']) : 0.0; @@ -100,11 +97,7 @@ function process_print_image_to_cart() $custom_width = isset($_POST['custom_width']) ? intval($_POST['custom_width']) : 0; $custom_height = isset($_POST['custom_height']) ? intval($_POST['custom_height']) : 0; $is_custom_size = isset($_POST['is_custom_size']) ? filter_var($_POST['is_custom_size'], FILTER_VALIDATE_BOOLEAN) : false; - $image_index = isset($_POST['image_index']) ? intval($_POST['image_index']) : 0; - - error_log('📊 Datos recibidos: Tamaño=' . $size . ', Marco=' . $frame . ', Índice=' . $image_index); - // Obtener el ID del producto base $product_id = get_option('print_image_product_id', 159); if (!$product_id || !wc_get_product($product_id)) { error_log('Error: No se ha configurado un producto base para impresiones'); @@ -112,83 +105,80 @@ function process_print_image_to_cart() return; } - // Configuración para guardar la imagen $upload_dir = wp_upload_dir(); - $image_url = ''; - - // Procesar el archivo subido - $file = $_FILES['image_data']; - - // Verificar si hay errores en la subida - if ($file['error'] !== UPLOAD_ERR_OK) { - error_log('❌ Error en la subida: ' . $file['error']); - wp_send_json_error('Error al subir la imagen: ' . $file['error']); - return; - } + $images_data = []; + + // Soportar tanto un solo archivo como múltiples archivos + $files = $_FILES['image_data']; + $file_count = is_array($files['name']) ? count($files['name']) : 1; + + for ($i = 0; $i < $file_count; $i++) { + $file = [ + 'name' => is_array($files['name']) ? $files['name'][$i] : $files['name'], + 'type' => is_array($files['type']) ? $files['type'][$i] : $files['type'], + 'tmp_name' => is_array($files['tmp_name']) ? $files['tmp_name'][$i] : $files['tmp_name'], + 'error' => is_array($files['error']) ? $files['error'][$i] : $files['error'], + 'size' => is_array($files['size']) ? $files['size'][$i] : $files['size'], + ]; - // Verificar tipo de archivo (solo permitir imágenes) - $allowed_types = array('image/jpeg', 'image/png', 'image/gif', 'image/webp'); + if ($file['error'] !== UPLOAD_ERR_OK) { + error_log('❌ Error en la subida: ' . $file['error']); + continue; + } - // Obtener el tipo MIME real del archivo - $file_info = wp_check_filetype_and_ext($file['tmp_name'], $file['name']); - $file_type = $file_info['type'] ? $file_info['type'] : $file['type']; + $allowed_types = array('image/jpeg', 'image/png', 'image/gif', 'image/webp'); + $file_info = wp_check_filetype_and_ext($file['tmp_name'], $file['name']); + $file_type = $file_info['type'] ? $file_info['type'] : $file['type']; - error_log('🖼️ Tipo de archivo: ' . $file_type); + if (!in_array($file_type, $allowed_types)) { + error_log('❌ Tipo de archivo no permitido: ' . $file_type); + continue; + } - if (!in_array($file_type, $allowed_types)) { - error_log('❌ Tipo de archivo no permitido: ' . $file_type); - wp_send_json_error('Solo se permiten imágenes (JPEG, PNG, GIF, WEBP)'); - return; + $filename = 'print_image_' . time() . '_' . $i . '_' . sanitize_file_name($file['name']); + $filepath = $upload_dir['path'] . '/' . $filename; + + if (move_uploaded_file($file['tmp_name'], $filepath)) { + $image_url = $upload_dir['url'] . '/' . $filename; + $images_data[] = [ + 'id' => $filename, + 'image_url' => esc_url($image_url), + 'size' => $is_custom_size ? "{$custom_width}x{$custom_height}" : $size, + 'is_custom_size' => $is_custom_size, + 'frame' => $frame, + 'comments' => $comments, + ]; + } else { + error_log('❌ Error al mover el archivo subido de ' . $file['tmp_name'] . ' a ' . $filepath); + } } - // Generar nombre único para el archivo - $filename = 'print_image_' . time() . '_' . $image_index . '_' . sanitize_file_name($file['name']); - $filepath = $upload_dir['path'] . '/' . $filename; - - // Mover el archivo subido a la carpeta de uploads - if (move_uploaded_file($file['tmp_name'], $filepath)) { - // Obtener URL de la imagen - $image_url = $upload_dir['url'] . '/' . $filename; - error_log('✅ Imagen guardada correctamente en: ' . $image_url); - } else { - error_log('❌ Error al mover el archivo subido de ' . $file['tmp_name'] . ' a ' . $filepath); - wp_send_json_error('Error al guardar la imagen en el servidor'); + if (empty($images_data)) { + wp_send_json_error('No se pudieron procesar las imágenes.'); return; } - // Guardar la imagen en los metadatos del carrito + // Guardar todas las imágenes en el array print_image_data $cart_item_data = array( - 'print_image_data' => array( - 'id'=> $filename, // ID único para la imagen - 'image_url' => esc_url($image_url), - 'size' => $is_custom_size ? "{$custom_width}x{$custom_height}" : $size, - 'price' => $price, - 'is_custom_size' => $is_custom_size, - 'frame' => $frame, - 'comments' => $comments, - 'image_index' => $image_index // Guarda el índice para referencia - ), - 'unique_key' => md5(microtime() . rand() . $image_index) // Asegura que cada item sea único + 'print_image_data' => $images_data, + 'size' => $is_custom_size ? "{$custom_width}x{$custom_height}" : $size, + 'price' => $price, + 'is_custom_size' => $is_custom_size, + 'frame' => $frame, + 'comments' => $comments, + 'unique_key' => md5(microtime() . rand()) ); - error_log('🛒 Datos del carrito para imagen ' . $image_index . ': ' . print_r($cart_item_data, true)); - - // Añadir al carrito $cart_item_key = WC()->cart->add_to_cart($product_id, $quantity, 0, array(), $cart_item_data); - if ($cart_item_key) { - error_log('✅ Producto ' . $image_index . ' añadido al carrito correctamente'); wp_send_json_success(array( 'message' => 'Producto añadido al carrito', - 'cart_url' => wc_get_cart_url(), - 'image_index' => $image_index + 'cart_url' => wc_get_cart_url() )); } else { - // Obtener errores de WooCommerce y formatearlos correctamente $error_messages = array(); $wc_errors = wc_get_notices('error'); - foreach ($wc_errors as $error) { if (is_array($error) && isset($error['notice'])) { $error_messages[] = $error['notice']; @@ -196,17 +186,11 @@ function process_print_image_to_cart() $error_messages[] = $error; } } - - // Limpiar los errores para no mostrarlos duplicados después wc_clear_notices(); - $error_text = !empty($error_messages) ? implode(', ', $error_messages) : 'Error desconocido al añadir al carrito'; - - error_log('❌ Errores reales del carrito: ' . $error_text); wp_send_json_error($error_text); - return; } wp_die(); @@ -219,29 +203,27 @@ function process_print_image_to_cart() function display_cart_product($product_images, $cart_item, $cart_item_key) { - // Verifica si el artículo tiene una imagen personalizada - if (isset($cart_item['print_image_data']['image_url'])) { - $image_url = $cart_item['print_image_data']['image_url']; - - return [ - (object) [ + if (isset($cart_item['print_image_data']) && is_array($cart_item['print_image_data'])) { + $images = []; + foreach ($cart_item['print_image_data'] as $img) { + $images[] = (object) [ 'id' => 0, - 'src' => $image_url, // Imagen personalizada - 'thumbnail' => $image_url, // Miniatura personalizada + 'src' => $img['image_url'], + 'thumbnail' => $img['image_url'], 'srcset' => '', 'sizes' => '', 'name' => 'Imagen personalizada del producto', 'alt' => 'Imagen personalizada del producto', - ] - ]; + ]; + } + return $images; } - - // Si no hay imagen personalizada, devuelve la imagen original del producto return $product_images; } - add_filter('woocommerce_store_api_cart_item_images', 'display_cart_product', 10, 3); -function reemplazar_thumbnail_por_print_image($thumbnail, $item) { + +function reemplazar_thumbnail_por_print_image($thumbnail, $item) +{ // Si $item es un integer, se asume que es el ID del item, por lo que cargamos el objeto if (is_int($item)) { error_log('ℹ️ Recibido ID de item: ' . $item); @@ -268,7 +250,8 @@ function reemplazar_thumbnail_por_print_image($thumbnail, $item) { // Actualizar el precio del producto en el carrito -function update_cart_item_price($cart) { +function update_cart_item_price($cart) +{ if (did_action('woocommerce_before_calculate_totals') >= 2 || is_admin() && !defined('DOING_AJAX')) { return; } @@ -284,7 +267,6 @@ function update_cart_item_price($cart) { "30X45" => 35.00, "30X60" => 45.00, ]; - // Descuentos por cantidad mayor a 4 $bulk_size_prices = [ "10X15" => 3.00, "13X18" => 5.00, @@ -295,7 +277,6 @@ function update_cart_item_price($cart) { "30X45" => 30.00, "30X60" => 40.00, ]; - // Descuentos por cantidad mayor a 12 $bulk_size_prices_12 = [ "10X15" => 2.50, "13X18" => 4.00, @@ -306,15 +287,13 @@ function update_cart_item_price($cart) { "30X45" => 25.00, "30X60" => 35.00, ]; - // Recorrer los artículos del carrito foreach ($cart->get_cart() as $cart_item_key => $cart_item) { $product = $cart_item['data']; $quantity = $cart_item['quantity']; - // Obtener el tamaño del producto (asumiendo que se guarda en un campo personalizado) - $size = isset($cart_item['print_image_data']['size']) ? $cart_item['print_image_data']['size'] : ''; + // CORREGIDO: Obtener el tamaño del producto + $size = isset($cart_item['size']) ? $cart_item['size'] : ''; - // Aplicar precio según tamaño y cantidad if (!empty($size)) { if ($quantity >= 12 && isset($bulk_size_prices_12[$size])) { $precio_final = $bulk_size_prices_12[$size]; @@ -324,7 +303,6 @@ function update_cart_item_price($cart) { $precio_final = $size_prices[$size]; } - // Asignar el precio final al producto if (isset($precio_final)) { $product->set_price($precio_final); } @@ -333,34 +311,43 @@ function update_cart_item_price($cart) { } add_action('woocommerce_before_calculate_totals', 'update_cart_item_price', 20, 1); -function display_print_image_cart_item_data($item_data, $cart_item) -{ +function display_print_image_cart_item_data( + $item_data, + $cart_item +) { if (isset($cart_item['print_image_data'])) { - $print_data = $cart_item['print_image_data']; - - // Mostrar el tamaño - $item_data[] = array( - 'key' => 'Tamaño', - 'value' => $print_data['size'] - ); - - // Mostrar el marco (si no es "sin-marco") - if ($print_data['frame'] !== 'sin-marco') { - $item_data[] = array( - 'key' => 'Marco', - 'value' => $print_data['frame'] - ); + $images = $cart_item['print_image_data']; + // Si es un solo string (caso antiguo), conviértelo en array + if (!is_array($images) || (isset($images['image_url']) && is_string($images['image_url']))) { + $images = [$images]; } - - // Mostrar los comentarios (si existen) - if (!empty($print_data['comments'])) { - $item_data[] = array( - 'key' => 'Comentarios', - 'value' => $print_data['comments'] - ); + // Solo mostrar imágenes si es array de arrays + if (is_array($images)) { + // Mostrar miniaturas de todas las imágenes + $thumbnails = ''; + foreach ($images as $img) { + if (is_array($img) && !empty($img['image_url'])) { + $thumbnails .= 'Imagen subida'; + } + } + if (!empty($thumbnails)) { + $item_data[] = array( + 'key' => 'Imágenes', + 'value' => $thumbnails + ); + } + } + // Otros datos como tamaño, marco, comentarios... + if (isset($cart_item['size'])) { + $item_data[] = array('key' => 'Tamaño', 'value' => $cart_item['size']); + } + if (isset($cart_item['frame']) && $cart_item['frame'] !== 'sin-marco') { + $item_data[] = array('key' => 'Marco', 'value' => $cart_item['frame']); + } + if (!empty($cart_item['comments'])) { + $item_data[] = array('key' => 'Comentarios', 'value' => $cart_item['comments']); } } - return $item_data; } add_filter('woocommerce_get_item_data', 'display_print_image_cart_item_data', 10, 2); @@ -368,24 +355,17 @@ function display_print_image_cart_item_data($item_data, $cart_item) // Guardar datos personalizados en el pedido function save_print_image_order_item_meta($item, $cart_item_key, $values, $order) { - if (isset($values['print_image_data'])) { - $print_data = $values['print_image_data']; - - $item->add_meta_data('Tamaño', $print_data['size']); - - if ($print_data['frame'] !== 'sin-marco') { - $item->add_meta_data('Marco', $print_data['frame']); + if (isset($values['print_image_data']) && is_array($values['print_image_data'])) { + foreach ($values['print_image_data'] as $img) { + $item->add_meta_data('Imagen', $img['image_url']); } - - if (!empty($print_data['comments'])) { - $item->add_meta_data('Comentarios', $print_data['comments']); + $item->add_meta_data('Tamaño', $values['size']); + if ($values['frame'] !== 'sin-marco') { + $item->add_meta_data('Marco', $values['frame']); + } + if (!empty($values['comments'])) { + $item->add_meta_data('Comentarios', $values['comments']); } - - // Guardar la URL de la imagen para referencia - $item->add_meta_data('URL', $print_data['image_url'], true); - - // Guardar el ID único de la imagen - $item->add_meta_data('ID', $print_data['id'], true); } } add_action('woocommerce_checkout_create_order_line_item', 'save_print_image_order_item_meta', 10, 4); From 925e538f8f0761f8aa5b9deb123acb24bf90cb70 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Wed, 30 Apr 2025 13:21:41 -0500 Subject: [PATCH 17/72] Cambio en metadatos de imagenes unicas a un array que contiene todas --- web-form-plugin.php | 56 +++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 51 insertions(+), 5 deletions(-) diff --git a/web-form-plugin.php b/web-form-plugin.php index 6831fdf..adfbaed 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.1.0 +Version: 3.1.1 Author: Enmanuel */ @@ -356,11 +356,23 @@ function display_print_image_cart_item_data( function save_print_image_order_item_meta($item, $cart_item_key, $values, $order) { if (isset($values['print_image_data']) && is_array($values['print_image_data'])) { + $image_urls = []; foreach ($values['print_image_data'] as $img) { - $item->add_meta_data('Imagen', $img['image_url']); + if (isset($img['image_url'])) { + $image_urls[] = esc_url($img['image_url']); + } + } + + // Guardar todas las URLs de imágenes como un solo meta dato + if (!empty($image_urls)) { + $item->add_meta_data('Imágenes', implode(', ', $image_urls)); } - $item->add_meta_data('Tamaño', $values['size']); - if ($values['frame'] !== 'sin-marco') { + + // Guardar otros datos relacionados + if (isset($values['size'])) { + $item->add_meta_data('Tamaño', $values['size']); + } + if (isset($values['frame']) && $values['frame'] !== 'sin-marco') { $item->add_meta_data('Marco', $values['frame']); } if (!empty($values['comments'])) { @@ -435,4 +447,38 @@ function print_image_settings_page_content()
key === 'Imágenes') { + $image_urls = explode(', ', $meta->value); // Separar las URLs de las imágenes + $thumbnails = ''; + + foreach ($image_urls as $img_url) { + $img_url = esc_url($img_url); + $thumbnails .= '
' + . 'Imagen subida' + . 'Descargar / Ver imagen' + . '
'; + } + + // Acumular las imágenes en el grupo + $image_group .= $thumbnails; + + // Si es la primera imagen, agregar el encabezado "Imágenes:" + if ($is_first_image) { + $is_first_image = false; + return $image_group; + } + + // Retornar vacío para evitar duplicar encabezados + return ''; + } + + // Si no es una imagen, retornar el valor original + return $value; +}, 10, 3); \ No newline at end of file From 2ec8c1b9e66bfac511a42900b8fd53fa64de8d44 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Thu, 1 May 2025 09:20:04 -0500 Subject: [PATCH 18/72] Ajuste de imagenes en vista de pedido y administrador --- web-form-plugin.php | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/web-form-plugin.php b/web-form-plugin.php index adfbaed..22d5a74 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -450,20 +450,25 @@ function print_image_settings_page_content() } // Mostrar miniaturas en el detalle del pedido en el admin -add_filter('woocommerce_order_item_display_meta_value', function($value, $meta, $item) { +add_filter('woocommerce_order_item_display_meta_value', function ($value, $meta, $item) { static $image_group = ''; // Variable estática para acumular imágenes static $is_first_image = true; // Bandera para mostrar "Imágenes:" solo una vez if ($meta->key === 'Imágenes') { + // Verificar si el valor es un string y no un array $image_urls = explode(', ', $meta->value); // Separar las URLs de las imágenes $thumbnails = ''; foreach ($image_urls as $img_url) { $img_url = esc_url($img_url); $thumbnails .= '
' - . 'Imagen subida' - . 'Descargar / Ver imagen' - . '
'; + . 'Imagen subida'; + // Mostrar el enlace de descarga solo si está en el área de administración + if (is_admin()) { + $thumbnails .= 'Descargar / Ver imagen'; + } + + $thumbnails .= ''; } // Acumular las imágenes en el grupo From 86ce6f949200421f3f933070f777cda6aa888a12 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 2 May 2025 08:51:48 -0500 Subject: [PATCH 19/72] desactivar picker de precios estandar de acuerdo a boton seleccionado --- ios-picker.js | 64 ++++++++++++++++++++++++++++++++++++++++++--- web-form-plugin.php | 2 +- 2 files changed, 62 insertions(+), 4 deletions(-) diff --git a/ios-picker.js b/ios-picker.js index b37d5c1..cccc854 100644 --- a/ios-picker.js +++ b/ios-picker.js @@ -21,6 +21,12 @@ border-radius: 4px; touch-action: pan-y; margin: 0 auto; + outline: none; /* Remove default focus outline */ + } + + .carousel-container:focus { + border-color: var(--selected-text-color); + box-shadow: 0 0 0 2px rgba(228, 146, 125, 0.3); } .carousel-track { @@ -37,7 +43,6 @@ justify-content: center; font-size: 0.875rem; color: color: #070707;; - // padding: 0 20px; cursor: pointer; user-select: none; } @@ -70,6 +75,7 @@ pointer-events: none; } + // .picker-title { // text-align: center; // margin-bottom: 20px; @@ -178,9 +184,51 @@ } setupKeyboardNavigation() { + // Add tabindex to make the picker focusable + this.carouselContainer.setAttribute('tabindex', '0'); + + // Track if picker has focus or is being interacted with + let hasFocus = false; + + // Set focus state when picker is clicked or touched + this.carouselContainer.addEventListener('mousedown', () => { + hasFocus = true; + this.carouselContainer.focus(); + }); + + this.carouselContainer.addEventListener('touchstart', () => { + hasFocus = true; + this.carouselContainer.focus(); + }, { passive: true }); + + // Focus events + this.carouselContainer.addEventListener('focus', () => { + hasFocus = true; + }); + + this.carouselContainer.addEventListener('blur', () => { + hasFocus = false; + }); + + // Clear focus when touching outside + document.addEventListener('touchstart', (e) => { + if (!this.carouselContainer.contains(e.target)) { + hasFocus = false; + } + }, { passive: true }); + + // Only respond to arrow keys when picker has focus document.addEventListener('keydown', (e) => { - if (e.key === 'ArrowUp') this.carousel.prev(); - if (e.key === 'ArrowDown') this.carousel.next(); + if (!hasFocus) return; + + if (e.key === 'ArrowUp') { + e.preventDefault(); // Prevent page scrolling + this.carousel.prev(); + } + if (e.key === 'ArrowDown') { + e.preventDefault(); // Prevent page scrolling + this.carousel.next(); + } }); } @@ -319,6 +367,16 @@ } handleWheel(e) { + // Only respond to wheel events when inside the carousel container + const bounds = this.element.getBoundingClientRect(); + const isInside = + e.clientX >= bounds.left && + e.clientX <= bounds.right && + e.clientY >= bounds.top && + e.clientY <= bounds.bottom; + + if (!isInside) return; + e.preventDefault(); if (this.isAnimating) return; diff --git a/web-form-plugin.php b/web-form-plugin.php index 22d5a74..ee7977b 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.1.1 +Version: 3.1.2 Author: Enmanuel */ From 4f25344c77c5ae636cdee548f2e53cf288098145 Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Fri, 2 May 2025 09:32:34 -0500 Subject: [PATCH 20/72] Nueva funcionalidad de precio personalizado --- main.js | 550 ++++++++++++++++++++++++++------------------ web-form-plugin.php | 38 +-- 2 files changed, 345 insertions(+), 243 deletions(-) diff --git a/main.js b/main.js index 86b7f6b..566a290 100644 --- a/main.js +++ b/main.js @@ -1,47 +1,47 @@ // Variable global para seleccion de tamaños y almacenar múltiples imágenes var selectedOptionSize; -let currentSelectedFrame = 'frame1'; // Valor por defecto para el marco +let currentSelectedFrame = "frame1"; // Valor por defecto para el marco let uploadedImagesData = []; // Array para almacenar múltiples imágenes let sizePicker; let selectedSize; let currentSizePrices; const sizePrices = { - "10X15": 5.00, - "13X18": 7.00, - "15X21": 8.00, - "20X25": 15.00, - "20X30": 18.00, - "25X38": 25.00, - "30X45": 35.00, - "30X60": 45.00, + "10X15": 5.0, + "13X18": 7.0, + "15X21": 8.0, + "20X25": 15.0, + "20X30": 18.0, + "25X38": 25.0, + "30X45": 35.0, + "30X60": 45.0, }; // Precios para cantidades mayores o iguales a 4 const bulkSizePrices = { - "10X15": 3.00, - "13X18": 5.00, - "15X21": 6.00, - "20X25": 13.00, - "20X30": 15.00, - "25X38": 20.00, - "30X45": 30.00, - "30X60": 40.00, + "10X15": 3.0, + "13X18": 5.0, + "15X21": 6.0, + "20X25": 13.0, + "20X30": 15.0, + "25X38": 20.0, + "30X45": 30.0, + "30X60": 40.0, }; // Precios para cantidades mayores o iguales a 12 const bulkSizePrices12 = { - "10X15": 2.50, - "13X18": 4.00, - "15X21": 5.00, - "20X25": 10.00, - "20X30": 12.00, - "25X38": 18.00, - "30X45": 25.00, - "30X60": 35.00, + "10X15": 2.5, + "13X18": 4.0, + "15X21": 5.0, + "20X25": 10.0, + "20X30": 12.0, + "25X38": 18.0, + "30X45": 25.0, + "30X60": 35.0, }; -document.addEventListener('DOMContentLoaded', function () { +document.addEventListener("DOMContentLoaded", function () { initializeSizePicker(); initializeFramePicker(); setupEventListeners(); @@ -51,18 +51,25 @@ document.addEventListener('DOMContentLoaded', function () { }); function initializeSizePicker() { - const quantityInput = document.querySelector('.quantity-field input'); - const sizePickerContainer = document.getElementById('size-picker'); + const quantityInput = document.querySelector(".quantity-field input"); + const sizePickerContainer = document.getElementById("size-picker"); function getCurrentSizePrices(quantity) { - return quantity >= 12 ? bulkSizePrices12 : (quantity >= 4 ? bulkSizePrices : sizePrices); + return quantity >= 12 + ? bulkSizePrices12 + : quantity >= 4 + ? bulkSizePrices + : sizePrices; } function updateSizeOptions(quantity) { currentSizePrices = getCurrentSizePrices(quantity); - const sizeOptions = Object.keys(currentSizePrices).map(value => ({ + const sizeOptions = Object.keys(currentSizePrices).map((value) => ({ value: value, - label: `${value.replace("X", "cm Ancho X ")}cm Alto => (S/.${currentSizePrices[value].toFixed(2)})` + label: `${value.replace( + "X", + "cm Ancho X " + )}cm Alto => (S/.${currentSizePrices[value].toFixed(2)})`, })); // Guardar el valor actual antes de destruir @@ -82,7 +89,7 @@ function initializeSizePicker() { selectedSize = value; // Guardar nuevo valor seleccionado revalidateImagesWithNewSize(value); updateTotalPrice(); - } + }, }); } @@ -90,23 +97,22 @@ function initializeSizePicker() { updateSizeOptions(1); // Escuchar cambios en cantidad - quantityInput.addEventListener('input', function () { + quantityInput.addEventListener("input", function () { const quantity = parseInt(this.value) || 1; console.log(`Cantidad ingresada: ${quantity}`); updateSizeOptions(quantity); }); } - function initializeFramePicker() { - const framePicker = document.getElementById('frame-picker'); + const framePicker = document.getElementById("frame-picker"); if (!framePicker) return; - const pickerItems = framePicker.querySelectorAll('.swiper-slide'); - pickerItems.forEach(item => { - item.addEventListener('click', function () { - pickerItems.forEach(i => i.classList.remove('selected')); - item.classList.add('selected'); + const pickerItems = framePicker.querySelectorAll(".swiper-slide"); + pickerItems.forEach((item) => { + item.addEventListener("click", function () { + pickerItems.forEach((i) => i.classList.remove("selected")); + item.classList.add("selected"); currentSelectedFrame = item.dataset.value; }); }); @@ -115,128 +121,152 @@ function initializeFramePicker() { function setupCommentLengthChecker() { const commentArea = document.querySelector(".comment-area"); - commentArea.addEventListener('input', function () { + commentArea.addEventListener("input", function () { const maxLength = 255; const currentLength = this.value.length; if (currentLength >= maxLength) { - showNotification('Has alcanzado el límite máximo de 255 caracteres en los comentarios.', 'error'); + showNotification( + "Has alcanzado el límite máximo de 255 caracteres en los comentarios.", + "error" + ); } }); } function setupEventListeners() { - const quantityInput = document.querySelector('.quantity-field input'); + const quantityInput = document.querySelector(".quantity-field input"); - - quantityInput.addEventListener('focus', function () { - if (this.value === '1') { - this.value = ''; + quantityInput.addEventListener("focus", function () { + if (this.value === "1") { + this.value = ""; } }); - quantityInput.addEventListener('input', function () { + quantityInput.addEventListener("input", function () { let value = parseInt(this.value); - // Si es mayor a 1000, vuelve al mismo valor - if (value > 1000) { - this.value = 1000; - showNotification('La cantidad no puede ser mayor a 1000 unidades', 'error'); - } + // Si es mayor a 1000, vuelve al mismo valor + if (value > 1000) { + this.value = 1000; + showNotification( + "La cantidad no puede ser mayor a 1000 unidades", + "error" + ); + } updateTotalPrice(); }); - quantityInput.addEventListener('blur', function () { + quantityInput.addEventListener("blur", function () { let value = parseInt(this.value); if (isNaN(value) || value < 1) { this.value = 1; - } }); - document.getElementById("custom-size__width").addEventListener("input", handleCustomSize); - document.getElementById("custom-size__height").addEventListener("input", handleCustomSize); - document.querySelector('.add-to-cart-btn').addEventListener('click', addToCart); + document + .getElementById("custom-size__width") + .addEventListener("input", handleCustomSize); + document + .getElementById("custom-size__height") + .addEventListener("input", handleCustomSize); + document + .querySelector(".add-to-cart-btn") + .addEventListener("click", addToCart); } function handleCustomSize() { const widthInput = document.getElementById("custom-size__width"); const heightInput = document.getElementById("custom-size__height"); - // ANCHO - widthInput.addEventListener('focus', function () { - if (this.value === '10') this.value = ''; + // ANCHO + widthInput.addEventListener("focus", function () { + if (this.value === "10") this.value = ""; }); - widthInput.addEventListener('input', function () { + widthInput.addEventListener("input", function () { let value = parseInt(this.value); if (value > 60) { this.value = 60; - showNotification("El ancho no puede ser mayor a 60 cm.", 'error'); + showNotification("El ancho no puede ser mayor a 60 cm.", "error"); } updateTotalPrice(); }); - widthInput.addEventListener('blur', function () { + widthInput.addEventListener("blur", function () { let value = parseInt(this.value); if (isNaN(value) || value < 10) { this.value = 10; - showNotification("El ancho no puede ser menor a 10 cm.", 'error'); + showNotification("El ancho no puede ser menor a 10 cm.", "error"); } + updateTotalPrice(); }); - // ALTO - heightInput.addEventListener('focus', function () { - if (this.value === '15') this.value = ''; + // ALTO + heightInput.addEventListener("focus", function () { + if (this.value === "15") this.value = ""; }); - heightInput.addEventListener('input', function () { + heightInput.addEventListener("input", function () { let value = parseInt(this.value); if (value > 100) { this.value = 100; - showNotification("El alto no puede ser mayor a 100 cm.", 'error'); + showNotification("El alto no puede ser mayor a 100 cm.", "error"); } updateTotalPrice(); }); - heightInput.addEventListener('blur', function () { + heightInput.addEventListener("blur", function () { let value = parseInt(this.value); if (isNaN(value) || value < 15) { this.value = 15; - showNotification("El alto no puede ser menor a 15 cm.", 'error'); + showNotification("El alto no puede ser menor a 15 cm.", "error"); } + updateTotalPrice(); }); - document.querySelector('.add-to-cart-btn').addEventListener('click', addToCart); + document + .querySelector(".add-to-cart-btn") + .addEventListener("click", addToCart); } // Función para alternar entre opciones de tamaño estándar y personalizado function toggleSizeOptions(type) { - if (type === 'standard') { - document.getElementById('standard-sizes').style.display = 'block'; - document.getElementById('custom-size').style.display = 'none'; - this.selectedOptionSize = 'standard'; + if (type === "standard") { + document.getElementById("standard-sizes").style.display = "block"; + document.getElementById("custom-size").style.display = "none"; + this.selectedOptionSize = "standard"; + updateTotalPrice(); } else { - document.getElementById('standard-sizes').style.display = 'none'; - document.getElementById('custom-size').style.display = 'flex'; - this.selectedOptionSize = 'custom'; + document.getElementById("standard-sizes").style.display = "none"; + document.getElementById("custom-size").style.display = "flex"; + this.selectedOptionSize = "custom"; + + // Entrada de tamaño personalizado + const widthInput = document.getElementById("custom-size__width"); + const heightInput = document.getElementById("custom-size__height"); + if (widthInput) widthInput.value = ''; + if (heightInput) heightInput.value = ''; + + // Inicializa el precio en 0.00 + document.getElementById("total-price-button").innerText = `S/. 0.00`; } } // Función para inicializar el visor de imágenes (Viewer.js) para las imágenes del Swiper function initializeViewerForSwiper() { - const slides = document.querySelectorAll('.swiper-slide'); + const slides = document.querySelectorAll(".swiper-slide"); slides.forEach((slide) => { - const img = slide.querySelector('img'); // Busca la imagen dentro del slide - const btn = slide.querySelector('.zoom-icon-btn'); //Busca el botón con clase .zoom-icon-btn dentro del slide + const img = slide.querySelector("img"); // Busca la imagen dentro del slide + const btn = slide.querySelector(".zoom-icon-btn"); //Busca el botón con clase .zoom-icon-btn dentro del slide if (btn && img) { let viewerInstance = null; // Agrega un evento de clic al botón de zoom - btn.addEventListener('click', function (event) { + btn.addEventListener("click", function (event) { event.stopPropagation(); // Evita crear múltiples viewers @@ -246,8 +276,8 @@ function initializeViewerForSwiper() { // Crea un contenedor temporal solo para zoom const clone = img.cloneNode(true); - const tempContainer = document.createElement('div'); - tempContainer.style.display = 'none'; + const tempContainer = document.createElement("div"); + tempContainer.style.display = "none"; tempContainer.appendChild(clone); document.body.appendChild(tempContainer); @@ -256,15 +286,17 @@ function initializeViewerForSwiper() { navbar: false, title: false, toolbar: { - zoomIn: { // Habilitar zoom in + zoomIn: { + // Habilitar zoom in show: true, - title: 'Zoom In', - size: 'large' + title: "Zoom In", + size: "large", }, - zoomOut: { // Habilitar zoom out + zoomOut: { + // Habilitar zoom out show: true, - title: 'Zoom Out', - size: 'large' + title: "Zoom Out", + size: "large", }, oneToOne: false, reset: false, @@ -283,7 +315,7 @@ function initializeViewerForSwiper() { viewerInstance.destroy(); tempContainer.remove(); viewerInstance = null; - } + }, }); // Muestra el visor viewerInstance.show(); @@ -293,12 +325,14 @@ function initializeViewerForSwiper() { } function toggleFrameOptions(show) { - document.getElementById('frame-ios-picker-wrapper').style.display = show ? 'block' : 'none'; + document.getElementById("frame-ios-picker-wrapper").style.display = show + ? "block" + : "none"; if (show) { if (!window.mySwiper) { - window.mySwiper = new Swiper('.mySwiper', { - effect: 'coverflow', + window.mySwiper = new Swiper(".mySwiper", { + effect: "coverflow", grabCursor: true, centeredSlides: true, slidesPerView: "2", @@ -321,10 +355,10 @@ function toggleFrameOptions(show) { // Variables globales para almacenar las imágenes // uploadedImagesData está declarado al inicio del archivo como un array vacío -const uploadArea = document.getElementById('upload-area'); -const uploadElements = document.getElementById('upload-elements'); -const fileInput = document.getElementById('file-input'); -const imagesGrid = document.getElementById('images-grid'); +const uploadArea = document.getElementById("upload-area"); +const uploadElements = document.getElementById("upload-elements"); +const fileInput = document.getElementById("file-input"); +const imagesGrid = document.getElementById("images-grid"); // Función para activar el input de archivo function triggerFileInput() { @@ -332,11 +366,13 @@ function triggerFileInput() { } // Click en el área de subida -uploadArea.addEventListener('click', function (e) { +uploadArea.addEventListener("click", function (e) { // Verificar si el clic fue en un botón de eliminar o en una imagen - if (e.target.classList.contains('remove-image-btn') || - e.target.closest('.remove-image-btn') || - e.target.tagName === 'IMG') { + if ( + e.target.classList.contains("remove-image-btn") || + e.target.closest(".remove-image-btn") || + e.target.tagName === "IMG" + ) { return; } @@ -344,22 +380,22 @@ uploadArea.addEventListener('click', function (e) { }); // Manejo de archivos seleccionados -fileInput.addEventListener('change', handleFiles); +fileInput.addEventListener("change", handleFiles); // Manejo de arrastrar y soltar -uploadArea.addEventListener('dragover', (e) => { +uploadArea.addEventListener("dragover", (e) => { e.preventDefault(); - uploadArea.style.backgroundColor = '#f1f1f1'; + uploadArea.style.backgroundColor = "#f1f1f1"; }); -uploadArea.addEventListener('dragleave', (e) => { +uploadArea.addEventListener("dragleave", (e) => { e.preventDefault(); - uploadArea.style.backgroundColor = '#f8f9fa'; + uploadArea.style.backgroundColor = "#f8f9fa"; }); -uploadArea.addEventListener('drop', (e) => { +uploadArea.addEventListener("drop", (e) => { e.preventDefault(); - uploadArea.style.backgroundColor = '#f8f9fa'; + uploadArea.style.backgroundColor = "#f8f9fa"; if (e.dataTransfer.files.length > 0) { handleFiles({ target: { files: e.dataTransfer.files } }); @@ -369,18 +405,23 @@ uploadArea.addEventListener('drop', (e) => { // Función para manejar los archivos - Modificada para soportar múltiples imágenes function handleFiles(e) { const files = e.target.files; // Obtiene los archivos seleccionados - const isCustomSize = selectedOptionSize === 'custom'; // Verifica si el tamaño es personalizado + const isCustomSize = selectedOptionSize === "custom"; // Verifica si el tamaño es personalizado selectedSize = sizePicker.getValue(); // Obtiene el tamaño seleccionado // Si es personalizado, obtiene los valores actuales de ancho y alto let customWidth, customHeight; if (isCustomSize) { - customWidth = parseInt(document.getElementById('custom-size__width').value); - customHeight = parseInt(document.getElementById('custom-size__height').value); + customWidth = parseInt(document.getElementById("custom-size__width").value); + customHeight = parseInt( + document.getElementById("custom-size__height").value + ); // Si los valores personalizados no son válidos, muestra una advertencia y detiene el proceso if (isNaN(customWidth) || isNaN(customHeight)) { - showNotification("Por favor, ingresa un ancho y alto válidos para el tamaño personalizado.", "error"); + showNotification( + "Por favor, ingresa un ancho y alto válidos para el tamaño personalizado.", + "error" + ); return; } } @@ -388,7 +429,7 @@ function handleFiles(e) { const lowQualityIndexes = []; // Array para almacenar índices de imágenes de baja calidad if (files.length > 0) { - uploadElements.classList.add('has-images'); // Añadir clase para mostrar que hay imágenes + uploadElements.classList.add("has-images"); // Añadir clase para mostrar que hay imágenes } let pending = files.length; // Contador de archivos pendientes de procesar @@ -397,7 +438,7 @@ function handleFiles(e) { const file = files[i]; // Verificar si el archivo es una imagen - if (!file.type.match('image.*')) { + if (!file.type.match("image.*")) { pending--; // Decrementar el contador de pendientes si no es una imagen continue; } @@ -410,9 +451,10 @@ function handleFiles(e) { img.onload = function () { const { minWidth, minHeight } = getMinimumPixelsForImage(selectedSize); // Calcular los píxeles mínimos por imagen - const isLowQuality = img.naturalWidth < minWidth || img.naturalHeight < minHeight; // Verificar si la imagen es de baja calidad + const isLowQuality = + img.naturalWidth < minWidth || img.naturalHeight < minHeight; // Verificar si la imagen es de baja calidad - const imageId = 'img_' + Date.now() + '_' + i; // Generar un ID único para la imagen + const imageId = "img_" + Date.now() + "_" + i; // Generar un ID único para la imagen // Almacenar los datos de la imagen en el array global uploadedImagesData.push({ @@ -422,39 +464,41 @@ function handleFiles(e) { }); // Crear el elemento de imagen en el DOM - const imageItem = document.createElement('div'); - imageItem.className = 'image-item'; + const imageItem = document.createElement("div"); + imageItem.className = "image-item"; imageItem.dataset.imageId = imageId; // Asignar el ID al elemento if (isLowQuality) { - imageItem.classList.add('low-quality'); // Añadir clase para baja calidad + imageItem.classList.add("low-quality"); // Añadir clase para baja calidad lowQualityIndexes.push(i + 1); // Guarda el índice (basado en 1) de la imagen Posición humana } // Crear el elemento de imagen y botón de eliminar - const imgElement = document.createElement('img'); + const imgElement = document.createElement("img"); imgElement.src = event.target.result; - imgElement.alt = 'Imagen subida'; + imgElement.alt = "Imagen subida"; // Crea el botón de eliminar imagen - const removeBtn = document.createElement('button'); - removeBtn.className = 'remove-image-btn'; - removeBtn.textContent = 'X'; - removeBtn.addEventListener('click', function (e) { + const removeBtn = document.createElement("button"); + removeBtn.className = "remove-image-btn"; + removeBtn.textContent = "X"; + removeBtn.addEventListener("click", function (e) { e.stopPropagation(); // Evitar que el evento de clic se propague al contenedor - uploadedImagesData = uploadedImagesData.filter(img => img.id !== imageId); // Eliminar la imagen del array + uploadedImagesData = uploadedImagesData.filter( + (img) => img.id !== imageId + ); // Eliminar la imagen del array imageItem.remove(); // Eliminar el elemento del DOM if (imagesGrid.children.length === 0) { - uploadElements.classList.remove('has-images'); // Remover clase si no hay imágenes + uploadElements.classList.remove("has-images"); // Remover clase si no hay imágenes } updateTotalPrice(); // Actualizar el precio total }); // Crear la etiqueta de advertencia de baja calidad - const warningLabel = document.createElement('div'); - warningLabel.className = 'quality-warning'; - warningLabel.textContent = '⚠️ Resolución baja'; - if (!isLowQuality) warningLabel.style.display = 'none'; // Ocultar si no es baja calidad + const warningLabel = document.createElement("div"); + warningLabel.className = "quality-warning"; + warningLabel.textContent = "⚠️ Resolución baja"; + if (!isLowQuality) warningLabel.style.display = "none"; // Ocultar si no es baja calidad // Añadir los elementos al contenedor de imágenes imageItem.appendChild(imgElement); @@ -471,7 +515,7 @@ function handleFiles(e) { reader.readAsDataURL(file); // Leer el archivo como URL de datos } - fileInput.value = ''; // Resetea el input para permitir subir el mismo archivo otra vez + fileInput.value = ""; // Resetea el input para permitir subir el mismo archivo otra vez } // Factor de conversión de cm a píxeles (ajusta según la calidad requerida) @@ -479,7 +523,7 @@ const PIXELS_PER_CM = 30; // Función para calcular los requisitos mínimos de píxeles según el tamaño de la imagen function getMinimumPixelsForImage(size) { - const [widthCm, heightCm] = size.split('X').map(Number); // Convierte "10X15" a [10, 15] + const [widthCm, heightCm] = size.split("X").map(Number); // Convierte "10X15" a [10, 15] const minWidth = widthCm * PIXELS_PER_CM; // Calcula el ancho mínimo en píxeles const minHeight = heightCm * PIXELS_PER_CM; // Calcula la altura mínima en píxeles return { minWidth, minHeight }; // Devuelve un objeto con los valores mínimos @@ -495,15 +539,20 @@ function validateImagesWithCustomSize(customWidth, customHeight) { // Recorre las imágenes subidas uploadedImagesData.forEach((imageData, index) => { - const imgElement = imagesGrid.querySelector(`[data-image-id="${imageData.id}"] img`); - const warningLabel = imagesGrid.querySelector(`[data-image-id="${imageData.id}"] .quality-warning`); + const imgElement = imagesGrid.querySelector( + `[data-image-id="${imageData.id}"] img` + ); + const warningLabel = imagesGrid.querySelector( + `[data-image-id="${imageData.id}"] .quality-warning` + ); if (imgElement) { const tempImg = new Image(); tempImg.src = imgElement.src; tempImg.onload = () => { - const isLowQuality = tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; + const isLowQuality = + tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; // Actualiza el estado de calidad en el array global imageData.lowQuality = isLowQuality; @@ -511,9 +560,9 @@ function validateImagesWithCustomSize(customWidth, customHeight) { // Modifica clases visuales y etiqueta if (isLowQuality) { lowQualityIndexes.push(index + 1); // Índice basado en 1 - warningLabel.style.display = 'block'; // Mostrar advertencia + warningLabel.style.display = "block"; // Mostrar advertencia } else { - warningLabel.style.display = 'none'; // Ocultar advertencia + warningLabel.style.display = "none"; // Ocultar advertencia } }; } @@ -521,27 +570,35 @@ function validateImagesWithCustomSize(customWidth, customHeight) { } // Modificar el evento de entrada para tamaños personalizados -document.getElementById("custom-size__width").addEventListener("input", function () { - const customWidth = parseInt(this.value); - const customHeight = parseInt(document.getElementById("custom-size__height").value); - - if (!isNaN(customWidth) && !isNaN(customHeight)) { - validateImagesWithCustomSize(customWidth, customHeight); - } -}); +document + .getElementById("custom-size__width") + .addEventListener("input", function () { + const customWidth = parseInt(this.value); + const customHeight = parseInt( + document.getElementById("custom-size__height").value + ); + + if (!isNaN(customWidth) && !isNaN(customHeight)) { + validateImagesWithCustomSize(customWidth, customHeight); + } + }); -document.getElementById("custom-size__height").addEventListener("input", function () { - const customWidth = parseInt(document.getElementById("custom-size__width").value); - const customHeight = parseInt(this.value); +document + .getElementById("custom-size__height") + .addEventListener("input", function () { + const customWidth = parseInt( + document.getElementById("custom-size__width").value + ); + const customHeight = parseInt(this.value); - if (!isNaN(customWidth) && !isNaN(customHeight)) { - validateImagesWithCustomSize(customWidth, customHeight); - } -}); + if (!isNaN(customWidth) && !isNaN(customHeight)) { + validateImagesWithCustomSize(customWidth, customHeight); + } + }); // Modificado para incluir el nombre del archivo function analyzeImagePixels(img, imageSize, fileName) { - const { minWidth, minHeight } = getMinimumPixelsForImage(imageSize); // Obtener los requisitos mínimos de píxeles + const { minWidth, minHeight } = getMinimumPixelsForImage(imageSize); // Obtener los requisitos mínimos de píxeles // Verificar si la imagen se ha cargado correctamente if (img.naturalWidth === 0 || img.naturalHeight === 0) { @@ -550,7 +607,10 @@ function analyzeImagePixels(img, imageSize, fileName) { // Verificar si la imagen cumple con los requisitos mínimos if (img.naturalWidth < minWidth || img.naturalHeight < minHeight) { - return `La imagen "${fileName}" no tiene suficiente calidad para imprimir en ${imageSize.replace("X", "x")} cm. Debe tener al menos ${minWidth}x${minHeight} píxeles.`; + return `La imagen "${fileName}" no tiene suficiente calidad para imprimir en ${imageSize.replace( + "X", + "x" + )} cm. Debe tener al menos ${minWidth}x${minHeight} píxeles.`; } return null; // No hay error @@ -562,31 +622,32 @@ function revalidateImagesWithNewSize(newSize) { const lowQualityIndexes = []; // Array para almacenar índices de imágenes de baja calidad // Recorre las imágenes en el orden en que aparecen en el grid - const imageItems = Array.from(imagesGrid.querySelectorAll('.image-item')); + const imageItems = Array.from(imagesGrid.querySelectorAll(".image-item")); imageItems.forEach((item, index) => { const imageId = item.dataset.imageId; // Obtener el ID de la imagen - const data = uploadedImagesData.find(img => img.id === imageId); // Buscar en el array de datos - const imgElement = item.querySelector('img'); // Obtener el elemento de imagen - const warningLabel = item.querySelector('.quality-warning'); // Obtener la etiqueta de advertencia + const data = uploadedImagesData.find((img) => img.id === imageId); // Buscar en el array de datos + const imgElement = item.querySelector("img"); // Obtener el elemento de imagen + const warningLabel = item.querySelector(".quality-warning"); // Obtener la etiqueta de advertencia const tempImg = new Image(); // Crear un nuevo objeto Image para cargar la imagen tempImg.src = imgElement.src; // Asignar la URL de la imagen al objeto Image tempImg.onload = () => { - const isLowQuality = tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; + const isLowQuality = + tempImg.naturalWidth < minWidth || tempImg.naturalHeight < minHeight; // Actualiza el estado de calidad en el array global data.lowQuality = isLowQuality; // Modifica clases visuales y etiqueta if (isLowQuality) { - item.classList.add('low-quality'); - if (warningLabel) warningLabel.style.display = 'block'; // Mostrar advertencia + item.classList.add("low-quality"); + if (warningLabel) warningLabel.style.display = "block"; // Mostrar advertencia lowQualityIndexes.push(index + 1); // Índice basado en el orden del grid } else { - item.classList.remove('low-quality'); - if (warningLabel) warningLabel.style.display = 'none'; // Ocultar advertencia + item.classList.remove("low-quality"); + if (warningLabel) warningLabel.style.display = "none"; // Ocultar advertencia } }; }); @@ -605,41 +666,51 @@ function addToCart() { console.log("🚀 addToCart() fue llamada correctamente"); if (uploadedImagesData.length === 0) { - showNotification('Por favor, sube al menos una imagen primero.', 'error'); + showNotification("Por favor, sube al menos una imagen primero.", "error"); console.log("⚠️ No hay imágenes subidas."); return; } - const quantity = document.querySelector('.quantity-field input').value; - const isCustomSize = selectedOptionSize == 'custom'; + const quantity = document.querySelector(".quantity-field input").value; + const isCustomSize = selectedOptionSize == "custom"; let size, customWidth, customHeight, pricePerUnit; if (isCustomSize) { - customWidth = document.getElementById('custom-size__width').value; - customHeight = document.getElementById('custom-size__height').value; + customWidth = document.getElementById("custom-size__width").value; + customHeight = document.getElementById("custom-size__height").value; if (!customWidth || !customHeight) { - showNotification('Ingresa las medidas personalizadas', 'error'); + showNotification("Ingresa las medidas personalizadas", "error"); return; } size = `${customWidth}x${customHeight}`; - pricePerUnit = getSelectedSizePrice(); + pricePerUnit = parseInt(customHeight) * parseInt(customWidth) * 0.03; + pricePerUnit = Math.round(pricePerUnit); } else { size = sizePicker.getValue(); pricePerUnit = getSelectedSizePrice(); } + console.log("🚀 pricePerUnit", pricePerUnit); - const comments = document.querySelector('.comment-area').value; - const withFrame = document.querySelector('input[name="frame"]:checked').value !== 'sin-marco'; - let frame = 'sin-marco'; + const comments = document.querySelector(".comment-area").value; + const withFrame = + document.querySelector('input[name="frame"]:checked').value !== "sin-marco"; + let frame = "sin-marco"; if (withFrame) { - const selectedFrameElement = document.querySelector('#frame-picker .swiper-slide.selected'); - frame = selectedFrameElement ? currentSelectedFrame : 'frame1'; + const selectedFrameElement = document.querySelector( + "#frame-picker .swiper-slide.selected" + ); + frame = selectedFrameElement ? currentSelectedFrame : "frame1"; } - if (typeof ajax_object === 'undefined') { - console.error("❌ ERROR: ajax_object no está definido. Verifica que el script de WordPress está cargando correctamente."); - showNotification("Error de configuración. Contacte con el administrador.", "error"); + if (typeof ajax_object === "undefined") { + console.error( + "❌ ERROR: ajax_object no está definido. Verifica que el script de WordPress está cargando correctamente." + ); + showNotification( + "Error de configuración. Contacte con el administrador.", + "error" + ); return; } @@ -647,32 +718,32 @@ function addToCart() { // --- NUEVO: Enviar todas las imágenes en un solo FormData --- const formData = new FormData(); - formData.append('action', 'process_print_image'); - formData.append('nonce', ajax_object.nonce); - formData.append('quantity', quantity); - formData.append('size', size); - formData.append('price', pricePerUnit); - formData.append('is_custom_size', isCustomSize); - formData.append('custom_width', customWidth || 0); - formData.append('custom_height', customHeight || 0); - formData.append('comments', comments); - formData.append('frame', frame); + formData.append("action", "process_print_image"); + formData.append("nonce", ajax_object.nonce); + formData.append("quantity", quantity); + formData.append("size", size); + formData.append("price", pricePerUnit); + formData.append("is_custom_size", isCustomSize); + formData.append("custom_width", customWidth || 0); + formData.append("custom_height", customHeight || 0); + formData.append("comments", comments); + formData.append("frame", frame); // Agregar todas las imágenes como image_data[] uploadedImagesData.forEach((imageData, idx) => { - formData.append('image_data[]', imageData.file, imageData.file.name); + formData.append("image_data[]", imageData.file, imageData.file.name); }); fetch(ajax_object.ajax_url, { - method: 'POST', + method: "POST", body: formData, - credentials: 'same-origin' + credentials: "same-origin", }) - .then(response => response.json()) - .then(data => { + .then((response) => response.json()) + .then((data) => { hideLoadingIndicator(); if (data.success) { - showNotification('¡Imágenes añadidas al carrito!', 'success'); + showNotification("¡Imágenes añadidas al carrito!", "success"); if (data.data && data.data.cart_url) { setTimeout(() => { window.location.href = data.data.cart_url; @@ -680,22 +751,28 @@ function addToCart() { } // Limpiar imágenes uploadedImagesData = []; - imagesGrid.innerHTML = ''; - uploadElements.classList.remove('has-images'); + imagesGrid.innerHTML = ""; + uploadElements.classList.remove("has-images"); } else { - showNotification(data.data || 'Error al procesar las imágenes.', 'error'); + showNotification( + data.data || "Error al procesar las imágenes.", + "error" + ); } }) - .catch(error => { + .catch((error) => { hideLoadingIndicator(); - showNotification('Error al procesar las imágenes. Por favor intenta de nuevo.', 'error'); + showNotification( + "Error al procesar las imágenes. Por favor intenta de nuevo.", + "error" + ); console.error(error); }); } // Función para mostrar notificaciones -function showNotification(message, type = 'info') { - const notification = document.createElement('div'); +function showNotification(message, type = "info") { + const notification = document.createElement("div"); notification.className = `notification ${type}`; notification.textContent = message; document.body.appendChild(notification); @@ -703,30 +780,30 @@ function showNotification(message, type = 'info') { // Agregar el mensaje al log de la consola console.log(`[${type.toUpperCase()}] ${message}`); - setTimeout(() => notification.classList.add('show'), 10); + setTimeout(() => notification.classList.add("show"), 10); setTimeout(() => { - notification.classList.remove('show'); + notification.classList.remove("show"); setTimeout(() => document.body.removeChild(notification), 500); }, 3000); } // Función para mostrar indicador de carga mejorado function showLoadingIndicator() { - const loadingOverlay = document.createElement('div'); - loadingOverlay.className = 'loading-overlay'; + const loadingOverlay = document.createElement("div"); + loadingOverlay.className = "loading-overlay"; loadingOverlay.innerHTML = `

Procesando imagen 1/${uploadedImagesData.length}...

`; document.body.appendChild(loadingOverlay); - setTimeout(() => loadingOverlay.classList.add('show'), 10); + setTimeout(() => loadingOverlay.classList.add("show"), 10); } // Función para ocultar indicador de carga function hideLoadingIndicator() { - const loadingOverlay = document.querySelector('.loading-overlay'); + const loadingOverlay = document.querySelector(".loading-overlay"); if (loadingOverlay) { - loadingOverlay.classList.remove('show'); + loadingOverlay.classList.remove("show"); setTimeout(() => { if (document.body.contains(loadingOverlay)) { @@ -736,33 +813,52 @@ function hideLoadingIndicator() { } } -// Función para calcular el precio total (Actualizada para múltiples imágenes) +// Función para calcular el precio total function updateTotalPrice() { if (uploadedImagesData.length === 0) { document.getElementById("total-price-button").innerText = `S/. 0.00`; return; } - const quantity = parseInt(document.querySelector('.quantity-field input').value) || 1; + const quantity = + parseInt(document.querySelector(".quantity-field input").value) || 1; + let pricePerUnit = 0; - // Use the sizePicker instance to get the value instead of DOM selection - selectedSize = sizePicker.getValue(); + if (selectedOptionSize === "custom") { + const width = + parseInt(document.getElementById("custom-size__width").value) || 0; + const height = + parseInt(document.getElementById("custom-size__height").value) || 0; - let pricePerUnit = 0; + if (width > 0 && height > 0) { + // Primero calculamos el precio según la fórmula + pricePerUnit = Math.round(height * width * 0.03); - if (selectedSize) { - pricePerUnit = currentSizePrices[selectedSize] || 0; - } + const totalPrice = pricePerUnit * quantity; + document.getElementById( + "total-price-button" + ).innerText = `S/. ${totalPrice.toFixed(2)}`; + return; + } else { + document.getElementById("total-price-button").innerText = `S/. 0.00`; + return; + } + } else { + //Si se utilice los pickers + selectedSize = sizePicker.getValue(); - // Calculate the total price considering all images - const totalPrice = pricePerUnit * quantity; + if (selectedSize) { + pricePerUnit = currentSizePrices[selectedSize] || 0; + } - // Round - const roundedPrice = totalPrice.toFixed(2); + const totalPrice = pricePerUnit * quantity; - // Update total price text on button - document.getElementById("total-price-button").innerText = `S/. ${roundedPrice}`; + // Update total price text on button + document.getElementById( + "total-price-button" + ).innerText = `S/. ${totalPrice.toFixed(2)}`; + } } // Exponer la función al ámbito global para evitar problemas con `onclick` -window.addToCart = addToCart; \ No newline at end of file +window.addToCart = addToCart; diff --git a/web-form-plugin.php b/web-form-plugin.php index ee7977b..216f16f 100644 --- a/web-form-plugin.php +++ b/web-form-plugin.php @@ -2,7 +2,7 @@ /* Plugin Name: Web Form Plugin Description: Un plugin para integrar mi aplicación HTML, CSS y JS en WordPress con WooCommerce. -Version: 3.1.2 +Version: 3.1.3 Author: Enmanuel */ @@ -46,10 +46,10 @@ function web_form_shortcode() wp_enqueue_style('viewer-css', 'https://cdnjs.cloudflare.com/ajax/libs/viewerjs/1.11.7/viewer.css', array(), '1.11.7'); // Cargar IOS-Picker - wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.1', true); + wp_enqueue_script('picker-script-js', $picker_path, array('jquery'), '2.0.2', true); // Cargar JS - wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.6', true); + wp_enqueue_script('script-js', $js_path, array('jquery'), '5.6.7', true); // Cargar Swiper JS desde CDN (depende de jQuery) wp_enqueue_script('swiper-js', 'https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js', array('jquery'), '11.0.0', true); @@ -291,20 +291,26 @@ function update_cart_item_price($cart) $product = $cart_item['data']; $quantity = $cart_item['quantity']; - // CORREGIDO: Obtener el tamaño del producto - $size = isset($cart_item['size']) ? $cart_item['size'] : ''; + $is_custom_size = isset($cart_item['is_custom_size']) ? $cart_item['is_custom_size'] : false; - if (!empty($size)) { - if ($quantity >= 12 && isset($bulk_size_prices_12[$size])) { - $precio_final = $bulk_size_prices_12[$size]; - } elseif ($quantity >= 4 && isset($bulk_size_prices[$size])) { - $precio_final = $bulk_size_prices[$size]; - } elseif (isset($size_prices[$size])) { - $precio_final = $size_prices[$size]; - } - - if (isset($precio_final)) { - $product->set_price($precio_final); + if ($is_custom_size && isset($cart_item['price'])) { + // Si es tamaño personalizado, usar el precio enviado + $precio_final = floatval($cart_item['price']); + $product->set_price($precio_final); + } else { + // Lógica existente para tamaños estándar + $size = isset($cart_item['size']) ? $cart_item['size'] : ''; + if (!empty($size)) { + if ($quantity >= 12 && isset($bulk_size_prices_12[$size])) { + $precio_final = $bulk_size_prices_12[$size]; + } elseif ($quantity >= 4 && isset($bulk_size_prices[$size])) { + $precio_final = $bulk_size_prices[$size]; + } elseif (isset($size_prices[$size])) { + $precio_final = $size_prices[$size]; + } + if (isset($precio_final)) { + $product->set_price($precio_final); + } } } } From 23e58b35966b4147fe7701f2dcb3e71c36681bbb Mon Sep 17 00:00:00 2001 From: Enmanuel Date: Mon, 5 May 2025 11:42:49 -0500 Subject: [PATCH 21/72] =?UTF-8?q?Ajustes=20en=20dise=C3=B1o=20responsive?= =?UTF-8?q?=20y=20eliminaci=C3=B3n=20de=20alerta=20en=20IOS=20picker?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- index.php | 90 ++++++++++++++++++++++----------------------- ios-picker.js | 2 +- styles.css | 22 +++++------ web-form-plugin.php | 8 ++-- 4 files changed, 60 insertions(+), 62 deletions(-) diff --git a/index.php b/index.php index d50b549..9a6c112 100644 --- a/index.php +++ b/index.php @@ -64,65 +64,63 @@
- Comentarios adicionales para tu diseño + Indicaciones de impresión
-
- - + + + +
-
-