diff --git a/backend/server.js b/backend/server.js index d827b1a..395e866 100644 --- a/backend/server.js +++ b/backend/server.js @@ -22,6 +22,7 @@ const SECRET = process.env.SESSION_SECRET || 'infinity-cambia-esta-clave-en-prod const DATA_DIR = path.join(__dirname, 'data'); const FRONTEND_DIR = path.join(__dirname, 'frontend'); const IMG_DIR = path.join(__dirname, 'Imagenes'); +const PEDIDOS_DIR = path.join(__dirname, 'pedidos'); // ════════════════════════════════ // MIDDLEWARES @@ -30,6 +31,7 @@ app.use(express.json()); app.use(cookieParser(SECRET)); app.use(express.static(FRONTEND_DIR)); app.use('/Imagenes', express.static(IMG_DIR, { maxAge: '7d' })); +app.use('/pedidos', express.static(PEDIDOS_DIR)); // Evita el 404 de favicon.ico app.get('/favicon.ico', (req, res) => res.status(204).end()); @@ -1886,6 +1888,161 @@ app.post('/api/descargas/fotos', requireDescargasAccess, async (req, res) => { } }); +// ════════════════════════════════ +// PEDIDOS — generar PDF con pedido +// ════════════════════════════════ +app.post('/api/pedidos/generar-pdf', async (req, res) => { + const { items, cliente } = req.body || {}; + + if (!Array.isArray(items) || items.length === 0) { + return res.status(400).json({ error: 'El pedido debe tener al menos un producto' }); + } + if (!cliente || !cliente.nombre || !cliente.ciudad || !cliente.telefono || !cliente.direccion) { + return res.status(400).json({ error: 'Todos los datos del cliente son obligatorios' }); + } + + try { + const filename = `pedido-${Date.now()}.pdf`; + const filepath = path.join(PEDIDOS_DIR, filename); + + // Asegurar que la carpeta existe + await fs.mkdir(PEDIDOS_DIR, { recursive: true }); + + const doc = new PDFDocument({ + size: 'A4', + margins: { top: 50, bottom: 50, left: 50, right: 50 }, + info: { + Title: `Pedido - ${cliente.nombre}`, + Author: 'Infinity', + Subject: 'Pedido de productos' + } + }); + + const writeStream = fsSync.createWriteStream(filepath); + doc.pipe(writeStream); + + const pageW = doc.page.width; + const MARGIN_X = 50; + const pageH = doc.page.height; + + // === ENCABEZADO === + doc.fontSize(36).fillColor('#8b5cf6').font('Helvetica-Bold') + .text('∞ Infinity', MARGIN_X, 50, { width: pageW - 2 * MARGIN_X }); + + doc.moveTo(MARGIN_X, 100).lineTo(pageW - MARGIN_X, 100) + .strokeColor('#8b5cf6').lineWidth(1.5).stroke(); + + doc.fontSize(22).fillColor('#1a1a2e').font('Helvetica-Bold') + .text('Pedido', MARGIN_X, 120, { width: pageW - 2 * MARGIN_X }); + + const fechaStr = new Date().toLocaleDateString('es-CO', { + weekday: 'long', year: 'numeric', month: 'long', day: 'numeric', + hour: '2-digit', minute: '2-digit' + }); + doc.fontSize(10).fillColor('#8b8ba7').font('Helvetica') + .text(fechaStr, MARGIN_X, 150, { width: pageW - 2 * MARGIN_X }); + + // === DATOS DEL CLIENTE === + doc.fontSize(14).fillColor('#1a1a2e').font('Helvetica-Bold') + .text('Datos del cliente', MARGIN_X, 185, { width: pageW - 2 * MARGIN_X }); + + const clientFields = [ + ['Nombre', cliente.nombre], + ['Ciudad', cliente.ciudad], + ['Teléfono', cliente.telefono], + ['Dirección', cliente.direccion] + ]; + + let clientY = 210; + doc.fontSize(11).fillColor('#4a4a68').font('Helvetica'); + for (const [label, value] of clientFields) { + doc.text(`${label}: `, MARGIN_X, clientY, { continued: true }); + doc.fillColor('#1a1a2e').text(value, { width: pageW - 2 * MARGIN_X }); + clientY += 18; + } + + // === TABLA DE PRODUCTOS === + let tableY = clientY + 20; + doc.moveTo(MARGIN_X, tableY).lineTo(pageW - MARGIN_X, tableY) + .strokeColor('#e8e6f0').lineWidth(0.5).stroke(); + + tableY += 12; + const colRef = MARGIN_X; + const colNombre = MARGIN_X + 80; + const colCant = pageW - MARGIN_X - 180; + const colPrecio = pageW - MARGIN_X - 120; + const colSubtotal = pageW - MARGIN_X - 60; + + doc.fontSize(9).fillColor('#8b8ba7').font('Helvetica-Bold'); + doc.text('REF', colRef, tableY); + doc.text('Producto', colNombre, tableY); + doc.text('Cant', colCant, tableY, { align: 'center', width: 40 }); + doc.text('Precio', colPrecio, tableY, { align: 'right', width: 60 }); + doc.text('Total', colSubtotal, tableY, { align: 'right', width: 60 }); + + const headerY = tableY; + + let totalGeneral = 0; + for (const item of items) { + const subtotal = (item.precio || 0) * (item.cantidad || 1); + totalGeneral += subtotal; + + tableY += 22; + if (tableY > pageH - 90) { + doc.addPage(); + tableY = 60; + } + + doc.fontSize(9).fillColor('#8b8ba7').font('Helvetica') + .text(String(item.ref || '—'), colRef, tableY, { width: 76 }); + doc.fillColor('#1a1a2e').font('Helvetica') + .text(String(item.nombre || item.ref || '—'), colNombre, tableY, { width: colCant - colNombre - 10 }); + doc.fillColor('#1a1a2e').font('Helvetica-Bold') + .text(String(item.cantidad || 1), colCant, tableY, { align: 'center', width: 40 }); + doc.fillColor('#4a4a68').font('Helvetica') + .text(`$${(item.precio || 0).toLocaleString('es-CO')}`, colPrecio, tableY, { align: 'right', width: 60 }); + doc.fillColor('#8b5cf6').font('Helvetica-Bold') + .text(`$${subtotal.toLocaleString('es-CO')}`, colSubtotal, tableY, { align: 'right', width: 60 }); + } + + // Línea final + tableY += 20; + doc.moveTo(MARGIN_X, tableY).lineTo(pageW - MARGIN_X, tableY) + .strokeColor('#e8e6f0').lineWidth(0.5).stroke(); + + tableY += 14; + doc.fontSize(11).fillColor('#4a4a68').font('Helvetica'); + doc.text('Total pedido:', MARGIN_X, tableY, { continued: true }); + doc.fontSize(16).fillColor('#8b5cf6').font('Helvetica-Bold') + .text(` $${totalGeneral.toLocaleString('es-CO')}`, { width: pageW - 2 * MARGIN_X }); + + // === FOOTER con número de página === + const range = doc.bufferedPageRange(); + for (let i = range.start; i < range.start + range.count; i++) { + doc.switchToPage(i); + doc.fontSize(8).fillColor('#8b8ba7').font('Helvetica') + .text(`${i + 1} / ${range.count}`, + MARGIN_X, pageH - 40, { width: pageW - 2 * MARGIN_X, align: 'center' }); + } + + doc.end(); + + // Esperar a que termine de escribir + await new Promise((resolve, reject) => { + writeStream.on('finish', resolve); + writeStream.on('error', reject); + }); + + // Enviar el archivo + res.setHeader('Content-Type', 'application/pdf'); + res.setHeader('Content-Disposition', `attachment; filename="${filename}"`); + res.sendFile(filepath); + } catch (err) { + console.error('Pedido PDF error:', err); + res.status(500).json({ error: 'Error al generar el pedido: ' + err.message }); + } +}); + // ════════════════════════════════ // START // ════════════════════════════════ diff --git a/data/productos.json b/data/productos.json index 64ac9f7..d5c8e58 100644 --- a/data/productos.json +++ b/data/productos.json @@ -57,7 +57,9 @@ "dama", "aaa" ], - "img": "bolsos/dama/aaa/AF100-197.webp" + "img": "bolsos/dama/aaa/AF100-197.webp", + "nombre": "BOLSO", + "precio": 10000 }, { "id": "AF100-198", @@ -67,7 +69,9 @@ "dama", "aaa" ], - "img": "bolsos/dama/aaa/AF100-198.webp" + "img": "bolsos/dama/aaa/AF100-198.webp", + "nombre": "BOLSO", + "precio": 10000 }, { "id": "AF100-199", @@ -77,7 +81,9 @@ "dama", "aaa" ], - "img": "bolsos/dama/aaa/AF100-199.webp" + "img": "bolsos/dama/aaa/AF100-199.webp", + "nombre": "BOLSO", + "precio": 10000 }, { "id": "AF100-201", @@ -87,7 +93,9 @@ "dama", "aaa" ], - "img": "bolsos/dama/aaa/AF100-201.webp" + "img": "bolsos/dama/aaa/AF100-201.webp", + "nombre": "BOLSO", + "precio": 10000 }, { "id": "AF100-202", diff --git a/docker-compose.yml b/docker-compose.yml index 70904ab..98a78aa 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -10,6 +10,8 @@ services: # Persistencia: los cambios en JSON se guardan en el host - ./data:/app/data - ./Imagenes:/app/Imagenes + - ./pedidos:/app/pedidos + - ./frontend:/app/frontend environment: - NODE_ENV=production - PORT=3000 diff --git a/frontend/app.js b/frontend/app.js index cfd9baf..69451db 100644 --- a/frontend/app.js +++ b/frontend/app.js @@ -275,10 +275,21 @@ function renderProductos() {

${escapeHtml(nombre)}

${desc ? `

${escapeHtml(desc)}

` : ''}
${precio}
+
+ + +
`; }).join(''); + + wireAddToCart(); } // ════════════════════════════════ @@ -328,11 +339,256 @@ function setupNav() { }); } +// ════════════════════════════════ +// CARRITO DE COMPRAS +// ════════════════════════════════ +let carrito = []; // { ref, nombre, precio, cantidad } + +function actualizarBadge() { + const total = carrito.reduce((s, i) => s + i.cantidad, 0); + const badge = document.getElementById('cartBadge'); + badge.textContent = total; + badge.style.display = total > 0 ? 'flex' : 'none'; +} + +function actualizarTotal() { + const total = carrito.reduce((s, i) => s + i.precio * i.cantidad, 0); + document.querySelector('#cartTotal strong').textContent = + total > 0 ? `$${total.toLocaleString('es-CO')}` : '$0'; + document.getElementById('cartPdfBtn').disabled = carrito.length === 0; +} + +function renderCarritoModal() { + const body = document.getElementById('cartBody'); + if (carrito.length === 0) { + body.innerHTML = '

El carrito está vacío.

'; + actualizarTotal(); + return; + } + + const totalGeneral = carrito.reduce((s, i) => s + i.precio * i.cantidad, 0); + + body.innerHTML = `
+ ${carrito.map((item, idx) => { + const subtotal = item.precio * item.cantidad; + return `
+
+
${escapeHtml(item.nombre)}
+
${escapeHtml(item.ref)} — $${(item.precio || 0).toLocaleString('es-CO')} c/u
+
+
+ + ${item.cantidad} + +
+
$${subtotal.toLocaleString('es-CO')}
+ +
`; + }).join('')} +
`; + + body.querySelectorAll('[data-cart-inc]').forEach(btn => { + btn.addEventListener('click', () => { + const idx = parseInt(btn.dataset.cartInc, 10); + carrito[idx].cantidad++; + renderCarritoModal(); + actualizarBadge(); + }); + }); + + body.querySelectorAll('[data-cart-dec]').forEach(btn => { + btn.addEventListener('click', () => { + const idx = parseInt(btn.dataset.cartDec, 10); + if (carrito[idx].cantidad > 1) { + carrito[idx].cantidad--; + } else { + carrito.splice(idx, 1); + } + renderCarritoModal(); + actualizarBadge(); + }); + }); + + body.querySelectorAll('[data-cart-remove]').forEach(btn => { + btn.addEventListener('click', () => { + const idx = parseInt(btn.dataset.cartRemove, 10); + carrito.splice(idx, 1); + renderCarritoModal(); + actualizarBadge(); + }); + }); + + actualizarTotal(); +} + +// Modales +function abrirModal(id) { + document.getElementById(id).classList.add('open'); + document.body.style.overflow = 'hidden'; +} + +function cerrarModal(id) { + document.getElementById(id).classList.remove('open'); + document.body.style.overflow = ''; +} + +function setupCartModals() { + // FAB → abrir carrito + document.getElementById('cartFab').addEventListener('click', () => { + renderCarritoModal(); + abrirModal('cartModal'); + }); + + // Cerrar carrito + document.querySelectorAll('[data-cart-close]').forEach(el => { + el.addEventListener('click', () => cerrarModal('cartModal')); + }); + + // Cerrar pedido + document.querySelectorAll('[data-order-close]').forEach(el => { + el.addEventListener('click', () => cerrarModal('orderModal')); + }); + + // Vaciar carrito + document.getElementById('cartClearBtn').addEventListener('click', () => { + carrito = []; + renderCarritoModal(); + actualizarBadge(); + }); + + // Generar PDF → abrir formulario + document.getElementById('cartPdfBtn').addEventListener('click', () => { + cerrarModal('cartModal'); + document.getElementById('orderForm').reset(); + document.getElementById('orderError').classList.remove('show'); + abrirModal('orderModal'); + }); + + // Enviar pedido + document.getElementById('orderSubmitBtn').addEventListener('click', enviarPedido); + + // Enter en el formulario también envía + document.getElementById('orderForm').addEventListener('submit', e => { + e.preventDefault(); + enviarPedido(); + }); +} + +async function enviarPedido() { + const nombre = document.getElementById('orderNombre').value.trim(); + const ciudad = document.getElementById('orderCiudad').value.trim(); + const telefono = document.getElementById('orderTelefono').value.trim(); + const direccion = document.getElementById('orderDireccion').value.trim(); + const errEl = document.getElementById('orderError'); + + if (!nombre || !ciudad || !telefono || !direccion) { + errEl.textContent = 'Todos los campos son obligatorios'; + errEl.classList.add('show'); + return; + } + + errEl.classList.remove('show'); + + const btn = document.getElementById('orderSubmitBtn'); + const textEl = btn.querySelector('.btn-text'); + const spin = btn.querySelector('.btn-spinner'); + btn.disabled = true; + textEl.textContent = 'Generando…'; + spin.style.display = 'inline-block'; + + try { + const res = await fetch('/api/pedidos/generar-pdf', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + items: carrito.map(i => ({ ref: i.ref, nombre: i.nombre, precio: i.precio, cantidad: i.cantidad })), + cliente: { nombre, ciudad, telefono, direccion } + }) + }); + + if (!res.ok) { + const data = await res.json().catch(() => ({})); + throw new Error(data.error || 'Error al generar el pedido'); + } + + // Descargar PDF + const blob = await res.blob(); + const url = URL.createObjectURL(blob); + const a = document.createElement('a'); + a.href = url; + + const dispo = res.headers.get('Content-Disposition') || ''; + const match = dispo.match(/filename="(.+)"/); + a.download = match ? match[1] : `pedido-${Date.now()}.pdf`; + + document.body.appendChild(a); + a.click(); + a.remove(); + URL.revokeObjectURL(url); + + // Limpiar carrito y cerrar modal + carrito = []; + actualizarBadge(); + cerrarModal('orderModal'); + } catch (err) { + errEl.textContent = err.message; + errEl.classList.add('show'); + } finally { + btn.disabled = false; + textEl.textContent = '✅ Generar pedido'; + spin.style.display = 'none'; + } +} + +// Wire add-to-cart buttons después de renderizar productos +function wireAddToCart() { + document.querySelectorAll('.producto-card-add-btn').forEach(btn => { + if (btn.dataset.cartWired) return; + btn.dataset.cartWired = '1'; + + btn.addEventListener('click', () => { + const ref = btn.dataset.ref; + const nombre = btn.dataset.nombre; + const precio = parseFloat(btn.dataset.precio); + const qtyInput = document.querySelector(`.producto-card-add-qty[data-ref="${ref}"]`); + const cantidad = qtyInput ? parseInt(qtyInput.value, 10) : 1; + + if (!cantidad || cantidad < 1) return; + + // Mostrar feedback si el producto no tiene precio + if (!precio || isNaN(precio)) { + const orig = btn.textContent; + btn.textContent = '⚠ Sin precio'; + btn.style.opacity = '0.7'; + setTimeout(() => { btn.textContent = orig; btn.style.opacity = '1'; }, 1500); + return; + } + + const existente = carrito.find(i => i.ref === ref); + if (existente) { + existente.cantidad += cantidad; + } else { + carrito.push({ ref, nombre, precio, cantidad }); + } + + if (qtyInput) qtyInput.value = 1; + actualizarBadge(); + + const original = btn.textContent; + btn.textContent = '✓ Agregado'; + setTimeout(() => { btn.textContent = original; }, 1200); + }); + }); +} + // ════════════════════════════════ // INIT // ════════════════════════════════ document.addEventListener('DOMContentLoaded', () => { setupSearch(); setupNav(); + setupCartModals(); cargarDatos(); }); + + diff --git a/frontend/estilos.css b/frontend/estilos.css index 83174b2..ef35eca 100644 --- a/frontend/estilos.css +++ b/frontend/estilos.css @@ -887,3 +887,394 @@ section { font-size: 2rem; } } + +/* ════════════════════════════════ + CARRITO DE COMPRAS + ════════════════════════════════ */ + +/* Botón flotante del carrito */ +.cart-fab { + position: fixed; + bottom: 28px; + right: 28px; + z-index: 900; + width: 60px; + height: 60px; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #8b5cf6, #a78bfa); + color: #fff; + font-size: 1.5rem; + cursor: pointer; + box-shadow: 0 4px 20px rgba(139, 92, 246, 0.4); + display: flex; + align-items: center; + justify-content: center; + transition: transform 0.2s, box-shadow 0.2s; +} + +.cart-fab:hover { + transform: scale(1.08); + box-shadow: 0 6px 28px rgba(139, 92, 246, 0.55); +} + +.cart-fab:active { + transform: scale(0.95); +} + +.cart-fab-badge { + position: absolute; + top: -4px; + right: -4px; + min-width: 22px; + height: 22px; + padding: 0 6px; + border-radius: 11px; + background: #ef4444; + color: #fff; + font-size: 0.7rem; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + font-family: 'Plus Jakarta Sans', sans-serif; + box-shadow: 0 2px 6px rgba(239, 68, 68, 0.4); +} + +/* Modal genérico (compartido con carrito y pedido) */ +.modal { + display: none; + position: fixed; + inset: 0; + z-index: 1000; + align-items: center; + justify-content: center; +} + +.modal.open { + display: flex; +} + +.modal-backdrop { + position: absolute; + inset: 0; + background: rgba(0, 0, 0, 0.45); + backdrop-filter: blur(4px); +} + +.modal-content { + position: relative; + background: #fff; + border-radius: 16px; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.25); + max-width: 520px; + width: 90vw; + max-height: 85vh; + display: flex; + flex-direction: column; + animation: modalIn 0.25s ease-out; +} + +@keyframes modalIn { + from { opacity: 0; transform: translateY(20px) scale(0.97); } + to { opacity: 1; transform: translateY(0) scale(1); } +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 20px 24px 0; +} + +.modal-header h2 { + font-family: 'DM Serif Display', serif; + font-size: 1.4rem; + color: #1a1a2e; + margin: 0; +} + +.modal-close { + background: none; + border: none; + font-size: 1.3rem; + color: #8b8ba7; + cursor: pointer; + padding: 4px 8px; + border-radius: 6px; + transition: background 0.15s; +} + +.modal-close:hover { + background: #f3f0ff; + color: #1a1a2e; +} + +.modal-body { + padding: 20px 24px; + overflow-y: auto; + flex: 1; +} + +.modal-footer { + padding: 16px 24px 20px; + border-top: 1px solid #e8e6f0; + display: flex; + flex-direction: column; + gap: 12px; +} + +/* Carrito — lista de items */ +.cart-items { + display: flex; + flex-direction: column; + gap: 10px; +} + +.cart-item { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background: #f8f7fc; + border-radius: 10px; +} + +.cart-item-info { + flex: 1; + min-width: 0; +} + +.cart-item-name { + font-size: 0.85rem; + font-weight: 600; + color: #1a1a2e; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.cart-item-ref { + font-size: 0.72rem; + color: #8b8ba7; +} + +.cart-item-controls { + display: flex; + align-items: center; + gap: 6px; +} + +.cart-item-qty-btn { + width: 28px; + height: 28px; + border: 1px solid #d4d0e0; + border-radius: 6px; + background: #fff; + color: #1a1a2e; + font-size: 1rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: background 0.15s; +} + +.cart-item-qty-btn:hover { + background: #f3f0ff; +} + +.cart-item-qty { + font-size: 0.9rem; + font-weight: 600; + color: #1a1a2e; + min-width: 24px; + text-align: center; +} + +.cart-item-subtotal { + font-size: 0.85rem; + font-weight: 700; + color: #8b5cf6; + min-width: 70px; + text-align: right; +} + +.cart-item-remove { + background: none; + border: none; + color: #ef4444; + cursor: pointer; + font-size: 1rem; + padding: 4px; + border-radius: 4px; + transition: background 0.15s; +} + +.cart-item-remove:hover { + background: #fef2f2; +} + +.cart-empty { + text-align: center; + color: #8b8ba7; + padding: 32px 0; + font-size: 0.95rem; +} + +.cart-total { + display: flex; + align-items: center; + justify-content: space-between; + font-size: 1.05rem; + color: #1a1a2e; +} + +.cart-total strong { + font-size: 1.3rem; + color: #8b5cf6; +} + +.cart-actions { + display: flex; + gap: 10px; + justify-content: flex-end; +} + +/* Formulario de pedido */ +.order-modal-content { + max-width: 440px; +} + +.form-group { + margin-bottom: 14px; +} + +.form-group label { + display: block; + font-size: 0.82rem; + font-weight: 600; + color: #4a4a68; + margin-bottom: 4px; +} + +.form-group input { + width: 100%; + padding: 10px 14px; + border: 1.5px solid #d4d0e0; + border-radius: 10px; + font-size: 0.9rem; + font-family: 'Plus Jakarta Sans', sans-serif; + color: #1a1a2e; + background: #fafaff; + transition: border-color 0.2s, box-shadow 0.2s; + box-sizing: border-box; +} + +.form-group input:focus { + outline: none; + border-color: #8b5cf6; + box-shadow: 0 0 0 3px rgba(139, 92, 246, 0.15); +} + +.form-error { + color: #ef4444; + font-size: 0.82rem; + display: none; + margin-bottom: 8px; +} + +.form-error.show { + display: block; +} + +/* Botón "Agregar" en tarjetas de producto */ +.producto-card-add { + display: flex; + align-items: center; + gap: 6px; + margin-top: 10px; + padding-top: 10px; + border-top: 1px solid #e8e6f0; +} + +.producto-card-add-qty { + width: 48px; + padding: 6px 8px; + border: 1.5px solid #d4d0e0; + border-radius: 8px; + font-size: 0.82rem; + font-weight: 600; + text-align: center; + font-family: 'Plus Jakarta Sans', sans-serif; + color: #1a1a2e; + background: #fff; + transition: border-color 0.2s; + box-sizing: border-box; +} + +.producto-card-add-qty:focus { + outline: none; + border-color: #8b5cf6; +} + +.producto-card-add-btn { + flex: 1; + padding: 7px 12px; + border: none; + border-radius: 8px; + background: linear-gradient(135deg, #8b5cf6, #a78bfa); + color: #fff; + font-size: 0.78rem; + font-weight: 600; + font-family: 'Plus Jakarta Sans', sans-serif; + cursor: pointer; + transition: opacity 0.2s, transform 0.15s; + display: flex; + align-items: center; + justify-content: center; + gap: 4px; +} + +.producto-card-add-btn:hover { + opacity: 0.9; + transform: translateY(-1px); +} + +.producto-card-add-btn:active { + transform: scale(0.97); +} + +/* Spinner en botones */ +.btn-spinner { + margin-left: 6px; +} + +/* Responsive carrito */ +@media (max-width: 480px) { + .cart-fab { + width: 52px; + height: 52px; + bottom: 20px; + right: 20px; + font-size: 1.3rem; + } + + .modal-content { + width: 95vw; + border-radius: 12px; + } + + .cart-item { + flex-wrap: wrap; + } + + .cart-actions { + flex-direction: column; + } + + .cart-actions .btn { + width: 100%; + justify-content: center; + } +} diff --git a/frontend/index.html b/frontend/index.html index bb3a3b1..8efb504 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -9,7 +9,7 @@ - + @@ -102,6 +102,82 @@ - + + + + + + + + + + diff --git a/pedidos/pedido-1781012420688.pdf b/pedidos/pedido-1781012420688.pdf new file mode 100644 index 0000000..fb25417 Binary files /dev/null and b/pedidos/pedido-1781012420688.pdf differ diff --git a/pedidos/pedido-1781012802257.pdf b/pedidos/pedido-1781012802257.pdf new file mode 100644 index 0000000..f98b63d Binary files /dev/null and b/pedidos/pedido-1781012802257.pdf differ diff --git a/pedidos/pedido-1781017023680.pdf b/pedidos/pedido-1781017023680.pdf new file mode 100644 index 0000000..c5566fd Binary files /dev/null and b/pedidos/pedido-1781017023680.pdf differ