diff --git a/ap-pos/.dockerignore b/ap-pos/.dockerignore index cf62782..e5924f4 100644 --- a/ap-pos/.dockerignore +++ b/ap-pos/.dockerignore @@ -2,6 +2,3 @@ node_modules .git .gitignore - -# Excluir la base de datos de desarrollo para que se pueda usar un volumen en su lugar -ap-pos.db diff --git a/ap-pos/Dockerfile b/ap-pos/Dockerfile index b1bead0..678353a 100644 --- a/ap-pos/Dockerfile +++ b/ap-pos/Dockerfile @@ -1,27 +1,25 @@ -# Usar una imagen base de Node.js +# 1. Imagen Base: Usamos una imagen oficial de Node.js, versión 18-alpine. +# 'alpine' es una versión muy ligera, ideal para producción. FROM node:18-alpine -# Establecer el directorio de trabajo en el contenedor -WORKDIR /usr/src/app +# 2. Directorio de Trabajo: Establecemos el directorio de trabajo dentro del contenedor. +WORKDIR /app -# Copiar package.json y package-lock.json +# 3. Copiar Archivos de Dependencias: Copiamos package.json y package-lock.json. +# Hacemos esto en un paso separado para aprovechar el caché de Docker. +# Si no cambian estos archivos, Docker no reinstalará las dependencias. COPY package*.json ./ -# Instalar las dependencias de la aplicación +# 4. Instalar Dependencias: Instalamos las dependencias del proyecto. RUN npm install -# Copiar el resto de los archivos de la aplicación +# 5. Copiar el Resto de la Aplicación: Copiamos todos los demás archivos. +# Los archivos en .dockerignore serán excluidos automáticamente. COPY . . -# Crear un directorio para la base de datos persistente y definirlo como volumen -RUN mkdir -p /usr/src/app/data -VOLUME /usr/src/app/data - -# Establecer la ruta de la base de datos a través de una variable de entorno -ENV DB_PATH /usr/src/app/data/ap-pos.db - -# Exponer el puerto en el que corre la aplicación +# 6. Exponer Puerto: Informamos a Docker que el contenedor escucha en el puerto 3000. EXPOSE 3000 -# Comando para iniciar la aplicación -CMD [ "node", "server.js" ] +# 7. Comando de Inicio: El comando que se ejecutará cuando el contenedor inicie. +# Usamos 'npm start' que definiste en tu package.json. +CMD [ "npm", "start" ] \ No newline at end of file diff --git a/ap-pos/index.html b/ap-pos/index.html index 3f13351..ded21ef 100644 --- a/ap-pos/index.html +++ b/ap-pos/index.html @@ -325,13 +325,9 @@ - - -
+ diff --git a/ap-pos/print.js b/ap-pos/print.js index d5b8a1d..d797ce4 100644 --- a/ap-pos/print.js +++ b/ap-pos/print.js @@ -66,21 +66,60 @@ function templateTicket(mov, settings) { if (settings.leyenda) lines.push(``); + // Sección de Encuesta con QR + lines.push('
'); + lines.push('
¡Tu opinión es muy importante!
'); + lines.push('
Escanea el código QR para darnos tu feedback.
'); + lines.push(''); + lines.push('
'); + lines.push(''); return lines.join(''); } /** - * Renderiza el ticket en el DOM y llama a la función de impresión del navegador. + * Renderiza el ticket en el DOM, genera el QR y llama a la función de impresión. * @param {object} mov El objeto del movimiento. * @param {object} settings El objeto de configuración. */ export function renderTicketAndPrint(mov, settings) { const printArea = document.getElementById('printArea'); - if (printArea) { - printArea.innerHTML = templateTicket(mov, settings); - window.print(); - } else { + if (!printArea) { console.error("El área de impresión #printArea no se encontró."); + alert("Error: No se encontró el área de impresión. Contacte al soporte."); + return; + } + + try { + // 1. Renderizar la estructura HTML del ticket + printArea.innerHTML = templateTicket(mov, settings); + + // 2. Encontrar el elemento canvas para el QR + const canvas = document.getElementById('qr-canvas'); + if (!canvas) { + console.error("El canvas del QR #qr-canvas no se encontró. Se imprimirá sin QR."); + setTimeout(() => window.print(), 100); // Imprimir sin QR + return; + } + + // 3. Generar el código QR + const qrUrl = 'http://vanityexperience.mx/qr'; + QRCode.toCanvas(canvas, qrUrl, { width: 140, margin: 1 }, function (error) { + if (error) { + console.error('Error al generar el código QR:', error); + // Ocultar el canvas si hay error y proceder a imprimir + canvas.style.display = 'none'; + } + + // 4. Llamar a la impresión después de un breve timeout + // Esto asegura que el navegador haya tenido tiempo de renderizar el QR + setTimeout(() => { + window.print(); + }, 150); + }); + + } catch (error) { + console.error("Error al intentar imprimir:", error); + alert(`Ocurrió un error al preparar la impresión: ${error.message}. Revise la consola para más detalles.`); } } diff --git a/ap-pos/styles.css b/ap-pos/styles.css index 30754a8..442277a 100644 --- a/ap-pos/styles.css +++ b/ap-pos/styles.css @@ -226,6 +226,17 @@ button.action-btn { .t-row { display: flex; justify-content: space-between; } .t-footer { margin-top: 10px; } +.t-qr-section { + margin-top: 10px; + padding-top: 10px; + border-top: 1px dashed #000; + text-align: center; +} +#qr-canvas { + margin: 5px auto; + display: block; +} + /***** MODO IMPRESIÓN *****/ @media print { @@ -235,7 +246,7 @@ button.action-btn { margin: 0; } - .no-print, .container { + .no-print, .container, .main-footer-credits { display: none !important; } @@ -245,7 +256,7 @@ button.action-btn { @page { size: 58mm auto; - margin: 0; + margin: 1cm 0; } .ticket {