From 72ffe9a8068c5af95bf6ba4b0b0da5a33680fdec Mon Sep 17 00:00:00 2001 From: funman300 Date: Thu, 25 Jun 2026 18:09:00 -0700 Subject: [PATCH] Phase 21: onboarding flow and new game reset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- src/dashboard.html | 156 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 150 insertions(+), 6 deletions(-) diff --git a/src/dashboard.html b/src/dashboard.html index 90328f8..c739d88 100644 --- a/src/dashboard.html +++ b/src/dashboard.html @@ -247,6 +247,54 @@ h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; + + + + + +

⚽ OpenFUT Dashboard

Offline @@ -436,6 +484,14 @@ h2 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; +
+

Danger Zone

+

+ Reset all progress — this permanently deletes your profile, club, cards, coins, + matches, achievements, and all other data. This action cannot be undone. +

+ +
@@ -2092,6 +2148,90 @@ async function toggleEvent(eventId, activate, btn) { // ── 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 ───────────────────────────────────────────────────────────── const ACH_RARITY_COLOR = { common: '#8b949e', rare: '#58a6ff', epic: '#bc8cff', legendary: '#f5a623' }; @@ -2220,12 +2360,16 @@ async function markAllNotificationsRead() { // ── Init ───────────────────────────────────────────────────────────────────── window.addEventListener('DOMContentLoaded', () => { - loadClub(); - // Load notification badge count silently - api('GET', '/notifications').then(d => { - const unread = d.unread_count ?? (d.notifications || []).filter(n => !n.is_read).length; - el('notif-badge').textContent = unread > 0 ? ` (${unread})` : ''; - }).catch(() => {}); + checkOnboarding().then(() => { + // Only load club data if a profile exists (overlay hides header/nav if not) + if (el('onboarding-overlay').style.display === 'none') { + loadClub(); + api('GET', '/notifications').then(d => { + const unread = d.unread_count ?? (d.notifications || []).filter(n => !n.is_read).length; + el('notif-badge').textContent = unread > 0 ? ` (${unread})` : ''; + }).catch(() => {}); + } + }); });