limoncello

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

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:
Mmain.go | 46++++++++++++++++++++++++++++++++++++++++++++++
Mstatic/index.html | 2++
Mstatic/style.css | 40++++++++++++++++++++++++++++++++++++++++
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>&lt;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); +}