mirror of
https://github.com/marcogll/ap_pos.git
synced 2026-01-13 13:15:16 +00:00
feat: enhance ticket printing with QR code and improve Dockerfile\n\n- Added a QR code to printed tickets for customer feedback.\n- Improved Dockerfile for better caching and using npm start.\n- Updated print styles and fixed a print area error.\n- Minor updates to index.html and .dockerignore.
This commit is contained in:
@@ -2,6 +2,3 @@
|
|||||||
node_modules
|
node_modules
|
||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
|
|
||||||
# Excluir la base de datos de desarrollo para que se pueda usar un volumen en su lugar
|
|
||||||
ap-pos.db
|
|
||||||
|
|||||||
@@ -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
|
FROM node:18-alpine
|
||||||
|
|
||||||
# Establecer el directorio de trabajo en el contenedor
|
# 2. Directorio de Trabajo: Establecemos el directorio de trabajo dentro del contenedor.
|
||||||
WORKDIR /usr/src/app
|
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 ./
|
COPY package*.json ./
|
||||||
|
|
||||||
# Instalar las dependencias de la aplicación
|
# 4. Instalar Dependencias: Instalamos las dependencias del proyecto.
|
||||||
RUN npm install
|
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 . .
|
COPY . .
|
||||||
|
|
||||||
# Crear un directorio para la base de datos persistente y definirlo como volumen
|
# 6. Exponer Puerto: Informamos a Docker que el contenedor escucha en el puerto 3000.
|
||||||
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
|
|
||||||
EXPOSE 3000
|
EXPOSE 3000
|
||||||
|
|
||||||
# Comando para iniciar la aplicación
|
# 7. Comando de Inicio: El comando que se ejecutará cuando el contenedor inicie.
|
||||||
CMD [ "node", "server.js" ]
|
# Usamos 'npm start' que definiste en tu package.json.
|
||||||
|
CMD [ "npm", "start" ]
|
||||||
@@ -325,13 +325,9 @@
|
|||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
<footer class="main-footer-credits">
|
|
||||||
<p>v0.3.0</p>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
<!-- Área de impresión oculta -->
|
|
||||||
<div id="printArea" class="no-print"></div>
|
<div id="printArea" class="no-print"></div>
|
||||||
|
|
||||||
|
<script src="https://cdn.jsdelivr.net/npm/qrcode@1/build/qrcode.min.js"></script>
|
||||||
<script type="module" src="app.js?v=1.3"></script>
|
<script type="module" src="app.js?v=1.3"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
@@ -66,21 +66,60 @@ function templateTicket(mov, settings) {
|
|||||||
|
|
||||||
if (settings.leyenda) lines.push(`<div class="t-footer t-center t-small">${esc(settings.leyenda)}</div>`);
|
if (settings.leyenda) lines.push(`<div class="t-footer t-center t-small">${esc(settings.leyenda)}</div>`);
|
||||||
|
|
||||||
|
// Sección de Encuesta con QR
|
||||||
|
lines.push('<div class="t-qr-section">');
|
||||||
|
lines.push('<div class="t-small t-bold">¡Tu opinión es muy importante!</div>');
|
||||||
|
lines.push('<div class="t-small">Escanea el código QR para darnos tu feedback.</div>');
|
||||||
|
lines.push('<canvas id="qr-canvas"></canvas>');
|
||||||
|
lines.push('</div>');
|
||||||
|
|
||||||
lines.push('</div>');
|
lines.push('</div>');
|
||||||
return lines.join('');
|
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} mov El objeto del movimiento.
|
||||||
* @param {object} settings El objeto de configuración.
|
* @param {object} settings El objeto de configuración.
|
||||||
*/
|
*/
|
||||||
export function renderTicketAndPrint(mov, settings) {
|
export function renderTicketAndPrint(mov, settings) {
|
||||||
const printArea = document.getElementById('printArea');
|
const printArea = document.getElementById('printArea');
|
||||||
if (printArea) {
|
if (!printArea) {
|
||||||
printArea.innerHTML = templateTicket(mov, settings);
|
|
||||||
window.print();
|
|
||||||
} else {
|
|
||||||
console.error("El área de impresión #printArea no se encontró.");
|
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.`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -226,6 +226,17 @@ button.action-btn {
|
|||||||
.t-row { display: flex; justify-content: space-between; }
|
.t-row { display: flex; justify-content: space-between; }
|
||||||
.t-footer { margin-top: 10px; }
|
.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 *****/
|
/***** MODO IMPRESIÓN *****/
|
||||||
@media print {
|
@media print {
|
||||||
@@ -235,7 +246,7 @@ button.action-btn {
|
|||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.no-print, .container {
|
.no-print, .container, .main-footer-credits {
|
||||||
display: none !important;
|
display: none !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -245,7 +256,7 @@ button.action-btn {
|
|||||||
|
|
||||||
@page {
|
@page {
|
||||||
size: 58mm auto;
|
size: 58mm auto;
|
||||||
margin: 0;
|
margin: 1cm 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.ticket {
|
.ticket {
|
||||||
|
|||||||
Reference in New Issue
Block a user