stenodojo

stenodojo
git clone git@git.zachrice.app:repos/stenodojo.git
Log | Files | Refs

dashboard.html (8342B)


      1 {% extends "base.html" %}
      2 
      3 {% block title %}Dashboard - StenoDojo{% endblock %}
      4 
      5 {% block content %}
      6 <div class="dashboard">
      7     {% if not has_dicts %}
      8     <div class="dict-banner">
      9         <div class="dict-banner__icon">
     10             <svg viewBox="0 0 24 24" fill="none" width="28" height="28">
     11                 <rect x="1" y="3" width="9" height="11" rx="2.5" fill="currentColor" opacity="0.6"/>
     12                 <rect x="14" y="3" width="9" height="11" rx="2.5" fill="currentColor"/>
     13                 <rect x="5" y="16" width="14" height="6" rx="2.5" fill="currentColor" opacity="0.8"/>
     14             </svg>
     15         </div>
     16         <div class="dict-banner__text">
     17             <strong>No dictionaries loaded.</strong> Upload your Plover config in <a href="/settings">Settings</a> to get stroke hints and unlock all practice modes.
     18         </div>
     19     </div>
     20     {% endif %}
     21     <section class="hero-stats">
     22         <div class="stat-card stat-card--streak">
     23             <div class="stat-card__icon streak-fire"></div>
     24             <div class="stat-card__value">{{ streak }}</div>
     25             <div class="stat-card__label">Day Streak</div>
     26         </div>
     27         <div class="stat-card">
     28             <div class="stat-card__icon stat-icon--words"></div>
     29             <div class="stat-card__value">{{ today.words }}</div>
     30             <div class="stat-card__label">Words Today
     31                 {% if today.words > 0 %}
     32                 <span class="stat-card__sub">{{ today.accuracy | round(0) | int }}% accuracy</span>
     33                 {% endif %}
     34             </div>
     35         </div>
     36         <div class="stat-card">
     37             <div class="stat-card__icon stat-icon--mastered"></div>
     38             <div class="stat-card__value">{{ words_mastered }}</div>
     39             <div class="stat-card__label">Words Mastered</div>
     40         </div>
     41         <div class="stat-card">
     42             <div class="stat-card__icon stat-icon--review"></div>
     43             <div class="stat-card__value">{{ review_due }}</div>
     44             <div class="stat-card__label">Review Due
     45                 {% if review_due > 0 %}
     46                 <a href="/practice?mode=review" class="stat-card__action">Start Review</a>
     47                 {% endif %}
     48             </div>
     49         </div>
     50     </section>
     51 
     52     <section>
     53         <h2 class="section-title">Guided Lessons</h2>
     54         <div class="lesson-grid">
     55             {% for lesson in lessons %}
     56             <a href="/practice?mode=lesson&lesson={{ lesson.id }}" class="lesson-card lesson-card--{{ lesson.level }}">
     57                 <div class="lesson-card__order">{{ lesson.order }}</div>
     58                 <div class="lesson-card__info">
     59                     <div class="lesson-card__title">{{ lesson.title }}</div>
     60                     <div class="lesson-card__subtitle">{{ lesson.subtitle }}</div>
     61                     {% if lesson.progress %}
     62                     {% set prog = lesson.progress %}
     63                     {% set pct = (prog.mastered / prog.total_words * 100) | round(0) | int if prog.total_words > 0 else 0 %}
     64                     <div class="lesson-progress">
     65                         <div class="lesson-progress__bar">
     66                             <div class="lesson-progress__fill lesson-progress__fill--practiced" style="width: {{ (prog.practiced / prog.total_words * 100) | round(0) | int if prog.total_words > 0 else 0 }}%"></div>
     67                             <div class="lesson-progress__fill lesson-progress__fill--mastered" style="width: {{ pct }}%"></div>
     68                         </div>
     69                         <div class="lesson-progress__text">
     70                             <span>{{ prog.mastered }}/{{ prog.total_words }} mastered</span>
     71                             {% if prog.avg_wpm > 0 %}
     72                             <span>{{ prog.avg_wpm }} WPM</span>
     73                             {% endif %}
     74                         </div>
     75                     </div>
     76                     {% endif %}
     77                 </div>
     78                 <div class="lesson-card__level badge badge--{{ lesson.level }}">{{ lesson.level }}</div>
     79             </a>
     80             {% endfor %}
     81         </div>
     82     </section>
     83 
     84     <section class="quick-start">
     85         <h2 class="section-title">Free Practice</h2>
     86         <div class="mode-grid">
     87             <a href="/practice?mode=common" class="mode-card">
     88                 <div class="mode-card__icon mode-icon--common"></div>
     89                 <div class="mode-card__title">Common Words</div>
     90                 <div class="mode-card__desc">Most frequent words</div>
     91             </a>
     92             <a href="/practice?mode=speed" class="mode-card">
     93                 <div class="mode-card__icon mode-icon--speed"></div>
     94                 <div class="mode-card__title">Speed Drill</div>
     95                 <div class="mode-card__desc">Single-stroke words</div>
     96             </a>
     97             <a href="/practice?mode=phrases" class="mode-card">
     98                 <div class="mode-card__icon mode-icon--phrases"></div>
     99                 <div class="mode-card__title">Phrases</div>
    100                 <div class="mode-card__desc">Multi-word phrases</div>
    101             </a>
    102             <a href="/practice?mode=sentences" class="mode-card">
    103                 <div class="mode-card__icon mode-icon--sentences"></div>
    104                 <div class="mode-card__title">Sentences</div>
    105                 <div class="mode-card__desc">Full sentences</div>
    106             </a>
    107             <a href="/practice?mode=new" class="mode-card">
    108                 <div class="mode-card__icon mode-icon--new"></div>
    109                 <div class="mode-card__title">New Words</div>
    110                 <div class="mode-card__desc">Words you haven't seen</div>
    111             </a>
    112             <a href="/practice?mode=weak" class="mode-card">
    113                 <div class="mode-card__icon mode-icon--weak"></div>
    114                 <div class="mode-card__title">Weak Words</div>
    115                 <div class="mode-card__desc">Your trouble spots</div>
    116             </a>
    117             <a href="/practice?mode=review" class="mode-card">
    118                 <div class="mode-card__icon mode-icon--review"></div>
    119                 <div class="mode-card__title">Review</div>
    120                 <div class="mode-card__desc">Spaced repetition</div>
    121             </a>
    122             <a href="/practice?mode=random" class="mode-card">
    123                 <div class="mode-card__icon mode-icon--random"></div>
    124                 <div class="mode-card__title">Random</div>
    125                 <div class="mode-card__desc">Mix of all words</div>
    126             </a>
    127         </div>
    128     </section>
    129 
    130     <section class="today-progress">
    131         <h2 class="section-title">Today's Progress</h2>
    132         <div class="progress-container">
    133             <div class="progress-bar">
    134                 <div class="progress-bar__fill" style="width: {{ [today.words / 100 * 100, 100] | min }}%"></div>
    135             </div>
    136             <div class="progress-bar__label">{{ today.words }} / 100 words</div>
    137         </div>
    138     </section>
    139 
    140     <section class="recent-sessions">
    141         <h2 class="section-title">Recent Sessions</h2>
    142         {% if recent_sessions %}
    143         <div class="table-wrapper">
    144             <table class="table">
    145                 <thead>
    146                     <tr>
    147                         <th>Mode</th>
    148                         <th>Date</th>
    149                         <th>Words</th>
    150                         <th>Accuracy</th>
    151                         <th>WPM</th>
    152                     </tr>
    153                 </thead>
    154                 <tbody>
    155                     {% for session in recent_sessions[:5] %}
    156                     <tr>
    157                         <td><span class="badge badge--{{ session.mode }}">{{ session.mode }}</span></td>
    158                         <td>{{ session.date_str }}</td>
    159                         <td>{{ session.words_correct }} / {{ session.words_attempted }}</td>
    160                         <td class="accuracy-cell accuracy--{{ 'good' if session.accuracy >= 90 else ('mid' if session.accuracy >= 70 else 'low') }}">{{ session.accuracy | round(0) | int }}%</td>
    161                         <td>{{ session.wpm | round(1) }}</td>
    162                     </tr>
    163                     {% endfor %}
    164                 </tbody>
    165             </table>
    166         </div>
    167         {% else %}
    168         <div class="empty-state">
    169             <p>No sessions yet. Start practicing to see your progress here.</p>
    170         </div>
    171         {% endif %}
    172     </section>
    173 </div>
    174 {% endblock %}