Cómo crear o modificar un tema tomando uno existente como base
1. Arquitectura de un tema
Cada tema de fichas está compuesto por dos archivos con el mismo nombre base:
| Archivo | Función |
|---|---|
mi-tema.html.j2 |
Plantilla principal (layout). Define el CSS, la grilla de impresión y recorre las filas de fichas con Jinja2. |
mi-tema.html |
Partial (fragmento) de una sola ficha. Se incluye dentro de cada celda de la grilla. Contiene usuario, contraseña/PIN, límites y precio. |
├── ocean.html.j2 ← layout + estilos
├── ocean.html ← contenido de cada ficha
├── emerald-logo.html.j2
├── emerald-logo.html
└── ...
*.html.j2
y los muestra en el selector Plantilla de la ventana Imprimir Fichas.
2. Variables disponibles en Jinja2
El servicio FichaTemplateService pasa estas variables al renderizar:
| Variable | Tipo | Descripción |
|---|---|---|
title |
string | Título principal (ej. nombre del hotspot). |
subtitle |
string | Subtítulo (ej. identidad del router). |
footer |
string | Texto del pie de página. |
columns |
int (1–5) | Número de columnas por fila en la hoja. |
rows |
lista de listas | Matriz de fichas agrupadas por filas para la tabla. |
ficha |
dict | Datos de una ficha individual (solo dentro del partial). |
logo_url |
string | Logo del RouterBoard en base64 (data:image/...). Vacío si no hay logo. |
generated_at |
string | Fecha/hora de generación (dd/mm/YYYY HH:MM). |
Campos del objeto ficha
| Campo | Descripción |
|---|---|
ficha.username | Nombre de usuario. |
ficha.password | Contraseña o PIN. |
ficha.profile | Perfil Hotspot asignado. |
ficha.usage_limit | Tiempo de uso (ej. "1 hora"). Muestra "—" si no aplica. |
ficha.time_limit | Límite de sesión / uptime. |
ficha.price | Precio de la ficha. |
ficha.status | "Activo" o "Deshabilitado". |
ficha.username == ficha.password, la ficha es de tipo PIN
(usuario y clave iguales). En ese caso se recomienda mostrar solo el PIN con estilo destacado.
3. Estructura del layout (.html.j2)
El archivo principal sigue este patrón mínimo:
<!-- mi-tema.html.j2 --> <!DOCTYPE html> <html lang="es"> <head> <meta charset="UTF-8"> <title>{{ title }}</title> <style> html, body { margin: 0; padding: 0; background: #ffffff; /* fondo blanco para impresión */ } table.grid { width: 100%; border-collapse: separate; border-spacing: 6px; } td.ticket { width: {{ (100 / columns)|int }}%; page-break-inside: avoid; } /* ... más estilos del tema ... */ @page { size: Letter; margin: 0; } @media print { .ficha-header { -webkit-print-color-adjust: exact; print-color-adjust: exact; } } </style> </head> <body> <table class="grid"> {% for row in rows %} <tr> {% for ficha in row %} {% if ficha %} <td class="ticket"> {% include "mi-tema.html" %} </td> {% endif %} {% endfor %} </tr> {% endfor %} </table> <div class="footer"> {{ footer|default("Conserve esta ficha para acceder al servicio de internet.") }} </div> </body> </html>
{% include "mi-tema.html" %} debe coincidir exactamente
con el partial que creaste. Si el layout se llama ocean.html.j2,
el include debe ser {% include "ocean.html" %}.
4. Estructura del partial (.html)
Ejemplo simplificado del contenido de cada ficha:
<!-- mi-tema.html --> <div class="ficha-card"> <div class="ficha-header"> {% if title %} <div class="ficha-title">{{ title }}</div> {% if subtitle %} <div class="ficha-subtitle">{{ subtitle }}</div> {% endif %} {% else %} <div class="ficha-title">Acceso WiFi</div> {% endif %} </div> <div class="ficha-body"> {% if ficha.username != ficha.password %} <div class="ficha-item"> <span class="ficha-label">Usuario:</span> <span class="ficha-value">{{ ficha.username }}</span> </div> <div class="ficha-item"> <span class="ficha-label">Contraseña:</span> <span class="ficha-value">{{ ficha.password }}</span> </div> {% else %} <div class="ficha-item ficha-pin"> <span class="ficha-label">PIN:</span> <span class="ficha-value ficha-pin-value">{{ ficha.password }}</span> </div> {% endif %} {% if ficha.usage_limit and ficha.usage_limit != "—" %} <div class="ficha-item"> <span class="ficha-label">Tiempo de uso:</span> <span class="ficha-value">{{ ficha.usage_limit }}</span> </div> {% endif %} {% if ficha.price and ficha.price != "—" %} <div class="ficha-item"> <span class="ficha-label">Precio:</span> <span class="ficha-value">{{ ficha.price }}</span> </div> {% endif %} </div> </div>
5. Pasos para crear un tema nuevo (copiando uno existente)
Elegir un tema base
Recomendados para empezar:
ocean.html.j2— diseño horizontal con banner de color.emerald-logo.html.j2— incluye soporte de logo del RouterBoard.ejemplo.html.j2— múltiples variantes de color con clases CSS.
Copiar los dos archivos
Desde templates/fichas/, copia el par base y renómbralo. Ejemplo:
ocean.html.j2 → mi-cafe.html.j2 ocean.html → mi-cafe.html
Actualizar el include en el layout
En mi-cafe.html.j2, cambia la línea del include:
{% include "mi-cafe.html" %}
Modificar colores y estilos
En el bloque <style> del .html.j2, ajusta:
- Colores de borde, fondo de tarjeta y encabezado.
- Tipografía (
font-family, tamaños). - Bordes redondeados, sombras y espaciado.
Ejemplo: cambiar un tema azul a marrón café:
border-left-color: #2563eb; → border-left-color: #92400e; color: #2563eb; → color: #92400e; background: #dbeafe; → background: #fef3c7;
Ajustar el partial si cambias la estructura HTML
Si agregas nuevas secciones (iconos, logo, columnas internas), modifica
mi-cafe.html y define las clases CSS correspondientes en el layout.
Probar en la aplicación
- Abre Imprimir Fichas en el RouterBoard conectado.
- Selecciona tu plantilla en el combo Plantilla.
- Marca unas fichas de prueba y usa Exportar HTML o Exportar PDF.
- Revisa con 1, 2 y 3 columnas para verificar el ancho de cada celda.
6. Variante con logo del RouterBoard
Los temas emerald-logo e indigo-logo muestran el logo
configurado en el RouterBoard. No necesitas leer archivos manualmente: la aplicación
resuelve logo_path y entrega logo_url listo para usar.
<div class="ficha-logo"> {% if logo_url %} <img src="{{ logo_url }}" alt="Logo"> {% else %} <!-- Icono SVG de respaldo --> {% endif %} </div>
| Concepto | Detalle |
|---|---|
logo_path |
Ruta relativa guardada en la base de datos del RouterBoard
(ej. images/routerboards/1.png).
|
| Carpeta física | data/images/routerboards/ (ROUTERBOARD_LOGOS_DIR) |
logo_url |
Data URI base64 generada por FichaTemplateService.
Funciona en HTML exportado, PDF e impresión sin rutas externas.
|
7. Consejos para impresión
- Mantén
background: #ffffffenbodypara ahorrar tinta. - Usa
page-break-inside: avoidentd.ticketpara no cortar fichas entre páginas. - Para colores de fondo en impresión, agrega:
-webkit-print-color-adjust: exact; print-color-adjust: exact; - El ancho de columna se calcula con
{{ (100 / columns)|int }}%. - Tamaño de página:
@page { size: Letter; margin: 0; } - Usa SVG embebido para iconos decorativos (no dependen de archivos externos).
8. Temas incluidos de referencia
| Plantilla | Estilo | Logo |
|---|---|---|
default.html.j2 | Clásico con borde punteado | No |
ejemplo.html.j2 | Horizontal, 7 variantes de color CSS | No |
ocean.html.j2 | Azul/cyan, banner con icono WiFi | No |
sunset.html.j2 | Naranja/ámbar, icono sol | No |
forest.html.j2 | Verde, icono árbol lateral | No |
midnight.html.j2 | Oscuro/dorado, luna y estrellas | No |
coral.html.j2 | Rosa/coral, diseño centrado | No |
emerald-logo.html.j2 | Verde esmeralda, logo en header | Sí |
indigo-logo.html.j2 | Índigo/violeta, logo circular | Sí |
9. Solución de problemas
| Problema | Causa probable | Solución |
|---|---|---|
| La plantilla no aparece en el selector | Archivo sin extensión .html.j2 |
Renombra el layout a mi-tema.html.j2 |
| Error al renderizar / include not found | Nombre incorrecto en {% include %} |
Verifica que el partial exista y coincida el nombre |
| Colores no se imprimen | Navegador/impresora omite fondos | Agrega print-color-adjust: exact en CSS |
| Logo no aparece | RouterBoard sin logo asignado | Configura logo en edición del RouterBoard; usa tema *-logo |
| Ficha se corta en dos páginas | Falta regla CSS de page-break | Agrega page-break-inside: avoid a td.ticket |
10. Resumen rápido
- Copia un par
.html.j2+.htmlexistente. - Renombra ambos archivos con el mismo nombre base.
- Actualiza el
{% include %}en el layout. - Modifica CSS (colores, tipografía, bordes) en el
.html.j2. - Ajusta el HTML del partial si cambias la estructura de la ficha.
- Para logo, usa
{% if logo_url %}con imagen base64. - Prueba exportando HTML/PDF con distintas columnas.