Phase 21: onboarding flow and new game reset

Onboarding overlay: on DOMContentLoaded the dashboard calls GET
/auth/status. If no profile exists, a full-screen setup card replaces
the normal UI (header, nav, and main are hidden). The user enters a
username and clicks Start Playing — POST /auth/local is called, then
the overlay hides and the Club tab loads normally. Pressing Enter in
the username field also submits. Validation: minimum 2 characters,
error message inline below the button.

Danger Zone in Settings tab: a red-bordered section with a Reset All
Progress button. Clicking opens a confirmation modal that requires the
user to type the word RESET before the action is enabled. On confirm,
POST /auth/reset is called, a toast is shown, and the page reloads
after 1.5 s — which triggers the onboarding flow again since the
profile was deleted.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-06-25 18:09:00 -07:00
parent 335398df72
commit 72ffe9a806
+150 -6
View File
@@ -247,6 +247,54 @@ h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;
</head> </head>
<body> <body>
<!-- ── Onboarding overlay (shown when no profile exists) ─────────────────── -->
<div id="onboarding-overlay" style="display:none;position:fixed;inset:0;z-index:1000;
background:#0d1117;align-items:center;justify-content:center">
<div style="background:#161b22;border:1px solid #30363d;border-radius:12px;
padding:40px 48px;max-width:460px;width:90%;text-align:center">
<div style="font-size:3rem;margin-bottom:16px"></div>
<h1 style="margin:0 0 8px;font-size:1.6rem">Welcome to OpenFUT</h1>
<p style="color:#8b949e;margin:0 0 28px;font-size:0.9rem">
Your offline FIFA 23 FUT club. Set up your manager profile to get started.
</p>
<div style="text-align:left;margin-bottom:20px">
<label style="display:block;font-size:0.8rem;color:#8b949e;margin-bottom:6px">Manager username</label>
<input id="ob-username" type="text" placeholder="Enter your username"
style="width:100%;box-sizing:border-box;background:#0d1117;border:1px solid #30363d;
color:#f0f6fc;padding:10px 14px;border-radius:6px;font-size:1rem"
onkeydown="if(event.key==='Enter')createProfile()" />
</div>
<button class="btn btn-primary" onclick="createProfile()"
style="width:100%;padding:12px;font-size:1rem">
Start Playing
</button>
<p id="ob-error" style="color:#f85149;font-size:0.82rem;margin:12px 0 0;display:none"></p>
</div>
</div>
<!-- ── Reset confirmation modal ──────────────────────────────────────────── -->
<div id="reset-modal" style="display:none;position:fixed;inset:0;z-index:900;
background:rgba(0,0,0,0.75);align-items:center;justify-content:center">
<div style="background:#161b22;border:1px solid #7f1d1d;border-radius:10px;
padding:32px 36px;max-width:420px;width:90%">
<h3 style="color:#f85149;margin:0 0 12px">Reset All Progress?</h3>
<p style="color:#8b949e;font-size:0.85rem;margin:0 0 20px">
This will permanently delete your profile, cards, coins, matches, achievements,
and all other data. There is no undo.
</p>
<p style="font-size:0.85rem;margin:0 0 10px">
Type <strong style="color:#f85149">RESET</strong> to confirm:
</p>
<input id="reset-confirm-input" type="text" placeholder="RESET"
style="width:100%;box-sizing:border-box;background:#0d1117;border:1px solid #7f1d1d;
color:#f0f6fc;padding:8px 12px;border-radius:6px;margin-bottom:16px" />
<div style="display:flex;gap:10px;justify-content:flex-end">
<button class="btn btn-secondary" onclick="closeResetModal()">Cancel</button>
<button class="btn btn-danger" onclick="executeReset()">Reset Everything</button>
</div>
</div>
</div>
<header> <header>
<h1>⚽ OpenFUT Dashboard</h1> <h1>⚽ OpenFUT Dashboard</h1>
<span class="badge">Offline</span> <span class="badge">Offline</span>
@@ -436,6 +484,14 @@ h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em;
</div> </div>
</div> </div>
</div> </div>
<div style="margin-top:32px;border:1px solid #7f1d1d;border-radius:8px;padding:20px 24px;background:#0d1117">
<h3 style="color:#f85149;margin:0 0 8px">Danger Zone</h3>
<p style="color:#8b949e;font-size:0.85rem;margin:0 0 16px">
Reset all progress — this permanently deletes your profile, club, cards, coins,
matches, achievements, and all other data. This action cannot be undone.
</p>
<button class="btn btn-danger" onclick="showResetModal()">Reset All Progress</button>
</div>
</div> </div>
<!-- ── Matches tab ───────────────────────────────────────────────────────── --> <!-- ── Matches tab ───────────────────────────────────────────────────────── -->
@@ -2092,6 +2148,90 @@ async function toggleEvent(eventId, activate, btn) {
// ── Notifications ───────────────────────────────────────────────────────────── // ── Notifications ─────────────────────────────────────────────────────────────
// ── Onboarding & Reset ───────────────────────────────────────────────────────
async function checkOnboarding() {
try {
const s = await api('GET', '/auth/status');
if (!s.has_profile) {
showOnboarding();
}
} catch (_) {
showOnboarding();
}
}
function showOnboarding() {
const overlay = el('onboarding-overlay');
overlay.style.display = 'flex';
document.querySelector('header').style.display = 'none';
document.querySelector('nav').style.display = 'none';
document.querySelector('main').style.display = 'none';
setTimeout(() => { const inp = el('ob-username'); if (inp) inp.focus(); }, 100);
}
function hideOnboarding() {
el('onboarding-overlay').style.display = 'none';
document.querySelector('header').style.display = '';
document.querySelector('nav').style.display = '';
document.querySelector('main').style.display = '';
}
async function createProfile() {
const username = (el('ob-username').value || '').trim();
if (!username) { showObError('Please enter a username.'); return; }
if (username.length < 2) { showObError('Username must be at least 2 characters.'); return; }
try {
const btn = document.querySelector('#onboarding-overlay button');
btn.disabled = true; btn.textContent = 'Creating…';
await api('POST', '/auth/local', { username });
hideOnboarding();
loadClub();
showTab_internal('club');
} catch (e) {
showObError(e.message || 'Failed to create profile.');
const btn = document.querySelector('#onboarding-overlay button');
btn.disabled = false; btn.textContent = 'Start Playing';
}
}
function showObError(msg) {
const err = el('ob-error');
if (err) { err.textContent = msg; err.style.display = 'block'; }
}
function showTab_internal(name) {
document.querySelectorAll('.tab-panel').forEach(p => p.classList.remove('active'));
document.querySelectorAll('nav button').forEach(b => b.classList.remove('active'));
const panel = el('tab-' + name);
if (panel) panel.classList.add('active');
const btn = document.querySelector(`nav button[onclick="showTab('${name}')"]`);
if (btn) btn.classList.add('active');
}
function showResetModal() {
el('reset-confirm-input').value = '';
el('reset-modal').style.display = 'flex';
setTimeout(() => el('reset-confirm-input').focus(), 50);
}
function closeResetModal() {
el('reset-modal').style.display = 'none';
}
async function executeReset() {
if (el('reset-confirm-input').value.trim() !== 'RESET') {
showToast('Type RESET exactly to confirm.', true);
return;
}
closeResetModal();
try {
await api('POST', '/auth/reset');
showToast('Progress reset. Reloading…');
setTimeout(() => window.location.reload(), 1500);
} catch (e) { showToast(e.message, true); }
}
// ── Achievements ───────────────────────────────────────────────────────────── // ── Achievements ─────────────────────────────────────────────────────────────
const ACH_RARITY_COLOR = { common: '#8b949e', rare: '#58a6ff', epic: '#bc8cff', legendary: '#f5a623' }; const ACH_RARITY_COLOR = { common: '#8b949e', rare: '#58a6ff', epic: '#bc8cff', legendary: '#f5a623' };
@@ -2220,12 +2360,16 @@ async function markAllNotificationsRead() {
// ── Init ───────────────────────────────────────────────────────────────────── // ── Init ─────────────────────────────────────────────────────────────────────
window.addEventListener('DOMContentLoaded', () => { window.addEventListener('DOMContentLoaded', () => {
loadClub(); checkOnboarding().then(() => {
// Load notification badge count silently // Only load club data if a profile exists (overlay hides header/nav if not)
api('GET', '/notifications').then(d => { if (el('onboarding-overlay').style.display === 'none') {
const unread = d.unread_count ?? (d.notifications || []).filter(n => !n.is_read).length; loadClub();
el('notif-badge').textContent = unread > 0 ? ` (${unread})` : ''; api('GET', '/notifications').then(d => {
}).catch(() => {}); const unread = d.unread_count ?? (d.notifications || []).filter(n => !n.is_read).length;
el('notif-badge').textContent = unread > 0 ? ` (${unread})` : '';
}).catch(() => {});
}
});
}); });
</script> </script>
</body> </body>