apps/habbits/templates/index.html 5.0 K raw
1
<!doctype html>
2
<html lang="en">
3
  <head>
4
    <meta charset="UTF-8" />
5
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
    <meta name="theme-color" content="#121113" />
7
    <link rel="stylesheet" href="/assets/darkmatter.css" />
8
    <link rel="stylesheet" href="/static/styles.css" />
9
    <title>Habbits</title>
10
  </head>
11
  <body>
12
    <div class="header">
13
      <a href="/" class="logo">HABBITS</a>
14
      <nav class="links">
15
        <a href="/new">new habit</a>
16
        <a href="/settings">settings</a>
17
        <a href="/logout">logout</a>
18
      </nav>
19
    </div>
20
21
    {{if .Success}}<p class="success">{{.Success}}</p>{{end}}
22
    {{if .Error}}<p class="error">{{.Error}}</p>{{end}}
23
24
    {{if .Habits}}
25
    <section class="section">
26
      <h2>New record</h2>
27
      <form class="form" method="POST" action="/records">
28
        <input type="hidden" name="return_to" value="/" />
29
        <div class="form-row">
30
          <div class="form-field">
31
            <label for="habit">Habit</label>
32
            <select id="habit" name="habit" required>
33
              {{range .Habits}}<option value="{{.ShortID}}" data-type="{{.ValueType}}"{{if .Unit}} data-unit="{{.Unit}}"{{end}}>{{.Name}} ({{.ValueType}})</option>{{end}}
34
            </select>
35
          </div>
36
          <div class="form-field">
37
            <label for="value">Value</label>
38
            <div id="value-container"></div>
39
          </div>
40
          <div class="form-field">
41
            <label for="recorded_at">When</label>
42
            <input type="datetime-local" id="recorded_at" name="recorded_at" class="now-default" />
43
          </div>
44
        </div>
45
        <div class="form-actions">
46
          <button type="submit">Add record</button>
47
        </div>
48
      </form>
49
    </section>
50
    {{end}}
51
52
    <section class="section">
53
      <div class="admin-toolbar">
54
        <h2>Habits ({{len .Habits}})</h2>
55
      </div>
56
      {{if not .Habits}}
57
      <p class="empty">No habits yet. <a href="/new">Create one</a>.</p>
58
      {{else}}
59
      <ul class="admin-list">
60
        {{range .Habits}}
61
        <li class="admin-list-item">
62
          <div class="admin-list-info">
63
            <a class="admin-list-title" href="/habits/{{.ShortID}}">{{.Name}}</a>
64
            <span class="admin-list-meta">
65
              <span class="tag">{{.ValueType}}</span>
66
              {{if .Unit}}<span class="tag">{{.Unit}}</span>{{end}}
67
              <span class="admin-list-date">{{.RecordCount}} record{{if ne .RecordCount 1}}s{{end}}</span>
68
            </span>
69
          </div>
70
        </li>
71
        {{end}}
72
      </ul>
73
      {{end}}
74
    </section>
75
76
    <section class="section">
77
      <div class="admin-toolbar">
78
        <h2>Recent records</h2>
79
      </div>
80
      {{if not .Days}}
81
      <p class="empty">No records yet.</p>
82
      {{else}}
83
      {{range .Days}}
84
      <div class="day-group">
85
        <h3 class="day-heading">{{.Date}}</h3>
86
        <ul class="admin-list">
87
          {{range .Records}}
88
          <li class="admin-list-item">
89
            <div class="admin-list-info">
90
              <span class="admin-list-title">
91
                <a href="/habits/{{.HabitShortID}}">{{.HabitName}}</a>
92
                - {{.Value}}{{if .Unit}} {{.Unit}}{{end}}
93
              </span>
94
              <span class="admin-list-meta">
95
                <span class="admin-list-date">{{.TimeDisplay}}</span>
96
              </span>
97
            </div>
98
          </li>
99
          {{end}}
100
        </ul>
101
      </div>
102
      {{end}}
103
      {{end}}
104
    </section>
105
106
    <script>
107
      // Swap the record value widget to match the selected habit's type. The
108
      // server re-validates, so this is only a convenience.
109
      (function () {
110
        var sel = document.getElementById("habit");
111
        var container = document.getElementById("value-container");
112
        if (!sel || !container) return;
113
        function render() {
114
          var opt = sel.options[sel.selectedIndex];
115
          var type = opt ? opt.getAttribute("data-type") : "string";
116
          var html;
117
          if (type === "bool") {
118
            html = '<select id="value" name="value" required><option value="true">true</option><option value="false">false</option></select>';
119
          } else if (type === "int") {
120
            html = '<input type="number" step="1" id="value" name="value" required />';
121
          } else if (type === "float") {
122
            html = '<input type="number" step="any" id="value" name="value" required />';
123
          } else {
124
            html = '<input type="text" id="value" name="value" required />';
125
          }
126
          container.innerHTML = html;
127
        }
128
        sel.addEventListener("change", render);
129
        render();
130
      })();
131
132
      // Default any empty datetime-local input to the current local time.
133
      (function () {
134
        var now = new Date();
135
        now.setMinutes(now.getMinutes() - now.getTimezoneOffset());
136
        var stamp = now.toISOString().slice(0, 16);
137
        document.querySelectorAll("input.now-default").forEach(function (el) {
138
          if (!el.value) el.value = stamp;
139
        });
140
      })();
141
    </script>
142
  </body>
143
</html>