tenseleyflow/shithub / 6adccfc

Browse files

S21: issues/labels/milestones templates + CSS

Authored by mfwolffe <wolffemf@dukes.jmu.edu>
SHA
6adccfc8f6b4cd5202161a1682a6641e8c7b1444
Parents
e32287a
Tree
b45d985

6 changed files

StatusFile+-
M internal/web/static/css/shithub.css 88 0
A internal/web/templates/repo/issue_new.html 31 0
A internal/web/templates/repo/issue_view.html 148 0
A internal/web/templates/repo/issues_list.html 59 0
A internal/web/templates/repo/labels.html 49 0
A internal/web/templates/repo/milestones.html 52 0
internal/web/static/css/shithub.cssmodified
@@ -1175,3 +1175,91 @@ code {
1175
 .shithub-settings-branches form label { display: block; margin: 0.5rem 0; }
1175
 .shithub-settings-branches form label { display: block; margin: 0.5rem 0; }
1176
 .shithub-settings-branches form input[type=text],
1176
 .shithub-settings-branches form input[type=text],
1177
 .shithub-settings-branches form select { font: inherit; padding: 0.4rem 0.6rem; border: 1px solid var(--border-default); border-radius: 6px; min-width: 280px; }
1177
 .shithub-settings-branches form select { font: inherit; padding: 0.4rem 0.6rem; border: 1px solid var(--border-default); border-radius: 6px; min-width: 280px; }
1178
+
1179
+/* ========== Issues / Labels / Milestones (S21) ========== */
1180
+.shithub-issues, .shithub-issue-view, .shithub-issue-new, .shithub-labels, .shithub-milestones {
1181
+  max-width: 64rem;
1182
+  margin: 1.5rem auto;
1183
+  padding: 0 1rem;
1184
+}
1185
+.shithub-issues-head { display: flex; justify-content: space-between; align-items: center; gap: 1rem; flex-wrap: wrap; }
1186
+.shithub-issues-actions { display: flex; gap: 0.4rem; }
1187
+.shithub-issues-filter { display: flex; gap: 1.5rem; padding: 0.75rem 0; border-bottom: 1px solid var(--border-default); margin: 1rem 0; }
1188
+.shithub-issues-tab { color: var(--fg-muted); padding: 0.25rem 0; border-bottom: 2px solid transparent; }
1189
+.shithub-issues-tab-active { color: var(--fg-default); border-bottom-color: var(--accent-emphasis, #0969da); font-weight: 600; }
1190
+.shithub-issues-dot { display: inline-block; width: 0.6rem; height: 0.6rem; border-radius: 50%; vertical-align: middle; margin-right: 0.25rem; }
1191
+.shithub-issues-dot-open { background: #1a7f37; }
1192
+.shithub-issues-dot-closed { background: #8250df; }
1193
+.shithub-issues-list { list-style: none; padding: 0; margin: 0; }
1194
+.shithub-issues-row {
1195
+  display: flex; gap: 0.75rem; align-items: flex-start;
1196
+  padding: 0.75rem 0.5rem;
1197
+  border-bottom: 1px solid var(--border-default);
1198
+}
1199
+.shithub-issues-state { font-size: 1.1rem; line-height: 1.2; }
1200
+.shithub-issues-state-open { color: #1a7f37; }
1201
+.shithub-issues-state-closed { color: #8250df; }
1202
+.shithub-issues-body { flex: 1; }
1203
+.shithub-issues-title { font-weight: 600; color: var(--fg-default); }
1204
+.shithub-issues-title:hover { color: var(--accent-emphasis, #0969da); }
1205
+.shithub-issues-meta { color: var(--fg-muted); font-size: 0.85rem; margin-top: 0.2rem; }
1206
+.shithub-issues-assignees { font-size: 0.85rem; }
1207
+.shithub-issues-empty { color: var(--fg-muted); padding: 2rem; text-align: center; border: 1px dashed var(--border-default); border-radius: 6px; }
1208
+.shithub-issue-num { color: var(--fg-muted); font-weight: 400; margin-left: 0.5rem; }
1209
+.shithub-issue-title { display: flex; gap: 0.5rem; align-items: baseline; flex-wrap: wrap; }
1210
+.shithub-issue-meta { color: var(--fg-muted); margin: 0.5rem 0 1rem; display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
1211
+.shithub-issue-grid { display: grid; grid-template-columns: 1fr 16rem; gap: 1.5rem; }
1212
+@media (max-width: 768px) { .shithub-issue-grid { grid-template-columns: 1fr; } }
1213
+.shithub-comment {
1214
+  border: 1px solid var(--border-default);
1215
+  border-radius: 6px;
1216
+  margin-bottom: 1rem;
1217
+  background: var(--canvas-default);
1218
+}
1219
+.shithub-comment-head {
1220
+  padding: 0.5rem 0.75rem;
1221
+  background: var(--canvas-subtle);
1222
+  border-bottom: 1px solid var(--border-default);
1223
+  font-size: 0.9rem;
1224
+  color: var(--fg-muted);
1225
+}
1226
+.shithub-comment-body { padding: 0.75rem; }
1227
+.shithub-event { color: var(--fg-muted); font-size: 0.85rem; padding: 0.4rem 0.75rem; border-left: 2px solid var(--border-default); margin-left: 0.75rem; }
1228
+.shithub-event-kind { text-transform: lowercase; }
1229
+.shithub-comment-form { display: flex; flex-direction: column; gap: 0.5rem; margin-top: 1rem; }
1230
+.shithub-comment-form textarea, .shithub-issue-form textarea, .shithub-issue-form input[type=text] {
1231
+  font: inherit; padding: 0.5rem; border: 1px solid var(--border-default); border-radius: 6px; width: 100%;
1232
+  resize: vertical;
1233
+}
1234
+.shithub-issue-form { display: flex; flex-direction: column; gap: 1rem; max-width: 48rem; }
1235
+.shithub-form-row { display: flex; flex-direction: column; gap: 0.25rem; }
1236
+.shithub-form-row span { font-weight: 600; font-size: 0.9rem; }
1237
+.shithub-form-actions { display: flex; gap: 0.5rem; justify-content: flex-end; }
1238
+.shithub-issue-sidebar section { padding: 0.75rem 0; border-bottom: 1px solid var(--border-default); }
1239
+.shithub-issue-sidebar h3 { font-size: 0.85rem; text-transform: uppercase; letter-spacing: 0.05em; color: var(--fg-muted); margin: 0 0 0.5rem; }
1240
+.shithub-issue-signedout { color: var(--fg-muted); padding: 1rem; text-align: center; border: 1px dashed var(--border-default); border-radius: 6px; }
1241
+.shithub-label {
1242
+  display: inline-block;
1243
+  font-size: 0.75rem;
1244
+  padding: 0.1rem 0.5rem;
1245
+  border-radius: 1rem;
1246
+  color: #1f2328;
1247
+  font-weight: 500;
1248
+  margin: 0 0.15rem;
1249
+}
1250
+.shithub-label-pick { display: flex; gap: 0.4rem; align-items: center; padding: 0.2rem 0; }
1251
+.shithub-assignee-form { display: flex; gap: 0.25rem; flex-wrap: wrap; margin-top: 0.5rem; }
1252
+.shithub-assignee-form input[type=text] { padding: 0.3rem 0.5rem; border: 1px solid var(--border-default); border-radius: 6px; }
1253
+.shithub-labels-list, .shithub-milestones-list { list-style: none; padding: 0; }
1254
+.shithub-labels-row, .shithub-milestones-row {
1255
+  padding: 0.75rem 0;
1256
+  border-bottom: 1px solid var(--border-default);
1257
+  display: flex; gap: 0.75rem; align-items: center; flex-wrap: wrap;
1258
+}
1259
+.shithub-label-form, .shithub-milestone-form { display: flex; flex-wrap: wrap; gap: 0.4rem; padding: 0.5rem 0; }
1260
+.shithub-label-form input, .shithub-milestone-form input, .shithub-milestone-form textarea {
1261
+  padding: 0.4rem 0.6rem; border: 1px solid var(--border-default); border-radius: 6px; font: inherit;
1262
+}
1263
+.shithub-button-danger { color: #cf222e; }
1264
+.shithub-error { padding: 0.75rem; background: #ffebe9; border: 1px solid #ffcecb; border-radius: 6px; color: #82061e; margin-bottom: 1rem; }
1265
+.shithub-muted { color: var(--fg-muted); }
internal/web/templates/repo/issue_new.htmladded
@@ -0,0 +1,31 @@
1
+{{ define "page" -}}
2
+<section class="shithub-issue-new">
3
+  <header class="shithub-issues-head">
4
+    <h1>
5
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
+      <span class="shithub-code-sep">/</span>
7
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues">Issues</a>
8
+      <span class="shithub-code-sep">/</span>
9
+      New
10
+    </h1>
11
+  </header>
12
+
13
+  {{ if .Error }}<div class="shithub-error" role="alert">{{ .Error }}</div>{{ end }}
14
+
15
+  <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/issues" class="shithub-issue-form">
16
+    <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
17
+    <label class="shithub-form-row">
18
+      <span>Title</span>
19
+      <input type="text" name="title" maxlength="256" required value="{{ .FormTitle }}" autofocus>
20
+    </label>
21
+    <label class="shithub-form-row">
22
+      <span>Body (Markdown)</span>
23
+      <textarea name="body" rows="14" maxlength="65535">{{ .FormBody }}</textarea>
24
+    </label>
25
+    <div class="shithub-form-actions">
26
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues" class="shithub-button">Cancel</a>
27
+      <button type="submit" class="shithub-button shithub-button-primary">Submit new issue</button>
28
+    </div>
29
+  </form>
30
+</section>
31
+{{- end }}
internal/web/templates/repo/issue_view.htmladded
@@ -0,0 +1,148 @@
1
+{{ define "page" -}}
2
+<section class="shithub-issue-view">
3
+  <header class="shithub-issue-view-head">
4
+    <h1 class="shithub-issue-title">
5
+      <span>{{ .Issue.Title }}</span>
6
+      <span class="shithub-issue-num">#{{ .Issue.Number }}</span>
7
+    </h1>
8
+    <div class="shithub-issue-meta">
9
+      <span class="shithub-pill shithub-issues-state-{{ printf "%s" .Issue.State }}">
10
+        {{ if eq (printf "%s" .Issue.State) "open" }}● Open{{ else }}✓ Closed{{ end }}
11
+      </span>
12
+      {{ if .AuthorName }}<a href="/{{ .AuthorName }}">{{ .AuthorName }}</a>{{ end }}
13
+      opened this issue
14
+      <time datetime="{{ .Issue.CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .Issue.CreatedAt.Time }}</time>
15
+      · {{ len .Comments }} comment{{ if ne (len .Comments) 1 }}s{{ end }}
16
+      {{ if .Issue.Locked }}<span class="shithub-pill">locked</span>{{ end }}
17
+    </div>
18
+  </header>
19
+
20
+  <div class="shithub-issue-grid">
21
+    <article class="shithub-issue-thread">
22
+      <div class="shithub-comment">
23
+        <div class="shithub-comment-head">
24
+          {{ if .AuthorName }}<a href="/{{ .AuthorName }}">{{ .AuthorName }}</a>{{ end }}
25
+          <time datetime="{{ .Issue.CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .Issue.CreatedAt.Time }}</time>
26
+        </div>
27
+        <div class="shithub-comment-body markdown-body">
28
+          {{ if .Issue.BodyHtmlCached.Valid }}{{ safeHTML .Issue.BodyHtmlCached.String }}{{ else }}<p>{{ .Issue.Body }}</p>{{ end }}
29
+        </div>
30
+      </div>
31
+
32
+      {{ range .Comments }}
33
+      <div class="shithub-comment">
34
+        <div class="shithub-comment-head">
35
+          {{ if .AuthorName }}<a href="/{{ .AuthorName }}">{{ .AuthorName }}</a>{{ end }}
36
+          commented
37
+          <time datetime="{{ .C.CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .C.CreatedAt.Time }}</time>
38
+        </div>
39
+        <div class="shithub-comment-body markdown-body">
40
+          {{ if .C.BodyHtmlCached.Valid }}{{ safeHTML .C.BodyHtmlCached.String }}{{ else }}<p>{{ .C.Body }}</p>{{ end }}
41
+        </div>
42
+      </div>
43
+      {{ end }}
44
+
45
+      {{ range .Events }}
46
+      <div class="shithub-event">
47
+        <span class="shithub-event-kind">{{ .Kind }}</span>
48
+        <time datetime="{{ .CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .CreatedAt.Time }}</time>
49
+      </div>
50
+      {{ end }}
51
+
52
+      {{ if .Viewer.ID }}
53
+      <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/comments" class="shithub-comment-form">
54
+        <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
55
+        <label>
56
+          <span>Add a comment</span>
57
+          <textarea name="body" rows="6" maxlength="65535" required></textarea>
58
+        </label>
59
+        <div class="shithub-form-actions">
60
+          {{ if eq (printf "%s" .Issue.State) "open" }}
61
+          <button type="submit" formaction="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/state" name="state" value="closed" class="shithub-button">Close issue</button>
62
+          {{ else }}
63
+          <button type="submit" formaction="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/state" name="state" value="open" class="shithub-button">Reopen issue</button>
64
+          {{ end }}
65
+          <button type="submit" class="shithub-button shithub-button-primary">Comment</button>
66
+        </div>
67
+      </form>
68
+      {{ else }}
69
+      <p class="shithub-issue-signedout"><a href="/login">Sign in</a> to comment.</p>
70
+      {{ end }}
71
+    </article>
72
+
73
+    <aside class="shithub-issue-sidebar">
74
+      <section>
75
+        <h3>Labels</h3>
76
+        {{ if .Labels }}
77
+          {{ range .Labels }}<span class="shithub-label" style="background-color: #{{ .Color }}">{{ .Name }}</span>{{ end }}
78
+        {{ else }}<p class="shithub-muted">None yet</p>{{ end }}
79
+        {{ if .Viewer.ID }}
80
+        <details>
81
+          <summary>Edit labels</summary>
82
+          <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/labels">
83
+            <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
84
+            {{ $current := .Labels }}
85
+            {{ range .AllLabels }}
86
+              {{ $id := .ID }}
87
+              <label class="shithub-label-pick">
88
+                <input type="checkbox" name="label_ids" value="{{ .ID }}"
89
+                  {{ range $current }}{{ if eq .ID $id }}checked{{ end }}{{ end }}>
90
+                <span class="shithub-label" style="background-color: #{{ .Color }}">{{ .Name }}</span>
91
+              </label>
92
+            {{ end }}
93
+            <button type="submit" class="shithub-button">Apply</button>
94
+          </form>
95
+        </details>
96
+        {{ end }}
97
+      </section>
98
+
99
+      <section>
100
+        <h3>Assignees</h3>
101
+        {{ if .Assignees }}
102
+          {{ range .Assignees }}<a href="/{{ .Username }}">@{{ .Username }}</a>{{ end }}
103
+        {{ else }}<p class="shithub-muted">No one assigned</p>{{ end }}
104
+        {{ if .Viewer.ID }}
105
+        <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/assignees" class="shithub-assignee-form">
106
+          <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
107
+          <input type="text" name="username" placeholder="username" required>
108
+          <button type="submit" name="mode" value="add" class="shithub-button">Add</button>
109
+          <button type="submit" name="mode" value="remove" class="shithub-button">Remove</button>
110
+        </form>
111
+        {{ end }}
112
+      </section>
113
+
114
+      <section>
115
+        <h3>Milestone</h3>
116
+        {{ if .Issue.MilestoneID.Valid }}
117
+          {{ $mid := .Issue.MilestoneID.Int64 }}
118
+          {{ range .Milestones }}{{ if eq .ID $mid }}<a href="/{{ $.Owner }}/{{ $.Repo.Name }}/milestones">{{ .Title }}</a>{{ end }}{{ end }}
119
+        {{ else }}<p class="shithub-muted">No milestone</p>{{ end }}
120
+        {{ if .Viewer.ID }}
121
+        <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/milestone">
122
+          <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
123
+          <select name="milestone_id">
124
+            <option value="0">— None —</option>
125
+            {{ range .Milestones }}<option value="{{ .ID }}">{{ .Title }}</option>{{ end }}
126
+          </select>
127
+          <button type="submit" class="shithub-button">Set</button>
128
+        </form>
129
+        {{ end }}
130
+      </section>
131
+
132
+      {{ if .Viewer.ID }}
133
+      <section>
134
+        <h3>Lock</h3>
135
+        <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/issues/{{ .Issue.Number }}/lock">
136
+          <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
137
+          {{ if .Issue.Locked }}
138
+          <button type="submit" name="lock" value="false" class="shithub-button">Unlock</button>
139
+          {{ else }}
140
+          <button type="submit" name="lock" value="true" class="shithub-button">Lock conversation</button>
141
+          {{ end }}
142
+        </form>
143
+      </section>
144
+      {{ end }}
145
+    </aside>
146
+  </div>
147
+</section>
148
+{{- end }}
internal/web/templates/repo/issues_list.htmladded
@@ -0,0 +1,59 @@
1
+{{ define "page" -}}
2
+<section class="shithub-issues">
3
+  <header class="shithub-issues-head">
4
+    <h1>
5
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
+      <span class="shithub-code-sep">/</span>
7
+      Issues
8
+    </h1>
9
+    <div class="shithub-issues-actions">
10
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/labels" class="shithub-button">Labels</a>
11
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/milestones" class="shithub-button">Milestones</a>
12
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues/new" class="shithub-button shithub-button-primary">New issue</a>
13
+    </div>
14
+  </header>
15
+
16
+  <nav class="shithub-issues-filter">
17
+    <a href="?state=open" class="shithub-issues-tab {{ if eq .State "open" }}shithub-issues-tab-active{{ end }}">
18
+      <span class="shithub-issues-dot shithub-issues-dot-open"></span>
19
+      {{ .OpenCount }} Open
20
+    </a>
21
+    <a href="?state=closed" class="shithub-issues-tab {{ if eq .State "closed" }}shithub-issues-tab-active{{ end }}">
22
+      <span class="shithub-issues-dot shithub-issues-dot-closed"></span>
23
+      {{ .ClosedCount }} Closed
24
+    </a>
25
+  </nav>
26
+
27
+  {{ if .Items }}
28
+  <ul class="shithub-issues-list">
29
+    {{ range .Items }}
30
+    <li class="shithub-issues-row">
31
+      <span class="shithub-issues-state shithub-issues-state-{{ printf "%s" .Issue.State }}">
32
+        {{ if eq (printf "%s" .Issue.State) "open" }}●{{ else }}✓{{ end }}
33
+      </span>
34
+      <div class="shithub-issues-body">
35
+        <a class="shithub-issues-title" href="/{{ $.Owner }}/{{ $.Repo.Name }}/issues/{{ .Issue.Number }}">
36
+          {{ .Issue.Title }}
37
+        </a>
38
+        {{ range .Labels }}
39
+          <span class="shithub-label" style="background-color: #{{ .Color }}">{{ .Name }}</span>
40
+        {{ end }}
41
+        <div class="shithub-issues-meta">
42
+          #{{ .Issue.Number }} opened
43
+          <time datetime="{{ .Issue.CreatedAt.Time.Format "2006-01-02T15:04:05Z" }}">{{ relativeTime .Issue.CreatedAt.Time }}</time>
44
+          {{ if .AuthorName }}by <a href="/{{ .AuthorName }}">{{ .AuthorName }}</a>{{ end }}
45
+        </div>
46
+      </div>
47
+      {{ if .Assignees }}
48
+      <div class="shithub-issues-assignees">
49
+        {{ range .Assignees }}<a href="/{{ .Username }}" title="{{ .Username }}">@{{ .Username }}</a>{{ end }}
50
+      </div>
51
+      {{ end }}
52
+    </li>
53
+    {{ end }}
54
+  </ul>
55
+  {{ else }}
56
+  <p class="shithub-issues-empty">No {{ .State }} issues. <a href="/{{ .Owner }}/{{ .Repo.Name }}/issues/new">Open one</a>.</p>
57
+  {{ end }}
58
+</section>
59
+{{- end }}
internal/web/templates/repo/labels.htmladded
@@ -0,0 +1,49 @@
1
+{{ define "page" -}}
2
+<section class="shithub-labels">
3
+  <header class="shithub-issues-head">
4
+    <h1>
5
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
+      <span class="shithub-code-sep">/</span>
7
+      Labels
8
+    </h1>
9
+  </header>
10
+
11
+  {{ if .Viewer.ID }}
12
+  <details class="shithub-label-create">
13
+    <summary class="shithub-button shithub-button-primary">New label</summary>
14
+    <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/labels" class="shithub-label-form">
15
+      <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
16
+      <input type="text" name="name" placeholder="name" maxlength="50" required>
17
+      <input type="text" name="color" placeholder="color (6 hex)" maxlength="7" value="d0d7de">
18
+      <input type="text" name="description" placeholder="description (optional)">
19
+      <button type="submit" class="shithub-button">Create</button>
20
+    </form>
21
+  </details>
22
+  {{ end }}
23
+
24
+  <ul class="shithub-labels-list">
25
+    {{ range .Labels }}
26
+    <li class="shithub-labels-row">
27
+      <span class="shithub-label" style="background-color: #{{ .Color }}">{{ .Name }}</span>
28
+      {{ if .Description }}<span class="shithub-muted">{{ .Description }}</span>{{ end }}
29
+      {{ if $.Viewer.ID }}
30
+      <details class="shithub-label-edit">
31
+        <summary class="shithub-button">Edit</summary>
32
+        <form method="post" action="/{{ $.Owner }}/{{ $.Repo.Name }}/labels/{{ .ID }}/update" class="shithub-label-form">
33
+          <input type="hidden" name="csrf_token" value="{{ $.CSRFToken }}">
34
+          <input type="text" name="name" value="{{ .Name }}" required>
35
+          <input type="text" name="color" value="{{ .Color }}" required>
36
+          <input type="text" name="description" value="{{ .Description }}">
37
+          <button type="submit" class="shithub-button">Save</button>
38
+        </form>
39
+        <form method="post" action="/{{ $.Owner }}/{{ $.Repo.Name }}/labels/{{ .ID }}/delete">
40
+          <input type="hidden" name="csrf_token" value="{{ $.CSRFToken }}">
41
+          <button type="submit" class="shithub-button shithub-button-danger">Delete</button>
42
+        </form>
43
+      </details>
44
+      {{ end }}
45
+    </li>
46
+    {{ end }}
47
+  </ul>
48
+</section>
49
+{{- end }}
internal/web/templates/repo/milestones.htmladded
@@ -0,0 +1,52 @@
1
+{{ define "page" -}}
2
+<section class="shithub-milestones">
3
+  <header class="shithub-issues-head">
4
+    <h1>
5
+      <a href="/{{ .Owner }}/{{ .Repo.Name }}">{{ .Owner }}/{{ .Repo.Name }}</a>
6
+      <span class="shithub-code-sep">/</span>
7
+      Milestones
8
+    </h1>
9
+  </header>
10
+
11
+  {{ if .Viewer.ID }}
12
+  <details class="shithub-milestone-create">
13
+    <summary class="shithub-button shithub-button-primary">New milestone</summary>
14
+    <form method="post" action="/{{ .Owner }}/{{ .Repo.Name }}/milestones" class="shithub-milestone-form">
15
+      <input type="hidden" name="csrf_token" value="{{ .CSRFToken }}">
16
+      <input type="text" name="title" placeholder="title" maxlength="200" required>
17
+      <input type="date" name="due_on">
18
+      <textarea name="description" placeholder="description (optional)" rows="3"></textarea>
19
+      <button type="submit" class="shithub-button">Create</button>
20
+    </form>
21
+  </details>
22
+  {{ end }}
23
+
24
+  <ul class="shithub-milestones-list">
25
+    {{ range .Milestones }}
26
+    <li class="shithub-milestones-row">
27
+      <h3>{{ .Title }}
28
+        <span class="shithub-pill shithub-issues-state-{{ printf "%s" .State }}">{{ printf "%s" .State }}</span>
29
+        {{ if .DueOn.Valid }}<small>due {{ .DueOn.Time.Format "Jan 2, 2006" }}</small>{{ end }}
30
+      </h3>
31
+      {{ if .Description }}<p>{{ .Description }}</p>{{ end }}
32
+      {{ if $.Viewer.ID }}
33
+      <div class="shithub-milestone-actions">
34
+        <form method="post" action="/{{ $.Owner }}/{{ $.Repo.Name }}/milestones/{{ .ID }}/state">
35
+          <input type="hidden" name="csrf_token" value="{{ $.CSRFToken }}">
36
+          {{ if eq (printf "%s" .State) "open" }}
37
+          <button type="submit" name="state" value="closed" class="shithub-button">Close milestone</button>
38
+          {{ else }}
39
+          <button type="submit" name="state" value="open" class="shithub-button">Reopen milestone</button>
40
+          {{ end }}
41
+        </form>
42
+        <form method="post" action="/{{ $.Owner }}/{{ $.Repo.Name }}/milestones/{{ .ID }}/delete">
43
+          <input type="hidden" name="csrf_token" value="{{ $.CSRFToken }}">
44
+          <button type="submit" class="shithub-button shithub-button-danger">Delete</button>
45
+        </form>
46
+      </div>
47
+      {{ end }}
48
+    </li>
49
+    {{ end }}
50
+  </ul>
51
+</section>
52
+{{- end }}