Yōten: A social tracker for your language learning journey built on the atproto.
at master 140 lines 5.6 kB view raw
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}