feat: refactor to SI Quiz with Space Invaders arcade interface

- Updated Dockerfile to Python 3.11, selective COPY for cleaner image
- Expanded .gitignore with Python/OS patterns
- Added config.json, questions.json, env_example, and static assets
- Updated templates (host, player, scores) with Space Invaders UI
- Rewrote README for SI Quiz branding and setup docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-09 01:45:04 +00:00
co-authored by Claude Sonnet 4.6
parent d8f4cc97c6
commit 3539b1f75f
13 changed files with 451 additions and 408 deletions
+36 -6
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NI Quiz · Host</title>
<title>{{ app_name }} · Host</title>
<!-- QR Code library -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/qrcodejs/1.0.0/qrcode.min.js"></script>
<style>
@@ -155,15 +155,35 @@ html,body{width:100%;height:100vh;background:var(--bg);color:var(--text);font-fa
}
#auth-btn:hover{opacity:.85;}
#auth-err{font-family:var(--pixel);font-size:.4rem;color:var(--red);min-height:1rem;}
/* ── THEME TOGGLE ── */
.theme-toggle{
position:fixed;top:1rem;right:1rem;z-index:10000;
display:flex;gap:.3rem;
background:var(--surface);border:1px solid var(--border);
border-radius:6px;padding:.3rem .4rem;
}
.t-btn{
padding:.28rem .55rem;border-radius:4px;font-size:.5rem;cursor:pointer;
border:1px solid transparent;background:transparent;color:var(--muted);
font-family:var(--pixel);transition:all .15s;
}
.t-btn:hover{color:var(--text);}
.t-btn.active{background:var(--accent);color:#000;}
</style>
</head>
<body>
<div class="theme-toggle">
<button class="t-btn" onclick="setTheme('dark')" id="t-dark">DARK</button>
<button class="t-btn" onclick="setTheme('light')" id="t-light">LIGHT</button>
<button class="t-btn" onclick="setTheme('hc')" id="t-hc">HC</button>
</div>
<div class="grid-bg"></div>
<!-- LOBBY -->
<div class="screen active" id="lobby-screen">
<div class="logo">🌐 NI QUIZ</div>
<div class="tagline">Network Intelligence · LCPR — Host Panel</div>
<div class="logo">🌐 {{ app_name }}</div>
<div class="tagline">{{ org_name }}{% if org_name %} — {% endif %}Host Panel</div>
<div class="lobby-main">
<div class="qr-box">
<div class="qr-label">SCAN TO PLAY</div>
@@ -256,7 +276,8 @@ document.getElementById('join-url').textContent = baseUrl;
new QRCode(document.getElementById('qrcode'), {
text: baseUrl,
width: 160, height: 160,
colorDark: '#00c8ff', colorLight: '#0d1117',
colorDark: getComputedStyle(document.documentElement).getPropertyValue('--qr-dark').trim() || '#00c8ff',
colorLight: getComputedStyle(document.documentElement).getPropertyValue('--qr-light').trim() || '#0d1117',
correctLevel: QRCode.CorrectLevel.M
});
@@ -416,8 +437,17 @@ function confetti() {
}
}
// ── THEME ─────────────────────────────────────────────────────────────────────
function setTheme(t) {
document.documentElement.setAttribute('data-theme', t);
localStorage.setItem('si-quiz-theme', t);
document.querySelectorAll('.t-btn').forEach(b => b.classList.remove('active'));
document.getElementById('t-' + t)?.classList.add('active');
}
(function(){ setTheme(localStorage.getItem('si-quiz-theme') || 'dark'); })();
// ── AUTH ─────────────────────────────────────────────────────────────────────
const HOST_PASSWORD = 'ni2026'; // ← cambia esto
const HOST_PASSWORD = '{{ host_password }}';
function checkAuth() {
const saved = sessionStorage.getItem('host_auth');
@@ -428,7 +458,7 @@ function checkAuth() {
gate.id = 'auth-gate';
gate.innerHTML = `
<div id="auth-box">
<div id="auth-logo">🌐 NI QUIZ</div>
<div id="auth-logo">🌐 {{ app_name }}</div>
<div id="auth-sub">Host Panel · Acceso restringido</div>
<input id="auth-input" type="password" placeholder="contraseña" autocomplete="off" />
<button id="auth-btn">ENTRAR →</button>
+8 -11
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
<title>NI Quiz · Arcade</title>
<title>{{ app_name }} · Arcade</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&family=DM+Sans:wght@400;500&display=swap');
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent;}
@@ -156,16 +156,16 @@ body::after{content:'';position:fixed;inset:0;background:repeating-linear-gradie
<!-- SPLASH — goes straight to join -->
<div class="screen active" id="lang-screen">
<div class="title">🌐 NI QUIZ</div>
<div class="subtitle">NETWORK INTELLIGENCE · LCPR</div>
<div class="title">🌐 {{ app_name }}</div>
<div class="subtitle">{{ org_name }}</div>
<button class="lang-btn es" onclick="goJoin()">&nbsp; JUGAR / PLAY</button>
</div>
<!-- JOIN -->
<div class="screen" id="join-screen">
<div class="logo">🚀 NI QUIZ</div>
<div class="sub" id="join-sub">Network Intelligence · LCPR</div>
<div class="logo">🚀 {{ app_name }}</div>
<div class="sub" id="join-sub">{{ org_name }}</div>
<div class="field-wrap">
<label id="lbl-fname">FIRST NAME</label>
<input id="inp-fname" type="text" maxlength="20" autocomplete="off" autocorrect="off" autocapitalize="words">
@@ -178,11 +178,8 @@ body::after{content:'';position:fixed;inset:0;background:repeating-linear-gradie
<label id="lbl-dept">DEPARTMENT</label>
<select id="inp-dept">
<option value="">-- Select --</option>
<option>Finanzas</option>
<option>People</option>
<option>Legal</option>
<option>B2B</option>
<option>B2C</option>
{% for dept in departments %}<option>{{ dept }}</option>
{% endfor %}
</select>
</div>
<button id="join-btn">▶ ENTER GAME</button>
@@ -227,7 +224,7 @@ body::after{content:'';position:fixed;inset:0;background:repeating-linear-gradie
<!-- PODIUM -->
<div class="screen" id="podium-screen">
<h2 id="pod-title">🏆 GAME OVER</h2>
<p class="sub" id="pod-sub">Network Intelligence · LCPR</p>
<p class="sub" id="pod-sub">{{ org_name }}</p>
<div class="podium-list" id="podium-list"></div>
<button class="exit-btn" onclick="exitGame()">✕ salir</button>
</div>
+25 -2
View File
@@ -3,7 +3,7 @@
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>NI Quiz · Historial</title>
<title>{{ app_name }} · Historial</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Share+Tech+Mono&family=Exo+2:wght@400;600;800;900&display=swap');
@@ -156,6 +156,21 @@ tbody tr:hover{background:var(--surface2);}
/* animations */
@keyframes fadeUp{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:none}}
.fade-up{animation:fadeUp .4s ease both;}
/* ── THEME TOGGLE ── */
.theme-toggle{
position:fixed;top:1rem;right:1rem;z-index:10000;
display:flex;gap:.3rem;
background:var(--surface);border:1px solid var(--border);
border-radius:6px;padding:.3rem .4rem;box-shadow:0 2px 8px rgba(0,0,0,.15);
}
.t-btn{
padding:.28rem .55rem;border-radius:4px;font-size:.7rem;cursor:pointer;
border:1px solid transparent;background:transparent;color:var(--muted);
font-weight:700;transition:all .15s;
}
.t-btn:hover{color:var(--text);}
.t-btn.active{background:var(--accent);color:#000;}
</style>
</head>
<body>
@@ -163,7 +178,7 @@ tbody tr:hover{background:var(--surface2);}
<header>
<div class="header-left">
<h1>NI<span>QUIZ</span></h1>
<p>// HISTORIAL DE SCORES · NETWORK INTELLIGENCE · LCPR</p>
<p>// HISTORIAL DE SCORES · {{ org_name }}</p>
</div>
<nav class="nav-links">
<a href="/host">← HOST PANEL</a>
@@ -238,6 +253,14 @@ tbody tr:hover{background:var(--surface2);}
</main>
<script>
function setTheme(t) {
document.documentElement.setAttribute('data-theme', t);
localStorage.setItem('si-quiz-theme', t);
document.querySelectorAll('.t-btn').forEach(b => b.classList.remove('active'));
document.getElementById('t-' + t)?.classList.add('active');
}
(function(){ setTheme(localStorage.getItem('si-quiz-theme') || 'dark'); })();
const medals = ['🥇','🥈','🥉'];
const rankClass = ['gold','silver','bronze'];
const DEPT_COLORS = [