Skip to content

Commit e3cfb3e

Browse files
Added profile icon for accessing the details of the user.
1 parent 64ad056 commit e3cfb3e

3 files changed

Lines changed: 249 additions & 218 deletions

File tree

public/hub.html

Lines changed: 2 additions & 208 deletions
Original file line numberDiff line numberDiff line change
@@ -34,14 +34,13 @@
3434
<button class="secondary-btn" onclick="toggleHighContrast()" style="padding: 5px 10px; font-size: 10px;">🌓 High Contrast</button>
3535
<button id="replayTourBtn" class="secondary-btn" onclick="replayTour()" title="Replay the onboarding tour">🧭 Tour</button>
3636
<button class="secondary-btn" onclick="showHowToPlay()" title="How to play CyberArena" style="padding: 5px 10px; font-size: 10px;">🎮 Guide</button>
37+
<button class="secondary-btn" onclick="window.location.href='profile.html'" title="Your Profile" style="padding: 5px 10px; font-size: 10px;">👤 Profile</button>
3738
</div>
3839
<button class="logout-btn" onclick="logout()">Logout</button>
3940
</div>
4041

4142
<h1>🛡️ CyberArena</h1>
4243
<p>Select Your Mission</p>
43-
44-
<!-- How to Play Modal -->
4544
<div id="howToPlayModal" class="modal" style="display:none;">
4645
<div class="modal-content" style="max-width:600px; padding:30px;">
4746
<h2 style="color:var(--neon-cyan); margin-top:0;">🎮 How to Play CyberArena</h2>
@@ -122,94 +121,14 @@ <h3 class="section-title">&#x1F6E0;&#xFE0F; Tools &amp; Knowledge</h3>
122121
</div>
123122
</div>
124123

125-
<hr />
126-
127-
<div class="user-stats">
128-
<h2>📊 Your Profile</h2>
129-
<div class="profile-layout">
130-
<div class="xp-container">
131-
<div id="profileSkeleton">
132-
<div class="skeleton" style="width:60%;"></div>
133-
<div class="skeleton" style="width:100%; height:12px;"></div>
134-
<div class="skeleton" style="width:40%;"></div>
135-
<div class="skeleton" style="width:50%;"></div>
136-
</div>
137-
<div id="profileData" style="display:none;">
138-
<p id="rankLabel" class="rank-label">Rank: Novice</p>
139-
<div class="progress-bar">
140-
<div id="userXPFill" style="width: 0%;"></div>
141-
</div>
142-
<p id="userXP">XP: Loading...</p>
143-
<p id="missionsCompleted">Missions Completed: Loading...</p>
144-
<button id="shareScoreBtn" class="secondary-btn" onclick="shareScore()" style="margin-top:10px; padding:8px 16px; font-size:11px; display:none;">📤 Share Your Score</button>
145-
146-
<!-- Mission Completion Tracker -->
147-
<div id="missionTracker" style="margin-top:20px; display:none;">
148-
<h4 style="font-size:12px; color:var(--neon-cyan); margin-bottom:8px;">✅ Completed Missions</h4>
149-
<div id="missionCheckList" style="font-size:11px; line-height:1.8; color:#9ca3af;"></div>
150-
</div>
151-
</div>
152-
</div>
153-
154-
<div class="achievement-gallery">
155-
<h3>🏅 Achievements</h3>
156-
<div id="badgeList" class="badge-list">
157-
<!-- Badges will be injected here -->
158-
</div>
159-
</div>
160-
</div>
161-
</div>
162-
163-
<div class="leaderboard">
164-
<h2>🏆 Global Leaderboard</h2>
165-
<div id="leaderboardSkeleton">
166-
<div class="skeleton" style="width:80%;"></div>
167-
<div class="skeleton" style="width:65%;"></div>
168-
<div class="skeleton" style="width:72%;"></div>
169-
<div class="skeleton" style="width:55%;"></div>
170-
<div class="skeleton" style="width:68%;"></div>
171-
</div>
172-
<ul id="leaderboardList" style="display:none;">
173-
<li>Loading...</li>
174-
</ul>
175-
</div>
176124
</div>
177125

178126
<script type="module">
179127
import { auth, db } from "./js/firebase.js";
180128
import { onAuthStateChanged } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-auth.js";
181129
import { getUserData } from "./js/xp.js";
182-
import { collection, query, orderBy, limit, getDocs } from "https://www.gstatic.com/firebasejs/10.12.2/firebase-firestore.js";
183130
import { initTour } from "./js/tour.js";
184-
import { applyLockStates, getNewlyUnlocked, showUnlockToast, UNLOCKS, getUnlockXP } from "./js/unlocks.js";
185-
186-
async function loadLeaderboard() {
187-
try {
188-
const list = document.getElementById("leaderboardList");
189-
const q = query(collection(db, "users"), orderBy("xp", "desc"), limit(5));
190-
const querySnapshot = await getDocs(q);
191-
192-
list.innerHTML = "";
193-
if (querySnapshot.empty) {
194-
list.innerHTML = "<li>No data available</li>";
195-
} else {
196-
querySnapshot.forEach((doc) => {
197-
const data = doc.data();
198-
const li = document.createElement("li");
199-
const name = data.email ? data.email.split('@')[0] : "Anonymous";
200-
li.innerHTML = `<span>${name}</span> <span>${data.xp || 0} XP</span>`;
201-
list.appendChild(li);
202-
});
203-
}
204-
document.getElementById("leaderboardSkeleton").style.display = "none";
205-
list.style.display = "block";
206-
} catch (error) {
207-
console.error("Error loading leaderboard:", error);
208-
document.getElementById("leaderboardSkeleton").style.display = "none";
209-
document.getElementById("leaderboardList").innerHTML = "<li>Failed to load leaderboard</li>";
210-
document.getElementById("leaderboardList").style.display = "block";
211-
}
212-
}
131+
import { applyLockStates, getNewlyUnlocked, showUnlockToast } from "./js/unlocks.js";
213132

214133
function startActivityTicker() {
215134
const activities = [
@@ -240,130 +159,24 @@ <h2>🏆 Global Leaderboard</h2>
240159
const userData = await getUserData();
241160
if (userData) {
242161
const xp = userData.xp || 0;
243-
document.getElementById("userXP").innerText = `XP: ${xp}`;
244-
document.getElementById("missionsCompleted").innerText = `Missions Completed: ${userData.missionsCompleted || 0}`;
245-
246-
// Rank Calculation
247-
let rank = "Novice";
248-
let nextXP = 100;
249-
let currentLevelXP = 0;
250-
251-
if (xp >= 500) {
252-
rank = "Elite Guardian";
253-
nextXP = 1000;
254-
currentLevelXP = 500;
255-
} else if (xp >= 100) {
256-
rank = "Specialist";
257-
nextXP = 500;
258-
currentLevelXP = 100;
259-
}
260-
261-
const progress = ((xp - currentLevelXP) / (nextXP - currentLevelXP)) * 100;
262-
document.getElementById("rankLabel").innerText = `Rank: ${rank}`;
263-
document.getElementById("userXPFill").style.width = `${Math.min(progress, 100)}%`;
264-
265-
// Apply lock/unlock states to mission buttons
266162
applyLockStates(xp);
267-
268-
// Show unlock toasts for anything newly unlocked this session
269163
const prevXP = parseInt(localStorage.getItem('prevXP') || '0');
270164
if (prevXP < xp) {
271165
getNewlyUnlocked(prevXP, xp).forEach((u, i) => {
272166
setTimeout(() => showUnlockToast(u), i * 1000);
273167
});
274168
}
275169
localStorage.setItem('prevXP', xp);
276-
277-
// Show profile data, hide skeleton
278-
document.getElementById("profileSkeleton").style.display = "none";
279-
document.getElementById("profileData").style.display = "block";
280-
document.getElementById("shareScoreBtn").style.display = "block";
281-
282-
// Mission Completion Tracker
283-
const completedMissions = userData.completedMissions || [];
284-
const allMissions = [
285-
{ id: 'phishing', name: '🕵️ Phishing Detective' },
286-
{ id: 'social', name: '🎭 Social Engineering' },
287-
{ id: 'smishing', name: '📱 Smishing Simulator' },
288-
{ id: 'ai', name: '🤖 AI Crime Lab' },
289-
{ id: 'malware', name: '🧩 Malware Escape' },
290-
{ id: 'password', name: '🛡️ Password Lab' },
291-
{ id: 'darkweb', name: '🌐 Dark Web Market' },
292-
{ id: 'incident', name: '🚨 Incident Response' },
293-
{ id: 'creator', name: '🧪 Mission Creator' },
294-
{ id: 'wiki', name: '📖 Cyber-Wiki' }
295-
];
296-
297-
const missionCheckList = document.getElementById('missionCheckList');
298-
missionCheckList.innerHTML = allMissions.map(m => {
299-
const completed = completedMissions.includes(m.id);
300-
return `<div>${completed ? '✅' : '⬜'} ${m.name}</div>`;
301-
}).join('');
302-
document.getElementById('missionTracker').style.display = 'block';
303-
304-
// Unlocks Panel
305-
const unlockPanel = document.createElement('div');
306-
unlockPanel.className = 'unlocks-panel';
307-
unlockPanel.innerHTML = `<h4 style="font-size:12px;color:var(--neon-cyan);margin-bottom:8px;">🔓 Unlocks</h4>` +
308-
UNLOCKS.filter(u => u.type === 'mission' || u.type === 'feature').map(u => {
309-
const done = xp >= u.xpRequired;
310-
const pct = Math.min(100, Math.round((xp / u.xpRequired) * 100));
311-
return `<div class="unlock-row">
312-
<span class="unlock-icon">${done ? '✅' : '🔒'}</span>
313-
<span class="unlock-label">${u.label}</span>
314-
${done
315-
? `<span class="unlock-status-done">Unlocked</span>`
316-
: `<div class="unlock-bar"><div class="unlock-bar-fill" style="width:${pct}%"></div></div>
317-
<span class="unlock-status-locked">${u.xpRequired} XP</span>`
318-
}
319-
</div>`;
320-
}).join('');
321-
document.getElementById('profileData').appendChild(unlockPanel);
322-
323-
// Achievements Display
324-
const badgeList = document.getElementById("badgeList");
325-
badgeList.innerHTML = "";
326-
327-
const possibleBadges = [
328-
{ id: "rookie", name: "Rookie", icon: "🔰", threshold: 0 },
329-
{ id: "detective", name: "Detective", icon: "🔍", threshold: 50 },
330-
{ id: "shield", name: "Shield", icon: "🛡️", threshold: 200 },
331-
{ id: "elite", name: "Elite", icon: "👑", threshold: 500 }
332-
];
333-
334-
possibleBadges.forEach(badge => {
335-
if (xp >= badge.threshold) {
336-
const span = document.createElement("span");
337-
span.className = "badge";
338-
span.title = badge.name;
339-
span.innerText = badge.icon;
340-
badgeList.appendChild(span);
341-
}
342-
});
343170
}
344-
await loadLeaderboard();
345171
startActivityTicker();
346-
347-
// Show How to Play modal on first login
348172
if (!localStorage.getItem('cyberarena_guide_seen')) {
349173
setTimeout(() => {
350174
document.getElementById('howToPlayModal').style.display = 'flex';
351175
}, 800);
352176
}
353-
354-
// Start tour after everything is loaded
355177
initTour();
356178
} catch (error) {
357179
console.error("Error loading user data:", error);
358-
// Graceful offline / network failure fallback
359-
document.getElementById("profileSkeleton").style.display = "none";
360-
document.getElementById("profileData").style.display = "block";
361-
document.getElementById("userXP").innerText = "XP: unavailable (offline)";
362-
document.getElementById("missionsCompleted").innerText = "";
363-
document.getElementById("rankLabel").innerText = "Rank: --";
364-
document.getElementById("leaderboardSkeleton").style.display = "none";
365-
document.getElementById("leaderboardList").innerHTML = "<li style='color:#666;'>Leaderboard unavailable — check your connection.</li>";
366-
document.getElementById("leaderboardList").style.display = "block";
367180
startActivityTicker();
368181
}
369182
}
@@ -381,28 +194,9 @@ <h2>🏆 Global Leaderboard</h2>
381194
localStorage.setItem('cyberarena_guide_seen', '1');
382195
}
383196

384-
// Close modal on backdrop click
385197
document.getElementById('howToPlayModal').addEventListener('click', (e) => {
386198
if (e.target === document.getElementById('howToPlayModal')) closeHowToPlay();
387199
});
388-
389-
function shareScore() {
390-
const xp = document.getElementById('userXP').innerText;
391-
const rank = document.getElementById('rankLabel').innerText;
392-
const missions = document.getElementById('missionsCompleted').innerText;
393-
const text = `🛡️ CyberArena Training Report\n${rank}\n${xp}\n${missions}\n\nI'm training to become an Elite Guardian in cybersecurity! Try CyberArena 🔗`;
394-
395-
if (navigator.share) {
396-
navigator.share({ title: 'CyberArena Score', text });
397-
} else {
398-
navigator.clipboard.writeText(text).then(() => {
399-
const btn = document.getElementById('shareScoreBtn');
400-
const orig = btn.innerText;
401-
btn.innerText = '✅ Copied!';
402-
setTimeout(() => btn.innerText = orig, 2000);
403-
});
404-
}
405-
}
406200
</script>
407201
</body>
408202
</html>

public/js/tour.js

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,16 +36,10 @@ const steps = [
3636
position: 'top',
3737
},
3838
{
39-
target: '.user-stats',
40-
title: '📊 Your Profile & XP',
41-
text: 'Every correct decision earns you XP. Your rank climbs from Novice → Specialist → Elite Guardian. Unlock achievement badges as you hit XP milestones.',
42-
position: 'top',
43-
},
44-
{
45-
target: '.leaderboard',
46-
title: '🏆 Global Leaderboard',
47-
text: 'The top 5 players by XP are shown here in real time. Complete missions, earn XP, and climb the ranks. You\'re all set — good luck, Guardian!',
48-
position: 'top',
39+
target: '.top-bar',
40+
title: '👤 Your Profile & Leaderboard',
41+
text: 'Click the 👤 Profile button in the top bar to view your XP, rank, achievement badges, completed missions, unlocks progress, and the Global Leaderboard — all in one place.',
42+
position: 'bottom',
4943
},
5044
];
5145

0 commit comments

Comments
 (0)