commit fdf17c37d2365964c1ae296a6d972261d3ea77ef
parent cc1675f645e642171c4c016973165f426645c7a4
Author: breadcat <breadcat@users.noreply.github.com>
Date: Thu, 2 Jul 2026 15:23:45 +0100
Add summary feature
I'm not sure the calculations are great, mind
Diffstat:
3 files changed, 88 insertions(+), 0 deletions(-)
diff --git a/main.go b/main.go
@@ -130,6 +130,44 @@ func sortDayLogs() {
})
}
+// Summary
+
+func renderSummary() string {
+ now := time.Now()
+ today := now.Format("2006-01-02")
+
+ var totalUnitsVal float64
+ totalDrinks := 0
+ freeDays := 0
+
+ for i := 1; i <= 7; i++ {
+ date := now.AddDate(0, 0, -i).Format("2006-01-02")
+ dl := findDayLog(date)
+ if dl == nil || len(dl.Drinks) == 0 {
+ freeDays++
+ } else {
+ for _, d := range dl.Drinks {
+ totalDrinks += d.Count
+ totalUnitsVal += d.Units()
+ }
+ }
+ }
+
+ // Also include today if it has drinks
+ if dl := findDayLog(today); dl != nil {
+ for _, d := range dl.Drinks {
+ totalDrinks += d.Count
+ totalUnitsVal += d.Units()
+ }
+ }
+
+ return `<div class="summary-grid">` +
+ `<div class="summary-card"><span class="summary-val">` + strconv.Itoa(totalDrinks) + `</span><span class="summary-label">drinks</span></div>` +
+ `<div class="summary-card"><span class="summary-val">` + formatUnits(totalUnitsVal) + `</span><span class="summary-label">units</span></div>` +
+ `<div class="summary-card"><span class="summary-val">` + strconv.Itoa(freeDays) + `<span class="summary-val-sub">/7</span></span><span class="summary-label">drink-free days</span></div>` +
+ `</div>`
+}
+
// Tile rendering
func dateColorClass(units float64, date string) string {
@@ -331,6 +369,7 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
return
}
page := string(tmpl)
+ page = strings.ReplaceAll(page, "{{SUMMARY}}", renderSummary())
page = strings.ReplaceAll(page, "{{DAYS_TILES}}", renderDaysRow(0))
page = strings.ReplaceAll(page, "{{WEEK_TILES}}", renderWeekRow(0))
page = strings.ReplaceAll(page, "{{MONTH_GRID}}", renderMonthGrid(0))
@@ -340,6 +379,12 @@ func handleIndex(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, page)
}
+
+func handleSummary(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Content-Type", "text/html; charset=utf-8")
+ fmt.Fprint(w, renderSummary())
+}
+
func handleTilesDays(w http.ResponseWriter, r *http.Request) {
offset, _ := strconv.Atoi(r.URL.Query().Get("offset"))
w.Header().Set("Content-Type", "text/html; charset=utf-8")
@@ -486,6 +531,7 @@ func main() {
mux := http.NewServeMux()
mux.Handle("/static/", http.FileServer(http.FS(staticFiles)))
mux.HandleFunc("/", handleIndex)
+ mux.HandleFunc("/summary", handleSummary)
mux.HandleFunc("/tiles/days", handleTilesDays)
mux.HandleFunc("/tiles/week", handleTilesWeek)
mux.HandleFunc("/tiles/month", handleTilesMonth)
diff --git a/static/index.html b/static/index.html
@@ -8,6 +8,8 @@
</head>
<body>
+<div id="summary">{{SUMMARY}}</div>
+
<div class="legend">
<div class="legend-item"><div class="legend-dot" style="background:#1e1e2e;border:1px solid #333"></div>0</div>
<div class="legend-item"><div class="legend-dot" style="background:#1a4a7a"></div><2</div>
diff --git a/static/style.css b/static/style.css
@@ -305,3 +305,43 @@ details > .details-inner { padding: 0.75rem 1rem 1rem; overflow-x: auto; }
.legend { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 1.5rem; }
.legend-item { display: flex; align-items: center; gap: 5px; font-size: 0.72rem; color: var(--text-dim); }
.legend-dot { width: 12px; height: 12px; border-radius: 3px; flex-shrink: 0; }
+
+/* Summary */
+
+#summary { margin-bottom: 1.5rem; }
+
+.summary-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 10px;
+}
+
+.summary-card {
+ background: var(--surface);
+ border-radius: var(--radius);
+ padding: 0.9rem 0.75rem;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 4px;
+}
+
+.summary-val {
+ font-size: clamp(1.4rem, 5vw, 2rem);
+ font-weight: 700;
+ color: var(--accent);
+ line-height: 1;
+}
+
+.summary-val-sub {
+ font-size: 0.65em;
+ color: var(--text-dim);
+ font-weight: 400;
+}
+
+.summary-label {
+ font-size: 0.7rem;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-dim);
+}