ssctopper/static/index.html

1036 lines
37 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>SSCTopper | 100,000+ SSC CGL Practice Questions & Mock Tests</title>
<meta name="description" content="Master the SSC CGL exam with SSCTopper. Access over 100,000 practice questions, detailed explanations, and performance analytics for Math, Reasoning, English, and GK.">
<meta name="keywords" content="SSC CGL, SSC Practice Questions, CGL Mock Test, SSC Exam Preparation, Quantitative Aptitude, Reasoning, English Language, General Awareness, SSCTopper">
<meta name="author" content="SSCTopper">
<link rel="canonical" href="https://ssctopper.com">
<!-- Open Graph / Facebook -->
<meta property="og:type" content="website">
<meta property="og:url" content="https://ssctopper.com/">
<meta property="og:title" content="SSCTopper | 100,000+ SSC CGL Practice Questions">
<meta property="og:description" content="Master the SSC CGL with elite practice tools and thousands of template-generated questions.">
<meta property="og:image" content="https://ssctopper.com/og_image.png">
<!-- Twitter -->
<meta property="twitter:card" content="summary_large_image">
<meta property="twitter:url" content="https://ssctopper.com/">
<meta property="twitter:title" content="SSCTopper | SSC CGL Question Bank">
<meta property="twitter:description" content="100,000+ Practice Questions for SSC CGL. Free tests and performance tracking.">
<meta property="twitter:image" content="https://ssctopper.com/og_image.png">
<link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;600;700&display=swap" rel="stylesheet">
<script src="https://accounts.google.com/gsi/client" async defer></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-2TK5WJ3GFR"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-2TK5WJ3GFR');
</script>
<!-- Microsoft Clarity -->
<script type="text/javascript">
(function(c,l,a,r,i,t,y){
c[a]=c[a]||function(){(c[a].q=c[a].q||[]).push(arguments)};
t=l.createElement(r);t.async=1;t.src="https://www.clarity.ms/tag/"+i;
y=l.getElementsByTagName(r)[0];y.parentNode.insertBefore(t,y);
})(window, document, "clarity", "script", "w35209edfr");
</script>
<!-- Structured Data -->
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "EducationalOrganization",
"name": "SSCTopper",
"url": "https://ssctopper.com",
"logo": "https://ssctopper.com/logo.png",
"description": "Premium SSC CGL practice platform with 100,000+ questions.",
"offers": {
"@type": "Service",
"name": "SSC CGL Question Bank",
"description": "Interactive question bank for SSC CGL exam preparation."
}
}
</script>
<style>
:root {
--primary: #6366f1;
--primary-dark: #4f46e5;
--bg: #0f172a;
--card-bg: rgba(30, 41, 59, 0.7);
--text-main: #f8fafc;
--text-dim: #94a3b8;
--glass-border: rgba(255, 255, 255, 0.1);
--gradient: linear-gradient(135deg, #6366f1 0%, #a855f7 100%);
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Outfit', sans-serif;
}
body {
background-color: var(--bg);
color: var(--text-main);
min-height: 100vh;
overflow-x: hidden;
background-image:
radial-gradient(circle at 0% 0%, rgba(99, 102, 241, 0.15) 0%, transparent 50%),
radial-gradient(circle at 100% 100%, rgba(168, 85, 247, 0.15) 0%, transparent 50%);
}
header {
padding: 2rem;
display: flex;
justify-content: space-between;
align-items: center;
backdrop-filter: blur(10px);
position: sticky;
top: 0;
z-index: 100;
border-bottom: 1px solid var(--glass-border);
}
.logo {
font-size: 1.5rem;
font-weight: 700;
background: var(--gradient);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
display: flex;
align-items: center;
gap: 0.5rem;
}
.stats-bar {
display: flex;
gap: 2rem;
}
.stat-item {
text-align: right;
}
.stat-value {
font-weight: 700;
color: var(--primary);
}
.stat-label {
font-size: 0.75rem;
color: var(--text-dim);
}
main {
max-width: 1200px;
margin: 0 auto;
padding: 2rem;
}
.hero {
text-align: center;
margin-bottom: 4rem;
padding: 4rem 2rem;
}
.hero h1 {
font-size: 3.5rem;
margin-bottom: 1rem;
letter-spacing: -0.02em;
}
.hero p {
color: var(--text-dim);
font-size: 1.25rem;
max-width: 600px;
margin: 0 auto 2rem;
}
.btn {
padding: 0.75rem 2rem;
border-radius: 9999px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
border: none;
outline: none;
}
.btn-primary {
background: var(--gradient);
color: white;
box-shadow: 0 10px 15px -3px rgba(99, 102, 241, 0.3);
}
.btn-primary:hover {
transform: translateY(-2px);
box-shadow: 0 20px 25px -5px rgba(99, 102, 241, 0.4);
}
.grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 1.5rem;
margin-top: 2rem;
}
.card {
background: var(--card-bg);
border: 1px solid var(--glass-border);
border-radius: 1.5rem;
padding: 2rem;
backdrop-filter: blur(12px);
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.card:hover {
border-color: var(--primary);
transform: translateY(-5px);
}
.card h3 {
margin-bottom: 0.5rem;
font-size: 1.25rem;
}
.card .q-count {
font-size: 2rem;
font-weight: 700;
margin: 1rem 0;
}
.view-section {
display: none;
animation: fadeIn 0.5s ease-out;
}
.view-section.active {
display: block;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Practice Area */
.practice-container {
max-width: 800px;
margin: 0 auto;
}
.question-card {
background: var(--card-bg);
border: 1px solid var(--glass-border);
border-radius: 2rem;
padding: 3rem;
margin-bottom: 2rem;
}
.q-meta {
display: flex;
gap: 1rem;
margin-bottom: 1.5rem;
font-size: 0.875rem;
color: var(--primary);
font-weight: 600;
text-transform: uppercase;
}
.q-text {
font-size: 1.5rem;
line-height: 1.4;
margin-bottom: 2.5rem;
}
.options-grid {
display: grid;
gap: 1rem;
}
.option {
background: rgba(255, 255, 255, 0.03);
border: 1px solid var(--glass-border);
padding: 1.25rem 1.5rem;
border-radius: 1rem;
cursor: pointer;
transition: all 0.2s;
display: flex;
align-items: center;
gap: 1rem;
}
.option:hover {
background: rgba(255, 255, 255, 0.08);
border-color: var(--primary);
}
.option.correct {
background: rgba(34, 197, 94, 0.2);
border-color: #22c55e;
}
.option.wrong {
background: rgba(239, 68, 68, 0.2);
border-color: #ef4444;
}
.opt-letter {
width: 32px;
height: 32px;
background: rgba(255, 255, 255, 0.1);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: 700;
}
.explanation {
margin-top: 2rem;
padding: 1.5rem;
background: rgba(99, 102, 241, 0.1);
border-left: 4px solid var(--primary);
border-radius: 0 1rem 1rem 0;
display: none;
}
/* Navigation */
.nav-breadcrumb {
margin-bottom: 2rem;
color: var(--text-dim);
display: flex;
gap: 0.5rem;
align-items: center;
}
.nav-link {
cursor: pointer;
color: var(--primary);
}
.nav-link:hover {
text-decoration: underline;
}
/* Syllabus Accordion */
.syllabus-item {
margin-bottom: 1rem;
}
.syllabus-header {
padding: 1rem 1.5rem;
background: rgba(255, 255, 255, 0.05);
border-radius: 1rem;
cursor: pointer;
display: flex;
justify-content: space-between;
align-items: center;
}
.syllabus-content {
padding: 1rem 2rem;
display: none;
}
.subject-card {
border-left: 6px solid var(--primary);
}
.back-btn {
display: inline-flex;
align-items: center;
gap: 0.5rem;
color: var(--text-dim);
cursor: pointer;
margin-bottom: 2rem;
}
.back-btn:hover {
color: var(--text-main);
}
/* Loading */
#loader {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 4px;
background: rgba(99, 102, 241, 0.2);
z-index: 1000;
display: none;
}
#loader-inner {
height: 100%;
background: var(--gradient);
width: 30%;
animation: loading 2s infinite linear;
}
@keyframes loading {
0% {
left: -30%;
}
100% {
left: 100%;
}
}
.topics-list {
margin-top: 1rem;
}
.topic-badge {
background: rgba(255, 255, 255, 0.05);
padding: 0.25rem 0.75rem;
border-radius: 99px;
font-size: 0.75rem;
color: var(--text-dim);
}
/* Modal */
.modal {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(15, 23, 42, 0.8);
backdrop-filter: blur(8px);
z-index: 1000;
align-items: center;
justify-content: center;
}
.modal-content {
background: var(--bg);
border: 1px solid var(--glass-border);
border-radius: 2rem;
padding: 3rem;
width: 100%;
max-width: 400px;
position: relative;
}
.modal-close {
position: absolute;
top: 1.5rem;
right: 1.5rem;
cursor: pointer;
color: var(--text-dim);
}
.form-group {
margin-bottom: 1.5rem;
}
.form-group label {
display: block;
margin-bottom: 0.5rem;
font-size: 0.875rem;
color: var(--text-dim);
}
.form-input {
width: 100%;
padding: 0.75rem 1rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid var(--glass-border);
border-radius: 0.75rem;
color: white;
outline: none;
}
.form-input:focus {
border-color: var(--primary);
}
/* Profile Styles */
.profile-header {
display: flex;
align-items: center;
gap: 2rem;
margin-bottom: 3rem;
padding: 2rem;
background: var(--card-bg);
border-radius: 2rem;
border: 1px solid var(--glass-border);
}
.profile-avatar {
width: 80px;
height: 80px;
background: var(--gradient);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 2rem;
font-weight: 700;
}
.progress-table {
width: 100%;
border-collapse: collapse;
margin-top: 2rem;
}
.progress-table th,
.progress-table td {
padding: 1rem;
text-align: left;
border-bottom: 1px solid var(--glass-border);
}
.progress-bar-bg {
width: 100%;
height: 8px;
background: rgba(255, 255, 255, 0.05);
border-radius: 4px;
overflow: hidden;
}
.progress-bar-fill {
height: 100%;
background: var(--gradient);
transition: width 0.5s ease;
}
</style>
</head>
<body>
<div id="loader">
<div id="loader-inner"></div>
</div>
<header>
<div class="logo">
<span>🚀</span> SSCTopper
</div>
<div class="stats-bar" id="stats-header">
<!-- Loaded via JS -->
</div>
<div id="auth-controls">
<button class="btn" style="background: rgba(255,255,255,0.05); color: white;"
onclick="showAuthModal('login')">Login</button>
<button class="btn btn-primary" onclick="showAuthModal('signup')">Sign Up</button>
</div>
<div id="user-controls" style="display: none;">
<span id="user-name" style="margin-right: 1rem; font-weight: 500; font-size: 0.875rem;"></span>
<button class="btn" style="background: rgba(255,255,255,0.05); color: white;"
onclick="showProfile()">Profile</button>
<button class="btn" style="background: rgba(255,255,255,0.05); color: white; margin-left: 0.5rem;" onclick="logout()">Logout</button>
</div>
</header>
<main>
<!-- Home View -->
<section id="view-home" class="view-section active">
<div class="hero">
<h1>Master the SSC CGL</h1>
<p>Access 100,000+ template-generated questions across Quantitative Aptitude, Reasoning, English, and
General Awareness.</p>
<button class="btn btn-primary" onclick="showSyllabus()">Browse Syllabus</button>
</div>
<div class="grid" id="subject-grid">
<!-- Subject cards via JS -->
</div>
</section>
<!-- Syllabus View -->
<section id="view-syllabus" class="view-section">
<div class="back-btn" onclick="showHome()">← Back to Home</div>
<h1>Full Syllabus Browser</h1>
<p style="color: var(--text-dim); margin-bottom: 2rem;">Explore topics and start practicing specifically for
each type.</p>
<div id="syllabus-container">
<!-- Syllabus tree via JS -->
</div>
</section>
<!-- Practice View -->
<section id="view-practice" class="view-section">
<div class="back-btn" onclick="showHome()">← Exit Practice</div>
<div class="practice-container">
<div class="nav-breadcrumb" id="practice-breadcrumb"></div>
<div id="question-area">
<!-- Questions via JS -->
</div>
<div style="text-align: center; margin-top: 2rem;">
<button class="btn btn-primary" id="btn-next" onclick="loadNextQuestion()">Next Question</button>
</div>
</div>
</section>
<!-- Profile View -->
<section id="view-profile" class="view-section">
<div class="back-btn" onclick="showHome()">← Back to Home</div>
<div id="profile-container">
<!-- Profile data via JS -->
</div>
</section>
</main>
<!-- Auth Modal -->
<div id="auth-modal" class="modal">
<div class="modal-content">
<div class="modal-close" onclick="closeAuthModal()"></div>
<h2 id="auth-title" style="margin-bottom: 2rem;">Login</h2>
<div id="auth-error" style="color: #ef4444; margin-bottom: 1rem; font-size: 0.875rem; display: none;"></div>
<div id="signup-fields" style="display: none;">
<div class="form-group">
<label>Email Address</label>
<input type="email" id="auth-email" class="form-input" placeholder="you@example.com">
</div>
</div>
<div class="form-group">
<label>Username</label>
<input type="text" id="auth-username" class="form-input" placeholder="Your username">
</div>
<div class="form-group">
<label>Password</label>
<input type="password" id="auth-password" class="form-input" placeholder="••••••••">
</div>
<button class="btn btn-primary" style="width: 100%;" id="auth-submit-btn"
onclick="handleAuth()">Continue</button>
<div style="margin: 1.5rem 0; display: flex; align-items: center; gap: 1rem;">
<div style="flex: 1; height: 1px; background: var(--glass-border);"></div>
<span style="font-size: 0.75rem; color: var(--text-dim);">OR</span>
<div style="flex: 1; height: 1px; background: var(--glass-border);"></div>
</div>
<div id="google-signin-btn" style="display: flex; justify-content: center;"></div>
<p style="text-align: center; font-size: 0.875rem; color: var(--text-dim); margin-top: 1.5rem;">
<span id="auth-toggle-text">Don't have an account?</span>
<span style="color: var(--primary); cursor: pointer;" onclick="toggleAuthMode()"
id="auth-toggle-link">Sign Up</span>
</p>
</div>
</div>
</main>
<script>
const API_BASE = '';
let currentSubject = null;
let currentTopic = null;
let currentQType = null;
let currentQuestions = [];
let currentQIdx = 0;
let currentUser = null;
let authMode = 'login';
let questionStartTime = null;
async function api(path, method = 'GET', body = null) {
document.getElementById('loader').style.display = 'block';
try {
const options = { method };
if (body) {
options.body = JSON.stringify(body);
options.headers = { 'Content-Type': 'application/json' };
}
const res = await fetch(`${API_BASE}${path}`, options);
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'Request failed');
return data;
} finally {
document.getElementById('loader').style.display = 'none';
}
}
async function init() {
const stats = await api('/api/stats');
updateStatsHeader(stats);
const syllabus = await api('/api/syllabus');
renderSubjects(syllabus);
renderSyllabus(syllabus);
// Check if already logged in (try to get profile)
try {
const profile = await api('/api/user/profile');
onLoginSuccess(profile);
} catch (e) {
// Not logged in
}
}
function updateStatsHeader(stats) {
const container = document.getElementById('stats-header');
container.innerHTML = `
<div class="stat-item">
<div class="stat-value">${(stats.total || 0).toLocaleString()}</div>
<div class="stat-label">Total Questions</div>
</div>
<div class="stat-item">
<div class="stat-value">${stats.topic_count || 0}</div>
<div class="stat-label">Topics</div>
</div>
`;
}
function renderSubjects(syllabus) {
const grid = document.getElementById('subject-grid');
grid.innerHTML = syllabus.map(s => `
<div class="card subject-card" onclick="startPractice({subject_id: ${s.id}, name: '${s.name}'})">
<div class="stat-label">${s.tier}</div>
<h3>${s.name}</h3>
<div class="q-count">${(s.question_count || 0).toLocaleString()}</div>
<div class="stat-label">Practice Questions</div>
</div>
`).join('');
}
function renderSyllabus(syllabus) {
const container = document.getElementById('syllabus-container');
container.innerHTML = syllabus.map(s => `
<div class="syllabus-item">
<div class="syllabus-header" onclick="toggleSyllabus(this)">
<div>
<strong>${s.name}</strong>
<span class="topic-badge">${s.question_count.toLocaleString()} Qs</span>
</div>
<span>▼</span>
</div>
<div class="syllabus-content">
${s.subtopics.map(st => `
<div style="margin-bottom: 1.5rem;">
<h4 style="color: var(--primary); margin-bottom: 0.5rem;">${st.name}</h4>
<div style="display: grid; gap: 0.5rem;">
${st.topics.map(t => `
<div style="background: rgba(255,255,255,0.03); padding: 0.75rem; border-radius: 0.5rem; display: flex; justify-content: space-between; align-items: center;">
<div>${t.name} <span class="topic-badge">${t.question_count} Qs</span></div>
<button class="btn btn-primary" style="padding: 0.4rem 1rem; font-size: 0.8rem;"
onclick="startPractice({topic_id: ${t.id}, name: '${t.name}'})">
Practice
</button>
</div>
`).join('')}
</div>
</div>
`).join('')}
</div>
</div>
`).join('');
}
function toggleSyllabus(el) {
const content = el.nextElementSibling;
const isVisible = content.style.display === 'block';
content.style.display = isVisible ? 'none' : 'block';
el.querySelector('span').innerText = isVisible ? '▼' : '▲';
}
function showView(viewId) {
document.querySelectorAll('.view-section').forEach(v => v.classList.remove('active'));
document.getElementById(viewId).classList.add('active');
window.scrollTo(0, 0);
}
function showHome() { showView('view-home'); }
function showSyllabus() { showView('view-syllabus'); }
async function startPractice(params) {
document.getElementById('practice-breadcrumb').innerText = params.name;
currentQuestions = [];
currentQIdx = 0;
let query = '';
if (params.subject_id) query = `?subject_id=${params.subject_id}`;
else if (params.topic_id) query = `?topic_id=${params.topic_id}`;
const data = await api(`/api/questions${query}&limit=50`);
currentQuestions = data.questions;
showView('view-practice');
renderQuestion();
}
function renderQuestion() {
if (currentQIdx >= currentQuestions.length) {
document.getElementById('question-area').innerHTML = `
<div class="question-card" style="text-align: center;">
<h2>Session Complete!</h2>
<p style="margin: 1rem 0; color: var(--text-dim);">You've practiced all loaded questions.</p>
<button class="btn btn-primary" onclick="showHome()">Back to Home</button>
</div>
`;
document.getElementById('btn-next').style.display = 'none';
return;
}
const q = currentQuestions[currentQIdx];
const area = document.getElementById('question-area');
area.innerHTML = `
<div class="question-card">
<div class="q-meta">${q.subject}${q.topic}${q.qtype}</div>
<div class="q-text">${q.question_text}</div>
<div class="options-grid">
${Object.entries(q.options).map(([key, val]) => `
<div class="option" onclick="checkAnswer(this, '${key}')">
<span class="opt-letter">${key}</span>
<span class="opt-val">${val}</span>
</div>
`).join('')}
</div>
<div class="explanation" id="ex-box">
<strong style="color: var(--primary)">Explanation:</strong><br>
${q.explanation || 'No explanation available.'}
</div>
</div>
`;
document.getElementById('btn-next').style.display = 'none';
questionStartTime = Date.now();
}
function checkAnswer(el, choice) {
const q = currentQuestions[currentQIdx];
const options = el.parentElement.querySelectorAll('.option');
// Disable further clicks
options.forEach(opt => opt.onclick = null);
if (choice === q.correct_option) {
el.classList.add('correct');
} else {
el.classList.add('wrong');
// Find and highlight correct answer
options.forEach(opt => {
if (opt.querySelector('.opt-letter').innerText === q.correct_option) {
opt.classList.add('correct');
}
});
}
document.getElementById('ex-box').style.display = 'block';
document.getElementById('btn-next').style.display = 'inline-block';
// Report progress if logged in
const timeTaken = (Date.now() - questionStartTime) / 1000;
reportProgress(q.id, choice === q.correct_option, timeTaken);
}
function loadNextQuestion() {
currentQIdx++;
renderQuestion();
}
// Auth Logic
function showAuthModal(mode) {
authMode = mode;
document.getElementById('auth-modal').style.display = 'flex';
updateAuthUI();
initGoogleBtn();
}
function initGoogleBtn() {
if (typeof google === 'undefined') return;
google.accounts.id.initialize({
client_id: "273072123939-dd82h4o1rt3k7811sri6qgsig73b3916.apps.googleusercontent.com",
callback: handleGoogleCallback
});
google.accounts.id.renderButton(
document.getElementById("google-signin-btn"),
{ theme: "dark", size: "large", width: 340, shape: "pill" }
);
}
async function handleGoogleCallback(response) {
try {
const data = await api('/api/auth/google', 'POST', { id_token: response.credential });
onLoginSuccess(data);
closeAuthModal();
} catch (err) {
const errorEl = document.getElementById('auth-error');
errorEl.innerText = err.message;
errorEl.style.display = 'block';
}
}
function closeAuthModal() {
document.getElementById('auth-modal').style.display = 'none';
document.getElementById('auth-error').style.display = 'none';
}
function toggleAuthMode() {
authMode = authMode === 'login' ? 'signup' : 'login';
updateAuthUI();
}
function updateAuthUI() {
const isLogin = authMode === 'login';
document.getElementById('auth-title').innerText = isLogin ? 'Login' : 'Sign Up';
document.getElementById('signup-fields').style.display = isLogin ? 'none' : 'block';
document.getElementById('auth-toggle-text').innerText = isLogin ? "Don't have an account?" : "Already have an account?";
document.getElementById('auth-toggle-link').innerText = isLogin ? 'Sign Up' : 'Login';
}
async function handleAuth() {
const username = document.getElementById('auth-username').value;
const password = document.getElementById('auth-password').value;
const email = document.getElementById('auth-email').value;
const errorEl = document.getElementById('auth-error');
errorEl.style.display = 'none';
try {
if (authMode === 'signup') {
await api('/api/auth/signup', 'POST', { username, email, password });
// After signup, auto-login
authMode = 'login';
}
const data = await api('/api/auth/login', 'POST', { username, password });
onLoginSuccess(data);
closeAuthModal();
} catch (err) {
errorEl.innerText = err.message;
errorEl.style.display = 'block';
}
}
function onLoginSuccess(user) {
currentUser = user;
document.getElementById('auth-controls').style.display = 'none';
document.getElementById('user-controls').style.display = 'flex';
document.getElementById('user-name').innerText = user.username;
closeAuthModal();
}
function logout() {
currentUser = null;
document.cookie = "session_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
location.reload();
}
async function showProfile(timeframe = 'overall') {
try {
const profile = await api(`/api/user/profile?timeframe=${timeframe}`);
renderProfile(profile, timeframe);
showView('view-profile');
} catch (err) {
showAuthModal('login');
}
}
function renderProfile(profile, currentTimeframe = 'overall') {
const container = document.getElementById('profile-container');
container.innerHTML = `
<div class="profile-header">
<div class="profile-avatar">${profile.username[0].toUpperCase()}</div>
<div style="flex: 1">
<h1>${profile.username}</h1>
<p style="color: var(--text-dim)">${profile.email} • Joined ${new Date(profile.joined).toLocaleDateString()}</p>
</div>
<div>
<select class="form-input" style="width: auto; background: rgba(255,255,255,0.1);" onchange="showProfile(this.value)">
<option value="overall" ${currentTimeframe === 'overall' ? 'selected' : ''}>Overall Stats</option>
<option value="daily" ${currentTimeframe === 'daily' ? 'selected' : ''}>Last 24 Hours</option>
<option value="weekly" ${currentTimeframe === 'weekly' ? 'selected' : ''}>Last 7 Days</option>
<option value="monthly" ${currentTimeframe === 'monthly' ? 'selected' : ''}>This Month</option>
</select>
</div>
</div>
<div class="grid" style="margin-bottom: 3rem;">
<div class="card">
<div class="stat-label">Total Attempts</div>
<div class="q-count">${profile.stats.total_attempts}</div>
</div>
<div class="card">
<div class="stat-label">Correct Answers</div>
<div class="q-count">${profile.stats.correct_attempts}</div>
</div>
<div class="card">
<div class="stat-label">Accuracy</div>
<div class="q-count">${profile.stats.accuracy}%</div>
</div>
<div class="card">
<div class="stat-label">Avg Pace (sec)</div>
<div class="q-count">${profile.stats.avg_time}s</div>
</div>
</div>
<h2 style="margin-bottom: 2rem;">Topic-wise Analytics</h2>
<div class="card" style="padding: 0; overflow: hidden; background: rgba(255,255,255,0.02);">
<table class="progress-table">
<thead>
<tr>
<th style="text-align: left">Subject > Topic</th>
<th>Mastered</th>
<th>Pace (Avg)</th>
<th style="text-align: right">Progress</th>
</tr>
</thead>
<tbody>
${profile.topic_progress.filter(t => t.total > 0).map(t => `
<tr>
<td>
<div style="font-weight: 600">${t.topic}</div>
<div style="font-size: 0.75rem; color: var(--text-dim)">${t.subject} > ${t.subtopic}</div>
</td>
<td style="text-align: center">${t.answered} / ${t.total}</td>
<td style="text-align: center">${t.avg_time}s</td>
<td style="text-align: right">
<div style="display: flex; align-items: center; gap: 0.8rem; justify-content: flex-end;">
<div class="progress-bar-bg" style="width: 120px; height: 10px; margin: 0; background: rgba(255,255,255,0.1); border-radius: 5px; overflow: hidden;">
<div class="progress-bar-fill" style="width: ${t.percent}%; height: 100%; background: var(--gradient);"></div>
</div>
<span style="font-size: 0.875rem; font-weight: 500; min-width: 45px;">${t.percent}%</span>
</div>
</td>
</tr>
`).join('')}
</tbody>
</table>
</div>
`;
}
async function reportProgress(questionId, isCorrect, timeTaken) {
if (!currentUser) return;
try {
await api('/api/user/progress', 'POST', { question_id: questionId, is_correct: isCorrect, time_taken: timeTaken });
} catch (e) {
console.error("Failed to report progress", e);
}
}
init();
</script>
</body>
</html>