KITCHEN_HTML

Constant KITCHEN_HTML 

Source
pub(crate) const KITCHEN_HTML: &str = "<!DOCTYPE html>\r\n<html>\r\n<head>\r\n    <meta charset=\"UTF-8\">\r\n    <link rel=\"icon\" href=\"/favicon.ico\" type=\"image/x-icon\">\r\n    <title>Cocina - Sistema de Comandas</title>\r\n    <style>\r\n        body {\r\n            font-family: sans-serif;\r\n            margin: 0;\r\n            padding: 0;\r\n            display: flex;\r\n            flex-direction: row;\r\n            height: 100vh;\r\n            transition: background 0.3s, color 0.3s;\r\n        }\r\n\r\n        .dark-mode {\r\n            background-color: #111;\r\n            color: white;\r\n        }\r\n        .dark-mode .card { background: #333; color: white; }\r\n        .dark-mode .btn { background: #444; color: white; }\r\n        .dark-mode .card .btn { background: #888; }\r\n\r\n        .card .btn {\r\n            width: auto;\r\n            height: auto;\r\n            font-size: 0.85em;\r\n            padding: 4px 8px;\r\n            background: #666;\r\n            border: none;\r\n            border-radius: 4px;\r\n            color: white;\r\n            margin-top: 4px;\r\n        }\r\n\r\n        .card .btn.preparado {\r\n            background: #28a745;\r\n        }\r\n\r\n        .card .btn.recogido {\r\n            background: #dc3545;\r\n        }\r\n\r\n        #top-section {\r\n            width: 370px;\r\n            flex-shrink: 0;\r\n            padding: 10px;\r\n            display: flex;\r\n            flex-direction: column;\r\n            align-items: center;\r\n            border-right: 2px solid #ccc;\r\n            overflow-y: auto;\r\n            position: relative; /* Para que la notificaci\u{f3}n se posicione relativa a esta secci\u{f3}n */\r\n        }\r\n\r\n        #pedido {\r\n            font-size: 1.6em;\r\n            text-align: center;\r\n            width: 220px;\r\n            margin-bottom: 10px;\r\n        }\r\n\r\n        h2 {\r\n            margin: 5px 0 10px 0;\r\n            font-size: 1.4em;\r\n            flex-shrink: 0;\r\n        }\r\n\r\n        .dark-mode #fullscreen-toggle,\r\n        .dark-mode #abrir-pantalla-dos {\r\n            background-color: #444;\r\n            color: white;\r\n            border-radius: 5px;\r\n        }\r\n\r\n        .teclado, .fila-final {\r\n            display: flex;\r\n            justify-content: center;\r\n            gap: 10px;\r\n            margin-bottom: 15px;\r\n            flex-wrap: wrap;\r\n        }\r\n        .canales {\r\n            display: flex;\r\n            justify-content: center;\r\n            gap: 7px;\r\n            margin-bottom: 15px;\r\n            flex-wrap: wrap;\r\n        }\r\n        .teclado {\r\n            display: grid;\r\n            grid-template-columns: repeat(3, 50px);\r\n            justify-content: center;\r\n        }\r\n\r\n        .btn {\r\n            font-size: 1.2em;\r\n            width: 55px;\r\n            height: 55px;\r\n            cursor: pointer;\r\n        }\r\n\r\n        #bottom-section {\r\n            flex: 1;\r\n            padding: 20px;\r\n            overflow: hidden;\r\n            display: flex;\r\n            flex-direction: column;\r\n            height: calc(100vh - 40px);\r\n        }\r\n\r\n        .comandas-grid {\r\n            display: grid;\r\n            grid-template-columns: repeat(auto-fill, 106px);\r\n            gap: 30px;\r\n            justify-content: start;\r\n            width: 97%;\r\n            overflow-y: auto;\r\n            flex: 1;\r\n            padding: 10px;\r\n            border: 1px solid #ddd;\r\n            border-radius: 5px;\r\n            align-content: start;\r\n        }\r\n\r\n        .comandas-grid-doble {\r\n            display: flex;\r\n            justify-content: space-around;\r\n            align-items: flex-start;\r\n            gap: 20px;\r\n            flex: 1;\r\n            height: 100%;\r\n        }\r\n\r\n        .comandas-grid-doble > div {\r\n            flex: 1;\r\n            min-width: 0;\r\n            height: 100%;\r\n            display: flex;\r\n            flex-direction: column;\r\n        }\r\n\r\n        .card {\r\n            background: #eee;\r\n            border-radius: 8px;\r\n            padding: 8px;\r\n            text-align: center;\r\n            display: flex;\r\n            flex-direction: column;\r\n            justify-content: center;\r\n            align-items: center;\r\n            width: 106px;\r\n            height: 65px;\r\n        }\r\n\r\n        .card .comanda {\r\n            font-size: 1.2em;\r\n            font-weight: bold;\r\n            margin-bottom: 4px;\r\n        }\r\n\r\n        #top-buttons {\r\n            position: fixed;\r\n            top: 10px;\r\n            right: 10px;\r\n            display: flex;\r\n            gap: 30px;\r\n            z-index: 1000;\r\n        }\r\n\r\n        #fullscreen-toggle,\r\n        #theme-toggle {\r\n            background: transparent;\r\n            border: none;\r\n            font-size: 22px;\r\n            cursor: pointer;\r\n            width: 36px;\r\n            height: 36px;\r\n            display: flex;\r\n            align-items: center;\r\n            justify-content: center;\r\n            padding: 0;\r\n        }\r\n        .notification {\r\n            position: absolute;\r\n            top: 520px;\r\n            left: 105px; /* Centrado exacto con el teclado (185px - 75px) */\r\n            background: #f44336;\r\n            color: white;\r\n            padding: 15px 25px;\r\n            border-radius: 8px;\r\n            font-size: 1.1em;\r\n            font-weight: bold;\r\n            z-index: 9999;\r\n            box-shadow: 0 4px 15px rgba(0,0,0,0.3);\r\n            animation: slideInDown 0.3s ease-out;\r\n            width: 150px; /* Mismo ancho que el teclado */\r\n            text-align: center;\r\n        }\r\n\r\n        .notification.success {\r\n            background: #4CAF50;\r\n        }\r\n\r\n        .notification.warning {\r\n            background: #ff9800;\r\n        }\r\n\r\n        @keyframes slideInDown {\r\n            from {\r\n                opacity: 0;\r\n                transform: translateX(-50%) translateY(-20px);\r\n            }\r\n            to {\r\n                opacity: 1;\r\n                transform: translateX(-50%) translateY(0);\r\n            }\r\n        }\r\n\r\n        .dark-mode .notification {\r\n            background: #d32f2f;\r\n            box-shadow: 0 4px 20px rgba(255,255,255,0.2);\r\n        }\r\n\r\n        .dark-mode .notification.success {\r\n            background: #388e3c;\r\n        }\r\n\r\n        .dark-mode .notification.warning {\r\n            background: #f57c00;\r\n        }\r\n    </style>\r\n</head>\r\n<body>\r\n<div id=\"top-buttons\">\r\n    <button id=\"abrir-pantalla-dos\" onclick=\"abrirPantallaDos()\">\u{1f5a5}\u{fe0f}</button>\r\n    <button id=\"fullscreen-toggle\" onclick=\"toggleFullscreen()\">\u{26f6}</button>\r\n    <button id=\"theme-toggle\" onclick=\"toggleDarkMode()\">\u{1f317}</button>\r\n</div>\r\n\r\n<div id=\"top-section\">\r\n    <input type=\"text\" id=\"pedido\">\r\n    <div class=\"canales\">\r\n        <button class=\"btn\" onclick=\"add(\'F\')\">F</button>\r\n        <button class=\"btn\" onclick=\"add(\'G\')\">G</button>\r\n        <button class=\"btn\" onclick=\"add(\'S\')\">S</button>\r\n        <button class=\"btn\" onclick=\"add(\'U\')\">U</button>\r\n        <button class=\"btn\" onclick=\"add(\'J\')\">J</button>\r\n    </div>\r\n    <div class=\"teclado\">\r\n        <button class=\"btn\" onclick=\"add(\'7\')\">7</button>\r\n        <button class=\"btn\" onclick=\"add(\'8\')\">8</button>\r\n        <button class=\"btn\" onclick=\"add(\'9\')\">9</button>\r\n        <button class=\"btn\" onclick=\"add(\'4\')\">4</button>\r\n        <button class=\"btn\" onclick=\"add(\'5\')\">5</button>\r\n        <button class=\"btn\" onclick=\"add(\'6\')\">6</button>\r\n        <button class=\"btn\" onclick=\"add(\'1\')\">1</button>\r\n        <button class=\"btn\" onclick=\"add(\'2\')\">2</button>\r\n        <button class=\"btn\" onclick=\"add(\'3\')\">3</button>\r\n        <button class=\"btn\" onclick=\"add(\'0\')\">0</button>\r\n        <button class=\"btn\" onclick=\"add(\'-\')\">-</button>\r\n        <button class=\"btn\" onclick=\"add(\'+\')\">+</button>\r\n    </div>\r\n    <div class=\"fila-final\">\r\n        <button class=\"btn\" onclick=\"borrar()\">\u{2190}</button>\r\n        <button class=\"btn\" onclick=\"enviar()\">OK</button>\r\n    </div>\r\n</div>\r\n\r\n<div id=\"bottom-section\">\r\n    <h3 style=\"text-align:center; margin: 0 0 20px 0; flex-shrink: 0;\">Comandas activas</h3>\r\n    <div class=\"comandas-grid-doble\">\r\n        <div>\r\n            <h2 style=\"color: orange; text-align:center\">EN PREPARACI\u{d3}N</h2>\r\n            <div class=\"comandas-grid\" id=\"preparacion\"></div>\r\n        </div>\r\n        <div>\r\n            <h2 style=\"color: green; text-align:center\">PREPARADO</h2>\r\n            <div class=\"comandas-grid\" id=\"listos\"></div>\r\n        </div>\r\n    </div>\r\n</div>\r\n\r\n<script>\r\n    let campo = document.getElementById(\'pedido\');\r\n    let procesandoComandas = new Set();\r\n\r\n    // \u{2705} FUNCI\u{d3}N PARA MOSTRAR NOTIFICACIONES SIN ALERT\r\n    function mostrarNotificacion(mensaje, tipo = \'error\', duracion = 3000) {\r\n        // Eliminar notificaci\u{f3}n anterior si existe\r\n        const existente = document.querySelector(\'.notification\');\r\n        if (existente) {\r\n            existente.remove();\r\n        }\r\n\r\n        const notif = document.createElement(\'div\');\r\n        notif.className = `notification ${tipo}`;\r\n        notif.textContent = mensaje;\r\n        document.body.appendChild(notif);\r\n\r\n        // Auto-ocultar despu\u{e9}s de la duraci\u{f3}n especificada\r\n        setTimeout(() => {\r\n            if (notif.parentNode) {\r\n                notif.remove();\r\n            }\r\n        }, duracion);\r\n    }\r\n\r\n    function add(char) { campo.value += char; }\r\n    function borrar() { campo.value = campo.value.slice(0, -1); }\r\n\r\n    async function enviar() {\r\n        if (!campo.value) {\r\n            mostrarNotificacion(\'Introduce algo.\', \'warning\');\r\n            return;\r\n        }\r\n\r\n        let canal, numero;\r\n        if (/^[0-9]+$/.test(campo.value)) {\r\n            canal = \'F\';\r\n            numero = campo.value;\r\n        } else if (/^[A-Za-z][0-9]+$/.test(campo.value)) {\r\n            canal = campo.value.charAt(0).toUpperCase();\r\n            numero = campo.value.slice(1);\r\n        } else {\r\n            mostrarNotificacion(\'Formato incorrecto. Ej: F123\', \'error\');\r\n            return;\r\n        }\r\n\r\n        // Verificar si la comanda ya existe\r\n        try {\r\n            const res = await fetch(\'/api/pedidos\');\r\n            const lista = await res.json();\r\n            const existe = lista.some(p => p.canal === canal && p.numero === numero);\r\n\r\n            if (existe) {\r\n                mostrarNotificacion(`La comanda ${canal}-${numero} ya existe!`, \'warning\');\r\n                return;\r\n            }\r\n\r\n            await fetch(\'/api/pedido\', {\r\n                method: \'POST\',\r\n                headers: { \'Content-Type\': \'application/json\' },\r\n                body: JSON.stringify({ canal, numero, estado: \"en_preparacion\" })\r\n            });\r\n\r\n            campo.value = \'\';\r\n            mostrarNotificacion(`Comanda ${canal}-${numero} a\u{f1}adida`, \'success\', 2000);\r\n            cargar();\r\n        } catch (e) {\r\n            mostrarNotificacion(\'Error al enviar pedido. Int\u{e9}ntalo de nuevo.\', \'error\');\r\n        }\r\n    }\r\n\r\n    let procesando = false;\r\n\r\n    async function marcarPreparado(canal, numero) {\r\n        if (procesando) return;\r\n        procesando = true;\r\n\r\n        try {\r\n            const response = await fetch(\'/api/preparado\', {\r\n                method: \'POST\',\r\n                headers: { \'Content-Type\': \'application/json\' },\r\n                body: JSON.stringify({ canal, numero })\r\n            });\r\n\r\n            if (!response.ok) {\r\n                const error = await response.text();\r\n                mostrarNotificacion(`Error: ${error}`, \'error\');\r\n            } else {\r\n                cargar();\r\n            }\r\n        } catch (e) {\r\n            mostrarNotificacion(\'Error al marcar como preparado. Int\u{e9}ntalo de nuevo.\', \'error\');\r\n        } finally {\r\n            setTimeout(() => { procesando = false; }, 500);\r\n        }\r\n    }\r\n\r\n    async function marcarRecogido(canal, numero) {\r\n        if (procesando) return;\r\n        procesando = true;\r\n\r\n        try {\r\n            const response = await fetch(\'/api/archivar\', {\r\n                method: \'POST\',\r\n                headers: { \'Content-Type\': \'application/json\' },\r\n                body: JSON.stringify({ canal, numero })\r\n            });\r\n\r\n            if (!response.ok) {\r\n                const error = await response.text();\r\n                mostrarNotificacion(`Error: ${error}`, \'error\');\r\n            } else {\r\n                cargar();\r\n            }\r\n        } catch (e) {\r\n            mostrarNotificacion(\'Error al marcar como recogido. Int\u{e9}ntalo de nuevo.\', \'error\');\r\n        } finally {\r\n            setTimeout(() => { procesando = false; }, 500);\r\n        }\r\n    }\r\n\r\n    async function cargar() {\r\n        try {\r\n            const res = await fetch(\'/api/pedidos\');\r\n            const lista = await res.json();\r\n            const enPrep = document.getElementById(\'preparacion\');\r\n            const listos = document.getElementById(\'listos\');\r\n            if (!enPrep || !listos) return;\r\n\r\n            enPrep.innerHTML = \'\';\r\n            listos.innerHTML = \'\';\r\n\r\n            lista.forEach(p => {\r\n                const div = document.createElement(\'div\');\r\n                div.className = \'card\';\r\n\r\n                if (p.estado === \'en_preparacion\') {\r\n                    const bloqueado = procesandoComandas.has(`${p.canal}-${p.numero}`);\r\n                    div.innerHTML = `<div class=\"comanda\">${p.canal}-${p.numero}</div>\r\n                        <button class=\"btn preparado\" onclick=\'marcarPreparado(\"${p.canal}\", \"${p.numero}\")\' ${bloqueado ? \'disabled\' : \'\'}>Preparado</button>`;\r\n                    enPrep.appendChild(div);\r\n                } else if (p.estado === \'listo\') {\r\n                    const bloqueado = procesandoComandas.has(`${p.canal}-${p.numero}`);\r\n                    div.innerHTML = `<div class=\"comanda\">${p.canal}-${p.numero}</div>\r\n                        <button class=\"btn recogido\" onclick=\'marcarRecogido(\"${p.canal}\", \"${p.numero}\")\' ${bloqueado ? \'disabled\' : \'\'}>Recogido</button>`;\r\n                    listos.appendChild(div);\r\n                }\r\n            });\r\n        } catch (e) {\r\n            console.error(\'Error al cargar pedidos:\', e);\r\n        }\r\n    }\r\n\r\n    function toggleFullscreen() {\r\n        if (!document.fullscreenElement) {\r\n            document.documentElement.requestFullscreen().catch(err => {\r\n                console.error(`Error al entrar en pantalla completa: ${err.message}`);\r\n            });\r\n        } else {\r\n            document.exitFullscreen().catch(err => {\r\n                console.error(`Error al salir de pantalla completa: ${err.message}`);\r\n            });\r\n        }\r\n    }\r\n\r\n    function toggleDarkMode() {\r\n        const modo = document.body.classList.toggle(\'dark-mode\') ? \'dark\' : \'light\';\r\n        localStorage.setItem(\'modo_kitchen\', modo);\r\n        fetch(\'/api/modo\', {\r\n            method: \'POST\',\r\n            headers: { \'Content-Type\': \'application/json\' },\r\n            body: JSON.stringify({ modo })\r\n        });\r\n    }\r\n\r\n    function aplicarModo() {\r\n        const modoGuardado = localStorage.getItem(\'modo_kitchen\');\r\n        if (modoGuardado === \'dark\') {\r\n            document.body.classList.add(\'dark-mode\');\r\n        } else {\r\n            document.body.classList.remove(\'dark-mode\');\r\n        }\r\n    }\r\n\r\n    let pantallaSecundaria;\r\n\r\n    function abrirPantallaDos() {\r\n        const anchoPantalla1 = screen.width;\r\n        pantallaSecundaria = window.open(\r\n            \'http://localhost:8000\',\r\n            \'PantallaComandas\',\r\n            `width=1920,height=1080,left=${anchoPantalla1},top=0`\r\n        );\r\n        setTimeout(() => {\r\n            pantallaSecundaria.postMessage({ fullscreen: true }, \'*\');\r\n        }, 1000);\r\n    }\r\n\r\n    aplicarModo();\r\n    setInterval(cargar, 3000);\r\n    cargar();\r\n</script>\r\n</body>\r\n</html>";