Files
fit/internal/web/templates/partials.html
scott faf73c8be5 Track five things a day for seventy-five days
The workout, the protein, the calories, the water, and three lines of
gratitude. One page per day, a card of seventy-five tiles that fill by
how many of the five landed, and a missed day left as a gap rather than
a reset.

Single Go binary with no dependencies. The log is one JSON file on a
mounted volume, written atomically and never silently replaced when it
fails to parse, since it is the one thing here that cannot be recreated.
Auth is Traefik basic auth at the ingress; the app has no login of its
own, so every POST checks Sec-Fetch-Site to stop another origin posting
with those credentials.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-01 21:59:01 -07:00

108 lines
3.9 KiB
HTML

{{define "head"}}<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
<title>{{.Title}} · 75</title>
<meta name="color-scheme" content="light dark">
<meta name="theme-color" content="#E6EEEF" media="(prefers-color-scheme: light)">
<meta name="theme-color" content="#0B1E27" media="(prefers-color-scheme: dark)">
<meta name="robots" content="noindex, nofollow">
<link rel="manifest" href="/manifest.webmanifest">
<link rel="icon" href="{{asset "/static/icon.svg"}}" type="image/svg+xml">
<link rel="apple-touch-icon" href="{{asset "/static/icon.png"}}">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-title" content="75">
<link rel="stylesheet" href="{{asset "/static/app.css"}}">
</head>
<body>
{{end}}
{{define "foot"}}
<footer class="footer">
<a href="/">Today</a>
<a href="/settings">Settings</a>
</footer>
<script src="{{asset "/static/app.js"}}" defer></script>
</body>
</html>
{{end}}
{{/* The card: seventy-five tiles, each filling from the bottom by how many of
the five that day hit. Fifteen across so a phone gets five clean rows. */}}
{{define "card"}}
<section class="card">
<h2 class="card-heading">The card</h2>
<ol class="card-grid">
{{range .Card}}
{{if .IsFuture}}
<li class="tile tile-ahead" style="--score:0"><span class="tile-fill"></span></li>
{{else}}
<li class="tile{{if .IsToday}} tile-today{{end}}{{if .Marks.Complete}} tile-full{{end}}" style="--score:{{.Score}}" data-date="{{.Date}}">
<a href="/d/{{.Date}}" title="{{tileTitle .}}"><span class="tile-fill"></span><span class="sr-only">{{tileTitle .}}</span></a>
</li>
{{end}}
{{end}}
</ol>
<dl class="tally">
<div><dt>Perfect</dt><dd>{{.Stats.Complete}}</dd></div>
<div><dt>Streak</dt><dd>{{.Stats.Streak}}</dd></div>
<div><dt>Left</dt><dd>{{.Stats.Remaining}}</dd></div>
</dl>
<ul class="legend">
<li><span>Workout</span><b>{{index .Stats.Marks 0}}</b></li>
<li><span>Protein</span><b>{{index .Stats.Marks 1}}</b></li>
<li><span>Calories</span><b>{{index .Stats.Marks 2}}</b></li>
<li><span>Water</span><b>{{index .Stats.Marks 3}}</b></li>
<li><span>Gratitude</span><b>{{index .Stats.Marks 4}}</b></li>
</ul>
</section>
{{end}}
{{/* The settings fields, shared by first-run setup and the settings page. */}}
{{define "settings-fields"}}
<div class="field">
<label for="start_date">Day 1</label>
<input id="start_date" name="start_date" type="date" value="{{.Settings.StartDate}}" required>
</div>
<div class="field">
<label for="length">Days</label>
<input id="length" name="length" type="number" inputmode="numeric" min="1" max="3650" value="{{.Settings.Length}}" required>
</div>
<div class="field">
<label for="protein_target">Protein target</label>
<div class="field-row">
<input id="protein_target" name="protein_target" type="number" inputmode="numeric" min="1" max="1000" value="{{.Settings.ProteinTarget}}" required>
<span class="field-unit">grams or more</span>
</div>
</div>
<div class="field">
<label for="calorie_target">Calorie target</label>
<div class="field-row">
<input id="calorie_target" name="calorie_target" type="number" inputmode="numeric" min="1" max="20000" value="{{.Settings.CalorieTarget}}" required>
<select name="calorie_mode" aria-label="Calorie target direction">
<option value="under" {{if ne .Settings.CalorieMode "over"}}selected{{end}}>at most</option>
<option value="over" {{if eq .Settings.CalorieMode "over"}}selected{{end}}>at least</option>
</select>
</div>
</div>
<div class="field">
<label for="water_target">Water target</label>
<div class="field-row">
<input id="water_target" name="water_target" type="number" inputmode="numeric" min="1" max="1000" value="{{.Settings.WaterTarget}}" required>
<span class="field-unit">fluid ounces (a gallon is 128)</span>
</div>
</div>
{{end}}