Tu colección de juegos de mesa, con vida propia.
Colección, partidas, rankings, lista de deseos, préstamos, multiusuario con permisos de verdad y un "Quiero jugar" con notificaciones — todo en una aplicación PHP + MySQL ligera, sin build step, pensada para desplegarse en cualquier hosting compartido (Hestia, por ejemplo) tan fácil como subir una carpeta por FTP.
Nació como el diseño de alta fidelidad de un grupo de amigos cansado de apuntar las partidas en una hoja de cálculo. Se convirtió en una app con autenticación real por persona, temas de color, vista responsive completa, carátulas traídas en vivo desde BoardGameGeek, y hasta un carrusel tipo tragaperras para decidir qué se juega esta noche cuando nadie se pone de acuerdo.
- ✨ Características
- 📋 Requisitos
- 🚀 Instalación
- 🔄 Actualizar tras subir una nueva versión
- 🖼️ Carátulas: siempre desde la API de BoardGameGeek
- 🧱 Complementos 3D
- 🎰 Juego aleatorio
- 🧩 Expansiones
- 📂 Menú lateral
- 📱 Responsive (móvil y tablet)
- 🎨 Temas y vista de la colección
- 👥 Usuarios y permisos
- 🙋 Quiero jugar
- 💾 Copia de seguridad
- 🗂️ Estructura
- 🏷️ Versión y aviso de actualización
- 🐳 Levantar todo con Docker
- 🔒 Notas de seguridad para producción
- 📜 Historial de versiones
- 📚 Colección con puntuación, precio, jugadores, duración, edad recomendada y 14 tipos de juego — carátula siempre traída en vivo desde BoardGameGeek, nunca subida ni guardada en el servidor.
- 🎰 Juego aleatorio — un carrusel gira entre tus juegos (de un tipo o de todos) y decide qué toca jugar hoy.
- 🧩 Expansiones enlazadas a su juego base en los dos sentidos.
- 🏆 Ranking y estadísticas de partidas, jugadores y rentabilidad.
- 💜 Lista de deseos y 📦 préstamos.
- 🙋 "Quiero jugar" — propón una partida a quien quieras; puede aceptarla (con aviso por email) o descartarla, y al registrar la partida la propuesta se resuelve sola.
- 👥 Multiusuario real — admin / coleccionista / jugador, cada uno con su propio correo y contraseña, permisos aplicados también en el servidor (no solo ocultando botones).
- 🎨 4 temas de color y dos vistas de colección (Carátula tipo kiosco / Ficha).
- 📱 Responsive de verdad — menú lateral colapsable en escritorio, cajón deslizante en móvil y tablet.
- 💾 Copia de seguridad con un clic — el propio número de versión es el botón.
- 🧱 Sin build step — PHP + MySQL puro, sube los ficheros y listo.
- PHP 7.4 o superior con las extensiones
pdo_mysqlycurl. - MySQL / MariaDB.
- Acceso de escritura a la carpeta del proyecto (para que
install.phpcreeconfig.php).
- Sube toda esta carpeta al dominio/subdominio en Hestia (por ejemplo, vía SFTP o el gestor de archivos del panel).
- En Hestia, crea una base de datos MySQL y su usuario (menú BBDD) si no lo has hecho ya.
- Abre
https://tu-dominio/install.phpen el navegador. - Rellena el host, puerto, nombre, usuario y contraseña de la base de datos, y define la contraseña de la cuenta admin.
- Al enviar el formulario, el instalador:
- comprueba la conexión a MySQL,
- crea la base de datos si no existía,
- crea las tablas (
games,players,plays,play_players,wishlist,loans,app_users...) conCREATE TABLE IF NOT EXISTS, así que se puede volver a ejecutar sin perder datos, - escribe
config.phpcon la conexión y el hash de la contraseña de admin.
- Entra en
login.phpcon correoadminy la contraseña que acabas de definir.
Para reconfigurar la conexión a MySQL, la contraseña de admin o el token de BGG más
adelante, vuelve a install.php: si ya existe config.php te pedirá la contraseña
actual antes de permitir sobrescribirlo. Al reinstalar, el formulario ya sale relleno
con el host/puerto/nombre/usuario de la base de datos que tenías, y la contraseña de la
base de datos, la contraseña de admin y el token de BGG se pueden dejar en blanco para
mantener los que ya había — así, por ejemplo, para pegar solo un token de BGG nuevo no
hace falta volver a escribir el resto.
Cuando subas ficheros nuevos de Ludoteca (por ejemplo, tras una actualización que añada
columnas o tablas), no hace falta volver a pasar por install.php ni reintroducir la
contraseña de MySQL: entra en https://tu-dominio/update.php (pide el login normal de
la app) y pulsa "Actualizar ahora". Reutiliza la conexión de config.php, vuelve a
ejecutar los CREATE TABLE IF NOT EXISTS (no tocan las tablas existentes) y aplica las
migraciones de columnas nuevas definidas en includes/schema.php
(ludoteca_run_migrations), sin borrar ni modificar los datos que ya tienes. Es el mismo
mecanismo que usa install.php al crear la base de datos por primera vez, así que
instalación y actualización comparten una única fuente de verdad del esquema.
Ludoteca no almacena ni sube ficheros de imagen. Al añadir o editar un juego (y también
al añadir un deseo a la lista), el diálogo ofrece un buscador que llama a la API de
BoardGameGeek (XMLAPI2) desde el servidor (api/bgg_search.php y api/bgg_thing.php,
ver includes/bgg.php), porque esa API no admite CORS desde el
navegador. Al elegir un resultado se copian nombre, editorial, nº de jugadores, duración
y, sobre todo, la URL de la carátula que devuelve BGG — el <img> la carga
directamente desde el CDN de BGG, nunca se descarga ni se guarda una copia en el servidor.
El nombre solo se rellena si el campo está vacío: si ya habías escrito uno (o estás editando un juego que ya tiene nombre), se respeta y no se sobrescribe. Es así a propósito — BGG solo expone un nombre "primary" (normalmente en inglés) y nombres alternativos sin ninguna etiqueta de idioma, así que no hay forma fiable de pedirle el nombre en español; el resto de datos (editorial, jugadores, duración, edad, puntuación e imagen) sí se aplican siempre que BGG los tenga.
El buscador pide hasta 50 resultados (antes se cortaba en 12, y por eso algunos juegos
que sí existían en BGG no aparecían) y busca tanto en juegos base como en expansiones
(boardgame,boardgameexpansion — antes solo pedía boardgame, así que ninguna
expansión salía nunca); cada resultado que sea una expansión lo indica junto al nombre.
Importante: desde octubre de 2025 BoardGameGeek exige que cualquier aplicación esté
registrada y envíe un token en cada petición (Authorization: Bearer <token>); sin él,
la API devuelve 401 aunque el juego exista. Para que el buscador funcione:
- Inicia sesión en tu cuenta de BGG y regístrala en boardgamegeek.com/applications/create.
- Pega el token que te den en
install.php(campo "Token de API de BoardGameGeek"; si ya instalaste la app, vuelve ainstall.php, marca "Sobrescribir configuración existente" y pon el token ahí).
Sin token, el resto de la aplicación funciona con normalidad: solo falla la búsqueda en BGG, con un mensaje explicando qué falta — no un fallo silencioso.
En la ficha de detalle de cada juego hay un enlace "Complementos 3D" que abre en una
pestaña nueva la búsqueda de ese nombre en MakerWorld
(makerworld.com/es/search/models?keyword=<nombre>) — organizadores, insertos y bandejas
imprimibles en 3D que la comunidad haya subido para ese juego. Es solo un enlace de
búsqueda (MakerWorld no tiene API pública), así que no depende de ninguna cuenta ni token.
El botón "🎲 Juego aleatorio" de la pestaña Colección pregunta de qué tipo lo quieres (incluye "Todos") y hace girar un carrusel con carátulas de la colección — empieza rápido y frena hasta detenerse justo en el centro, marcado con un marco dorado. El sorteo elige el juego al azar entre los que coincidan con el tipo elegido antes de empezar a girar (la animación no decide nada: solo "cuenta" hasta llegar ahí), así que siempre acaba en un juego que existe en tu colección — pero no hay ningún aviso de texto anunciándolo: el propio marco centrado sobre la carátula (con su nombre debajo) es la única señal de cuál ha salido, para que sea el carrusel el que se lleve toda la atención. Debajo quedan "Ver ficha" (abre su detalle normal), "Girar otra vez" o cerrar. Si no tienes ningún juego de ese tipo, avisa en vez de girar.
Los tipos de juego (includes/helpers.php, constante LUDOTECA_TIPOS) incluyen
Eurogame, Ameritrash/temático, Abstracto, Familiar, Filler, Party, Deducción social,
Legacy/campaña, Construcción de mazos, Wargame, Cooperativo, Juegos Rol, Roll&Write y
Colocación de losetas.
Al marcar un juego como expansión y elegir su juego base (al añadirlo o editarlo), la
relación se muestra en los dos sentidos sin tener que buscarla a mano: en el juego base
—al pasar el ratón en la vista Carátula, en la propia tarjeta en Ficha, o en su ficha de
detalle— aparece "Expansiones:" con un enlace a cada una; en cada expansión aparece "Se
necesita el juego base:" con un enlace al juego base. Los enlaces abren directamente la
ficha de detalle del otro juego (data-action="open-detail" en assets/js/app.js), así
que se puede saltar de una expansión a su base y viceversa sin volver a la colección.
La navegación vive en una barra lateral (no arriba): pestañas, tema y usuario, siempre
ocupando la altura completa de la pantalla (.app-shell es quien no hace scroll; el
contenido se desplaza dentro de sí mismo, no la página). En escritorio se puede colapsar
a solo iconos con el botón «/» de su cabecera — útil en pantallas más estrechas o
simplemente para ganar espacio en la colección. El colapsado es solo densidad visual del
dispositivo actual (no una preferencia de la cuenta como el tema), así que se guarda en
localStorage, no en la base de datos, y se aplica antes de pintar nada para que no haya
parpadeo al recargar.
Por debajo de 760px de ancho el menú lateral deja de ocupar sitio fijo y se convierte en un cajón deslizante: aparece una barra superior con el botón ☰ que lo abre sobre el contenido (con un fondo oscurecido detrás); se cierra tocando ese fondo o elegiendo una pestaña. El resto de la interfaz también se adapta — cuadrícula de carátulas y de fichas de una sola columna cuando no cabe más de una, formularios de los diálogos a una columna por debajo de 480px, y las tablas (Partidas, Ranking) se desplazan en horizontal dentro de sí mismas si no caben, en vez de forzar el scroll de toda la página.
Cuatro paletas (Crema, Pergamino, Tinta, Nocturno) seleccionables desde los 4 cuadraditos
de la cabecera; cada una define solo 5 colores base y el resto de rampas (100-900,
sombras) se derivan por interpolación — misma matemática en
includes/theme.php (resuelve el tema en el propio HTML para que no
haya parpadeo al cargar) y en assets/js/app.js (para el cambio en caliente). La
colección también tiene dos vistas intercambiables — "Carátula" (cuadrícula tipo kiosco:
zoom suave y brillo al pasar el ratón sobre la portada, con los datos revelándose encima)
y "Ficha" (la tarjeta con tags y pie que ya había) — con el mismo selector Vista de la
barra de filtros.
Ambas preferencias (tema y vista) se guardan en la tabla settings de la base de datos
(api/preferences.php) en cuanto se cambian, así que la próxima vez que inicies sesión
— desde el mismo dispositivo o desde otro — se carga tal cual se dejó. El tema es global
(no por persona) y se aplica también fuera de la SPA — login.php, users.php y
update.php resuelven y pintan el mismo <style> de includes/theme.php — para que no
haya ningún salto visual al color por defecto en ninguna pantalla de la aplicación.
Hay tres roles:
- Admin — entra en
login.phpcon el correo literaladminy la contraseña que se definió eninstall.php. Acceso completo y es el único que puede gestionar usuarios (users.php, enlace 👤 en la cabecera). - Coleccionista — todo lo que hace hoy la app salvo gestionar usuarios: crear, editar y eliminar juegos, deseos, préstamos, temas, copia de seguridad...
- Jugador — ve toda la app en modo lectura, pero solo puede registrar partidas y usar
"Quiero jugar". El resto de botones (añadir/editar/eliminar juegos, deseos, préstamos,
descargar backup) ni siquiera se pintan para este rol, y el servidor los rechaza con
403 aunque se llame a la API a mano — la restricción real está en
ludoteca_require_collector_api()(includes/auth.php), no en la interfaz.
Autenticación real por persona: cada usuario que el admin añade en users.php tiene
su propio correo y su propia contraseña (se piden al crearlo, con confirmación). En
login.php se entra con correo + contraseña, y el sistema resuelve solo quién eres y
qué rol tienes — no hay ninguna pantalla intermedia de "elige quién eres". El admin es
el único caso especial: no tiene un correo real, usa el literal admin junto con la
contraseña de install.php. Las contraseñas se guardan siempre con password_hash()
(bcrypt), nunca en texto plano; el admin puede restablecer la contraseña de cualquier
usuario desde users.php si la olvida.
Crear un usuario crea (o vincula, si ya existía uno con ese nombre) su jugador en la
pestaña "Jugadores" automáticamente — no hay que darlo de alta por separado para que
sus partidas y estadísticas se atribuyan bien. Esto repara también las instalaciones que
ya tenían usuarios de antes de esta versión: install.php/update.php recorren los
app_users sin jugador vinculado y les crean uno con su mismo nombre
(ludoteca_run_migrations en includes/schema.php).
Cualquiera puede proponer un juego de la colección a una o varias de las personas de la lista (pestaña "Quiero jugar" o botón "Proponer partida" desde la ficha de un juego). A quien se lo propongan le sale un aviso destacado en la cabecera la próxima vez que entre — y un contador en la propia pestaña — con dos opciones: Descartar (solo afecta a su propia notificación, no a las de los demás destinatarios) o Aceptar. Quien la propuso o el admin pueden cancelarla del todo en cualquier momento ("✕").
Aceptar deja la propuesta como "pendiente de registrar la partida": sigue apareciendo (ya no se puede ni aceptar ni descartar, solo se ve su estado) hasta que alguien registra una partida de ese juego — en ese momento se borra sola, porque ya "se convirtió" en esa partida. Si nadie la acepta nunca, no pasa nada especial: se queda como una propuesta normal hasta que se descarta o se cancela a mano.
En cuanto alguien la acepta, la tarjeta muestra un botón Registrar que abre el diálogo de "Registrar partida" con el juego y los jugadores ya rellenos (quien la propuso más quienes la hayan aceptado) — solo falta poner la duración y quién ganó.
Al crear la propuesta, cada destinatario recibe además un email automático avisándole
(includes/mailer.php, con mail() de PHP — el correo que ya usa para entrar, sin pedir
ni guardar ningún dato nuevo). No depende de ninguna cuenta ni servicio externo: en Hestia
el dominio ya sale listo para enviar correo. Si el envío falla (o el hosting no tiene
correo saliente configurado), la propuesta se crea igual — el aviso por email es un
extra, nunca una condición para que funcione "Quiero jugar". Se descartó WhatsApp
automático porque desde julio de 2025 Meta no ofrece capa gratuita para mensajes que
inicia la aplicación (se cobra por mensaje) y exige una plantilla aprobada de antemano
por Meta — cambiar a eso requeriría dar de alta una cuenta de WhatsApp Business aparte.
Cada intento de envío se registra en includes/mail.log (a quién, el asunto, si
mail() dijo que sí y el error de PHP si dijo que no) — esa carpeta ya está bloqueada
por completo por su .htaccess, así que el fichero no se puede descargar por la web,
pero se puede abrir por FTP o por el gestor de archivos de Hestia sin necesitar SSH. Que
mail() devuelva true solo significa que el servidor lo aceptó para intentar
entregarlo, no que haya llegado — si el log dice ok=true pero el correo no aparece
(ni en spam), hay que mirar el log de correo del propio servidor (en Hestia, panel del
dominio → Mail → revisar que el dominio tenga el correo activado, y el log de Exim
del servidor) y la configuración de SPF/DKIM del dominio.
El contador y el aviso significan cosas distintas según el rol: para jugador son "invitaciones que me proponen a mí" (solo cuentan las propuestas donde él es destinatario); admin y coleccionista ven en cambio el total de propuestas pendientes de toda la colección, sean para quien sean, ya que pueden ver y gestionar todo lo demás — el aviso se redacta como actividad general ("sin resolver") en vez de como algo dirigido a ellos en persona.
El número de versión de la cabecera (v1.9.7) es también un enlace "oculto": para quien
puede gestionar la colección (admin/coleccionista), pulsarlo descarga un .sql con el
esquema completo (CREATE TABLE IF NOT EXISTS) más todos los datos como INSERT
(backup.php / includes/backup.php) — restaurable directamente con
mysql basedatos < backup.sql sobre una base de datos vacía. Para jugador es texto
plano sin enlace (y backup.php lo rechaza con 403 aunque se llame directamente).
install.php Asistente de instalación (BBDD + esquema + contraseña + token BGG)
update.php Reaplica el esquema (tablas/columnas nuevas) sin tocar datos ni credenciales
backup.php Descarga el .sql completo (esquema + datos); solo admin/coleccionista
version.txt Versión de los ficheros desplegados; se compara con la de la BBDD
login.php / logout.php Acceso con correo + contraseña (resuelve la identidad directamente)
users.php Alta/baja/rol/contraseña de usuarios permitidos (solo admin)
index.php Cascarón de la aplicación (SPA ligera sin build step)
config.php Generado por install.php (no se versiona)
includes/ Conexión PDO, autenticación+roles, BGG, correo, helpers, temas, backup, esquema y migraciones
api/ Endpoints JSON consumidos por assets/js/app.js (preferences.php, want_to_play.php...)
assets/css/ Nocturne (base) + tokens ámbar y componentes propios de Ludoteca
assets/js/app.js Render de las 7 vistas y los diálogos, llamadas fetch a la API
_design_reference/ Paquetes de diseño originales (handoff), sin usar en producción
docker/ Entorno de desarrollo (PHP+Apache y MySQL) para probar en local
version.txt guarda la versión de los ficheros que hay desplegados; la tabla settings
de la base de datos guarda la última versión que se aplicó con install.php/update.php.
Si no coinciden, la cabecera de la app muestra "Actualizar a la versión X.X.X" enlazando a
update.php; si coinciden, muestra solo "vX.X.X" en gris. Sube el número de version.txt
cada vez que cambies el esquema o publiques una versión nueva.
No hace falta instalar PHP ni MySQL en tu máquina: docker/docker-compose.yml ya trae
los dos contenedores que necesita la aplicación — MySQL 8 (con sus datos en un volumen,
así que sobreviven a reinicios) y PHP 8.2 + Apache (con pdo_mysql y curl, montando
todo el proyecto dentro) sirviendo Ludoteca.
- Docker y Docker Compose (viene incluido en
Docker Desktop; en Linux, el paquete
docker-compose-plugin).
cd docker
docker compose up -d --buildLa primera vez tarda un poco (construye la imagen de PHP y descarga la de MySQL); las
siguientes veces arranca en segundos. Compruébalo con docker compose ps — deberías
ver docker-db-1 y docker-php-1 como Up.
- Abre
http://localhost:8080/install.phpen el navegador. - Rellena la conexión a MySQL exactamente así (son las credenciales que define
docker-compose.yml; si las cambias ahí, cámbialas también aquí):- Host:
db(es el nombre del servicio en la red interna de Docker, nolocalhost) - Puerto:
3306 - Nombre de la base de datos:
ludoteca - Usuario:
ludoteca - Contraseña:
ludoteca
- Host:
- Define la contraseña de la cuenta admin (y, si ya tienes uno, pega el token de BGG).
- Pulsa Instalar.
- Entra en
http://localhost:8080/login.phpcon correoadminy esa contraseña.
A partir de aquí la aplicación funciona igual que en cualquier otro sitio — Colección,
Partidas, "Quiero jugar"... todo contra la base de datos MySQL del contenedor db.
| Qué quieres hacer | Comando (desde docker/) |
|---|---|
| Ver qué está pasando (logs en vivo) | docker compose logs -f php (o db) |
| Parar los contenedores sin perder datos | docker compose down |
| Parar y borrar también la base de datos | docker compose down -v |
Reconstruir tras cambiar docker/php/Dockerfile |
docker compose up -d --build |
Los ficheros PHP/JS/CSS del proyecto están montados en vivo dentro del contenedor
(volumes: ..:/var/www/html en docker-compose.yml), así que si editas código no hace
falta reconstruir nada: recarga el navegador y ya está aplicado. Solo necesitas
--build si cambias el propio Dockerfile.
Este docker-compose.yml está pensado para uso local (contraseñas de ejemplo, MySQL
publicado en el puerto 33061 del host, sin HTTPS). Para dejarlo accesible desde fuera de
tu ordenador, como mínimo: cambia MYSQL_ROOT_PASSWORD/MYSQL_PASSWORD en
docker-compose.yml por unas contraseñas de verdad, no publiques el puerto de MySQL
(quita el bloque ports del servicio db, solo lo necesita php para conectarse por
la red interna) y pon delante un proxy con TLS (Caddy, Traefik, nginx...). Para un uso
personal normal, lo más sencillo sigue siendo la instalación descrita en
🚀 Instalación sobre un hosting como Hestia.
- Cambia la contraseña de admin desde
install.phpsi sospechas que se ha filtrado. config.phpestá bloqueado por.htaccess; no lo expongas por otra vía (backups públicos, etc.).- Las contraseñas de
app_usersse guardan conpassword_hash()(bcrypt), nunca en texto plano; si alguien olvida la suya, restablécela desdeusers.php. - Los backups (
backup.php) contienen todos tus datos, incluidos los hashes de contraseña de la lista de usuarios; trátalos con el mismo cuidado queconfig.php.
Reconstruido a partir del desarrollo de la aplicación (el proyecto no llevaba control de versiones desde el principio), así que las fechas exactas de cada versión no están disponibles — el orden y el contenido sí son fieles a como se fue construyendo.
| Versión | Resumen |
|---|---|
| 1.9.7 | Al registrar una partida competitiva ahora se puede marcar empate y elegir qué jugadores empataron; cuentan como victoria para todos ellos en Jugadores y Ranking. |
| 1.9.6 | Las partidas registradas también se pueden eliminar (botón ✕ junto al de editar, con confirmación). |
| 1.9.5 | Las partidas registradas se pueden editar (fecha, juego, jugadores, ganador/resultado y duración) desde un botón ✎ en la tabla de Partidas. Corrección: en móvil, los diálogos largos (por ejemplo Editar juego con "Es una expansión" marcado) no tenían límite de altura ni scroll propio, así que los botones de guardar podían quedar fuera de la pantalla sin forma de llegar a ellos. |
| 1.9.4 | Corrección: en el "Juego aleatorio", el resultado era literalmente la última carátula de la cinta, así que a su derecha no había nada y parecía que se había acabado la lista — ahora hay carátulas de más después del resultado, para que el carrusel siga viéndose "vivo" a los dos lados de donde para. |
| 1.9.3 | Corrección: editar un juego sin tocar el buscador de BGG borraba su carátula. El "Juego aleatorio" ya no anuncia el resultado con texto — el marco (ahora dorado) centrado sobre la carátula es la única señal de cuál ha salido. |
| 1.9.2 | Corrección: el diálogo de "Juego aleatorio" podía renderizarse fuera de la pantalla (un <div> con contenido muy ancho inflaba la rejilla que lo centraba). |
| 1.9.1 | Corrección: el carrusel del "Juego aleatorio" no se deslizaba — saltaba directo al resultado en vez de animarse. |
| 1.9.0 | Tres tipos de juego nuevos (Juegos Rol, Roll&Write, Colocación de losetas) y la función Juego aleatorio: un carrusel de carátulas que gira y frena hasta un resultado. |
| 1.8.6 | El buscador de BoardGameGeek trae hasta 50 resultados (antes 12) e incluye expansiones en la búsqueda. |
| 1.8.5 | El nombre de un juego ya no se sobrescribe con el de BGG si ya estaba definido (BGG no permite pedir el nombre en español). |
| 1.8.4 | install.php permite dejar en blanco los campos que no cambian al reinstalar (contraseña de BBDD, contraseña de admin, token de BGG) y mantiene los valores actuales. |
| 1.8.3 | Botón "Registrar" directo desde una propuesta de "Quiero jugar" ya aceptada, con el juego y los jugadores precargados. |
| 1.8.2 | Registro (includes/mail.log) de cada intento de envío de email, para poder diagnosticar por qué no llega un aviso. |
| 1.8.1 | Aviso automático por email a cada destinatario cuando se crea una propuesta de "Quiero jugar" (se valoró y descartó WhatsApp automático por su coste y por exigir una plantilla aprobada por Meta). |
| 1.8.0 | Las propuestas de "Quiero jugar" se pueden aceptar (no solo descartar); al registrar la partida correspondiente, la propuesta se resuelve sola. |
| 1.7.2 | El contador de "Quiero jugar" refleja el total de propuestas pendientes para admin y coleccionista, no solo las que le proponen a él. |
| 1.7.1 | Crear un usuario crea (o vincula) su jugador automáticamente; las instalaciones con usuarios antiguos se repararan solas al actualizar. |
| 1.7.0 | Diseño responsive completo: el menú lateral se convierte en cajón deslizante en móvil/tablet; diálogos y tablas se adaptan a pantallas pequeñas. |
| 1.6.1 | Se retira la rejilla de fondo decorativa; el menú lateral ocupa siempre toda la altura de la pantalla (antes podía dejar un hueco al hacer scroll). |
| 1.6.0 | Menú de navegación lateral colapsable (antes arriba); vista Carátula con efecto "kiosco" (zoom y brillo al pasar el ratón); la relación entre un juego base y sus expansiones se muestra en los dos sentidos. |
| 1.5.2 | El tema de color elegido se aplica también fuera de la colección: login, gestión de usuarios y pantalla de actualización. |
| 1.5.1 | Autenticación real por persona: cada usuario tiene su propio correo y contraseña; el admin entra con el correo literal admin. |
| 1.5.0 | Copia de seguridad completa descargable desde el número de versión; multiusuario real con roles (admin/coleccionista/jugador); "Quiero jugar" dirigido a personas concretas, con notificación. |
| 1.4.1 | Enlace "Complementos 3D" en la ficha de cada juego, con búsqueda directa en MakerWorld. |
| 1.4.0 | Nueva estética visual: cuatro temas de color y dos vistas de colección (Carátula/Ficha), guardadas como preferencia. |
| 1.3.0 | Partidas cooperativas: al registrar una partida de un juego cooperativo se pide Victoria/Derrota en vez de un ganador individual. |
| 1.2.0 | Edad mínima recomendada y "Versión Premium" en juegos y deseos; los juegos se pueden marcar como expansión de otro juego. |
| 1.1.0 | Edición de juegos; el buscador de BGG llega también a la lista de deseos; pestaña Ranking; control de versión de fichero vs. base de datos con aviso de actualización; entorno Docker para desarrollo; install.php actualiza instalaciones existentes sin perder datos. |
| 1.0.0 | Primera versión: colección, partidas, jugadores, deseos y préstamos; instalador con contraseña de acceso; carátulas siempre traídas en vivo desde la API de BoardGameGeek. |