Yōten: A social tracker for your language learning journey built on the atproto.
1package views
2
3import (
4 "yoten.app/internal/server/views/layouts"
5 "yoten.app/internal/server/views/partials"
6)
7
8templ StatsPage(params StatsPageParams) {
9 @layouts.Base(layouts.BaseParams{Title: "stats"}) {
10 @partials.Header(partials.HeaderProps{User: params.User})
11 <div class="container mx-auto max-w-6xl px-4 py-8">
12 <div class="flex flex-col gap-8">
13 <div class="flex flex-col md:flex-row md:items-center justify-between gap-4 md:gap-0">
14 <div>
15 <h1 class="text-3xl font-bold text-text">Learning Analytics</h1>
16 <p class="text-text-muted mt-1">Track your language learning progress and patterns</p>
17 </div>
18 </div>
19 <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
20 <div class="card text-center">
21 <p class="text-2xl font-bold">{ partials.FormatDuration(params.TotalStudyTime) }</p>
22 <div class="text-xs text-text-muted flex items-center justify-center gap-1 mt-1">
23 <i class="w-4 h-4" data-lucide="clock"></i>
24 <p class="text-xs">Total Study Time</p>
25 </div>
26 </div>
27 <div class="card text-center">
28 <p class="text-2xl font-bold">{ params.TotalStudySessions }</p>
29 <div class="text-xs text-text-muted flex items-center justify-center gap-1 mt-1">
30 <i class="w-4 h-4" data-lucide="target"></i>
31 <p class="text-xs">Total Sessions</p>
32 </div>
33 </div>
34 <div class="card text-center">
35 <p class="text-2xl font-bold">{ params.TotalActiveDays }</p>
36 <div class="text-xs text-text-muted flex items-center justify-center gap-1 mt-1">
37 <i class="w-4 h-4" data-lucide="calendar-check"></i>
38 <p class="text-xs">Active Days</p>
39 </div>
40 </div>
41 <div class="card text-center">
42 <p class="text-2xl font-bold">{ params.Streak }</p>
43 <div class="text-xs text-text-muted flex items-center justify-center gap-1 mt-1">
44 <i class="w-4 h-4" data-lucide="trending-up"></i>
45 <p class="text-xs">Streak</p>
46 </div>
47 </div>
48 </div>
49 <div>
50 @partials.NewHeatmap(params.HeatmapData)
51 </div>
52 <div x-data="{ tab: 'week' }" class="card">
53 <div class="flex border-b border-gray-200">
54 <button
55 id="week-period-button"
56 @click="tab = 'week'"
57 hx-get="/stats/time-per-graphs"
58 hx-include="#time-per-period"
59 hx-target="#time-per-graphs"
60 hx-indicator="#time-per-graphs-spinner"
61 hx-disabled-elt="#week-period-button,#month-period-button,#year-period-button,#all-period-button"
62 :class="{ 'border-primary': tab === 'week', 'border-transparent text-text-muted hover:text-text hover:border-gray-300': tab !== 'week' }"
63 class="py-2 px-4 font-medium border-b-2 cursor-pointer"
64 >
65 This Week
66 </button>
67 <button
68 id="month-period-button"
69 @click="tab = 'month'"
70 hx-get="/stats/time-per-graphs"
71 hx-include="#time-per-period"
72 hx-target="#time-per-graphs"
73 hx-indicator="#time-per-graphs-spinner"
74 hx-disabled-elt="#week-period-button,#month-period-button,#year-period-button,#all-period-button"
75 :class="{ 'border-primary': tab === 'month', 'border-transparent text-text-muted hover:text-text hover:border-gray-300': tab !== 'month' }"
76 class="py-2 px-4 font-medium border-b-2 cursor-pointer"
77 >
78 This Month
79 </button>
80 <button
81 id="year-period-button"
82 @click="tab = 'year'"
83 hx-get="/stats/time-per-graphs"
84 hx-include="#time-per-period"
85 hx-target="#time-per-graphs"
86 hx-indicator="#time-per-graphs-spinner"
87 hx-disabled-elt="#week-period-button,#month-period-button,#year-period-button,#all-period-button"
88 :class="{ 'border-primary': tab === 'year', 'border-transparent text-text-muted hover:text-text hover:border-gray-300': tab !== 'year' }"
89 class="py-2 px-4 font-medium border-b-2 cursor-pointer"
90 >
91 This Year
92 </button>
93 <button
94 id="all-period-button"
95 @click="tab = 'all'"
96 hx-get="/stats/time-per-graphs"
97 hx-include="#time-per-period"
98 hx-target="#time-per-graphs"
99 hx-indicator="#time-per-graphs-spinner"
100 hx-disabled-elt="#week-period-button,#month-period-button,#year-period-button,#all-period-button"
101 :class="{ 'border-primary': tab === 'all', 'border-transparent text-text-muted hover:text-text hover:border-gray-300': tab !== 'all' }"
102 class="py-2 px-4 font-medium border-b-2 cursor-pointer"
103 >
104 All Time
105 </button>
106 <div id="time-per-graphs-spinner" class="htmx-indicator ml-4">
107 <i data-lucide="loader-circle" class="w-6 h-6 animate-spin text-text-muted"></i>
108 </div>
109 </div>
110 <input type="hidden" id="time-per-period" name="period" x-bind:value="tab"/>
111 <div
112 id="time-per-graphs"
113 hx-get="/stats/time-per-graphs"
114 hx-trigger="load"
115 hx-target="this"
116 hx-include="#time-per-period"
117 hx-swap="innerHTML"
118 >
119 <div class="flex justify-center py-4">
120 <i data-lucide="loader-circle" class="w-6 h-6 animate-spin text-text-muted"></i>
121 </div>
122 </div>
123 </div>
124 <div class="grid grid-cols-1 lg:grid-cols-2 gap-8">
125 @partials.PieChart(partials.PieChartProps{
126 Percentage: params.InputOutputPercentage,
127 LabelA: "Input",
128 LabelB: "Output",
129 })
130 if len(params.LanguageSummaryChartSegments) > 1 {
131 @partials.DonutChart(partials.DonutChartProps{
132 Title: "Time per Language",
133 Segments: params.LanguageSummaryChartSegments,
134 })
135 }
136 </div>
137 </div>
138 </div>
139 }
140}