GphaHoa156
Add application file
8b3b66c
{% extends "layouts/base.html" %}
{% block title %}HSK {{ level }} · {{ mode|title }} · HanziLearn{% endblock %}
{% block extra_css %}
<link href="{{ url_for('static', filename='css/elearning.css') }}" rel="stylesheet" />
{% endblock %}
{% block content %}
<!-- LEARN HEADER
NOTE: The CSS variable --lc is set via JavaScript (see LEARN_CONFIG below)
to avoid Jinja2 inline-style linter false positives. -->
<div class="learn-header" id="learnHeader">
<div class="container">
<div class="d-flex align-items-center flex-wrap gap-3">
<div class="learn-badge" id="learnBadge">HSK {{ level }}</div>
<div>
<h1 class="learn-title mb-0">{{ meta.desc }}</h1>
<p class="learn-subtitle mb-0">{{ meta.words }} vocabulary words</p>
</div>
<div class="ms-auto">
<span class="learn-user-chip">
<i class="bi bi-person-fill me-1"></i>
<span id="learnUsername">Guest</span>
</span>
</div>
</div>
<!-- Mode Tabs -->
<div class="mode-tabs mt-4">
{% set mode_icons = {
'flashcard': 'card-text',
'quiz': 'patch-question',
'fillblank': 'pencil-square',
'leaderboard': 'trophy'
} %}
{% set mode_labels = {
'flashcard': 'Flashcard',
'quiz': 'Quiz',
'fillblank': 'Fill Blank',
'leaderboard': 'Leaderboard'
} %}
{% for m in valid_modes %}
<a href="/learn/{{ level }}?mode={{ m }}"
class="mode-tab{% if mode == m %} active{% endif %}">
<i class="bi bi-{{ mode_icons[m] }} me-1"></i>{{ mode_labels[m] }}
</a>
{% endfor %}
</div>
</div>
</div>
<!-- LEVEL SELECTOR STRIP -->
<div class="level-strip">
<div class="container">
<div class="d-flex gap-2 flex-wrap">
{% for lvl in range(1, 7) %}
<a href="/learn/{{ lvl }}?mode={{ mode }}"
class="level-pill{% if lvl == level %} active{% endif %}">
HSK {{ lvl }}
</a>
{% endfor %}
</div>
</div>
</div>
<!-- CONTENT AREA -->
<div class="container py-4" id="learnContainer">
<!-- Loading state -->
<div id="loadingPanel" class="text-center py-5">
<div class="learn-spinner"></div>
<p class="text-muted mt-3">Loading {{ mode }} data…</p>
</div>
<!-- ══════════════ FLASHCARD PANEL ══════════════ -->
<div id="flashcardPanel" class="mode-panel d-none">
<!-- Progress bar row -->
<div class="fc-progress-wrap mb-4">
<div class="d-flex justify-content-between align-items-center mb-1">
<span class="small text-muted">
Card <span id="fcCurrent">1</span> of <span id="fcTotal">0</span>
</span>
<button class="btn btn-sm btn-outline-secondary" id="fcShuffle" type="button">
<i class="bi bi-shuffle"></i> Shuffle
</button>
</div>
<div class="progress" style="height:6px" role="progressbar"
aria-label="Flashcard progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" id="fcProgressBar" style="width:0%"></div>
</div>
</div>
<!-- Card flip scene -->
<div class="fc-scene mx-auto mb-4" id="fcScene" tabindex="0" role="button"
aria-label="Click to flip card">
<div class="fc-card" id="fcCard">
<!-- Front face -->
<div class="fc-front">
<div class="fc-hanzi" id="fcHanziFront"></div>
<div class="fc-tap-hint">Tap to reveal</div>
</div>
<!-- Back face -->
<div class="fc-back">
<div class="fc-hanzi fc-hanzi--sm" id="fcHanziBack"></div>
<div class="fc-pinyin" id="fcPinyin"></div>
<div class="fc-divider"></div>
<div class="fc-meaning" id="fcEnglish"></div>
<div class="fc-meaning fc-meaning--vi" id="fcVietnamese"></div>
<div class="fc-example" id="fcExample"></div>
</div>
</div>
</div>
<!-- Nav controls -->
<div class="d-flex justify-content-center gap-3">
<button class="btn btn-outline-secondary btn-icon" id="fcPrev" type="button" aria-label="Previous card">
<i class="bi bi-arrow-left"></i>
</button>
<button class="btn btn-outline-secondary btn-icon" id="fcFlip" type="button" aria-label="Flip card">
<i class="bi bi-arrow-repeat"></i> Flip
</button>
<button class="btn btn-accent btn-icon" id="fcNext" type="button" aria-label="Next card">
<i class="bi bi-arrow-right"></i>
</button>
</div>
</div>
<!-- END flashcardPanel -->
<!-- ══════════════ QUIZ PANEL ══════════════ -->
<div id="quizPanel" class="mode-panel d-none">
<!-- Quiz in-progress -->
<div id="quizGame">
<div class="quiz-meta d-flex justify-content-between align-items-center mb-3">
<span class="small text-muted">
Q <span id="qNum">1</span> / <span id="qTotal">10</span>
</span>
<div class="quiz-timer" id="quizTimer" aria-live="polite">30</div>
<span class="badge bg-accent" id="qScore">Score: 0</span>
</div>
<div class="progress mb-4" style="height:4px" role="progressbar"
aria-label="Quiz progress" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar bg-accent" id="qProgress" style="width:0%"></div>
</div>
<!-- Question card -->
<div class="quiz-question-card mb-4">
<div class="quiz-hanzi" id="qHanzi"></div>
<div class="quiz-pinyin" id="qPinyin"></div>
</div>
<!-- Options grid -->
<div class="quiz-options" id="qOptions" role="group" aria-label="Answer options"></div>
</div>
<!-- Quiz result screen -->
<div id="quizResult" class="d-none text-center py-4">
<div class="result-emoji" id="resultEmoji" aria-hidden="true">&#x1F389;</div>
<h3 class="mt-3" id="resultTitle">Quiz Complete!</h3>
<div class="result-score-wrap my-4">
<div class="result-score" id="resultScore">0/10</div>
<div class="result-percent" id="resultPercent">0%</div>
</div>
<p class="text-muted mb-4" id="resultMsg"></p>
<div class="d-flex gap-3 justify-content-center flex-wrap">
<button class="btn btn-accent" id="quizRetry" type="button">
<i class="bi bi-arrow-clockwise me-1"></i>Try Again
</button>
<a href="/learn/{{ level }}?mode=flashcard" class="btn btn-outline-secondary">
<i class="bi bi-card-text me-1"></i>Flashcards
</a>
</div>
</div>
</div>
<!-- END quizPanel -->
<!-- ══════════════ FILL BLANK PANEL ══════════════ -->
<div id="fillblankPanel" class="mode-panel d-none">
<div class="fb-progress d-flex justify-content-between align-items-center mb-4">
<span class="small text-muted">
Exercise <span id="fbCurrent">1</span> / <span id="fbTotal">0</span>
</span>
<span class="badge bg-accent">
Score: <span id="fbScore">0</span>
</span>
</div>
<div class="fb-exercise-card mb-4" id="fbCard">
<p class="fb-context text-muted" id="fbContext"></p>
<div class="fb-sentence" id="fbSentence"></div>
<p class="fb-hint" id="fbHint"></p>
</div>
<div class="d-flex gap-2 mb-3" style="max-width:480px">
<input
type="text"
id="fbInput"
class="form-control fb-input"
placeholder="Type the missing word…"
autocomplete="off"
spellcheck="false"
/>
<button class="btn btn-accent" id="fbSubmit" type="button">Check</button>
</div>
<div id="fbFeedback" class="fb-feedback d-none" aria-live="polite"></div>
<div id="fbTranslation" class="fb-translation d-none"></div>
<div class="d-flex justify-content-between mt-4" style="max-width:480px">
<button class="btn btn-outline-secondary" id="fbPrev" type="button">
<i class="bi bi-arrow-left me-1"></i>Prev
</button>
<button class="btn btn-outline-secondary" id="fbNext" type="button">
Next<i class="bi bi-arrow-right ms-1"></i>
</button>
</div>
</div>
<!-- END fillblankPanel -->
<!-- ══════════════ LEADERBOARD PANEL ══════════════ -->
<div id="leaderboardPanel" class="mode-panel d-none">
<div class="row g-4">
<div class="col-lg-6">
<div class="lb-card">
<h5 class="lb-card-title">
<i class="bi bi-trophy-fill text-warning me-2"></i>Top Scores
</h5>
<div id="topScoresList" class="lb-list" aria-live="polite"></div>
</div>
</div>
<div class="col-lg-6">
<div class="lb-card">
<h5 class="lb-card-title">
<i class="bi bi-fire text-danger me-2"></i>Most Active
</h5>
<div id="mostActiveList" class="lb-list" aria-live="polite"></div>
</div>
</div>
</div>
<!-- My stats (shown when username is set) -->
<div class="lb-my-stats mt-4" id="myStats" style="display:none">
<h6 class="mb-3"><i class="bi bi-person-fill me-2"></i>Your Stats</h6>
<div class="row g-3" id="myStatsRow"></div>
</div>
</div>
<!-- END leaderboardPanel -->
</div>
<!-- END learnContainer -->
<!-- Config object for elearning.js β€” NO inline CSS variables here -->
<script>
window.LEARN_CONFIG = {
level: {{ level }},
mode: "{{ mode }}",
color: "{{ meta.color }}"
};
</script>
{% endblock %}
{% block extra_js %}
<script src="{{ url_for('static', filename='js/elearning.js') }}"></script>
{% endblock %}