issues.templ RawHistoryFolder
1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3package web
4
5// IssueList is the list of the issues of a repository, open or closed.
6templ IssueList(l Layout, p IssueListPage) {
7 @Shell(l, issueListRail(p)) {
8 if p.Live {
9 @LiveSlot("mr-notice")
10 }
11 <nav class="since" aria-label="State">
12 <a
13 href={ templ.SafeURL(p.OpenHref) }
14 if !p.Closed {
15 aria-current="page"
16 }
17 >open</a>
18 <a
19 href={ templ.SafeURL(p.ClosedHref) }
20 if p.Closed {
21 aria-current="page"
22 }
23 >closed</a>
24 </nav>
25 <div class="filter">
26 <label for="issue-filter" class="sr">filter</label>
27 <input
28 id="issue-filter"
29 type="search"
30 data-filter=".mrs tbody tr"
31 placeholder="Filter by title, author or state…"
32 autocomplete="off"
33 hx-on:input="ermite.filter(this)"
34 />
35 </div>
36 <h2 class="rule-h">
37 if p.Closed {
38 <span>Closed</span>
39 } else {
40 <span>Open</span>
41 }
42 <span class="count">{ itoa(len(p.Rows)) }</span>
43 </h2>
44 @issueRows(p.Rows)
45 if len(p.Rows) == 0 {
46 if p.Closed {
47 <p class="lede dim">no closed issues.</p>
48 } else {
49 <p class="lede dim">no open issues.</p>
50 }
51 }
52 if p.Older != "" || p.Newest != "" {
53 <nav class="pager" aria-label="Pages">
54 if p.Newest != "" {
55 <a class="btn" href={ templ.SafeURL(p.Newest) }>← Newest</a>
56 } else {
57 <span class="btn" aria-disabled="true">← Newest</span>
58 }
59 if p.Older != "" {
60 <a class="btn" href={ templ.SafeURL(p.Older) } rel="next">Older →</a>
61 } else {
62 <span class="btn" aria-disabled="true">Older →</span>
63 }
64 </nav>
65 }
66 }
67}
68
69templ issueRows(rows []IssueRow) {
70 if len(rows) > 0 {
71 <table class="list mrs" aria-label="Issues">
72 <tbody data-rows>
73 for _, i := range rows {
74 <tr data-cursor?={ i.Cursor } data-state={ i.State }>
75 <td class="c-n mute">{ i.Number }</td>
76 <td class="c-state">{ i.State }</td>
77 <td class="c-subject"><a href={ templ.SafeURL(i.Href) }>{ i.Title }</a></td>
78 <td class="c-who dim">{ i.Author }</td>
79 <td class="c-when nowrap dim">
80 if i.WhenAt != "" {
81 <time datetime={ i.WhenAt } title={ i.WhenAt }>{ i.When }</time>
82 }
83 </td>
84 </tr>
85 }
86 </tbody>
87 </table>
88 }
89}
90
91templ issueListRail(p IssueListPage) {
92 <section aria-labelledby="r-states">
93 <h2 id="r-states">States</h2>
94 <dl class="kv">
95 <dt>open</dt>
96 <dd>being worked on</dd>
97 <dt>closed</dt>
98 <dd>done with, can be reopened</dd>
99 </dl>
100 </section>
101 if p.NewHref != "" {
102 <section aria-labelledby="r-new">
103 <h2 id="r-new">Open one</h2>
104 <a class="btn primary" href={ templ.SafeURL(p.NewHref) }>New issue</a>
105 </section>
106 }
107}
108
109// IssueNew is the form that opens an issue.
110templ IssueNew(l Layout, p IssueNewPage) {
111 @Shell(l, issueNewRail()) {
112 <header class="show mr-h">
113 <h1>New issue</h1>
114 </header>
115 <form class="cform create" method="post" action={ templ.SafeURL(p.Action) }>
116 <label for="issue-title">title</label>
117 <input id="issue-title" name="title" type="text" maxlength="120" required autocomplete="off" autofocus/>
118 <label for="issue-body">description</label>
119 <textarea id="issue-body" name="body" rows="8" maxlength="10000" placeholder="Markdown, optional"></textarea>
120 <div class="f-btns">
121 <button type="submit" class="btn primary">Create issue</button>
122 <a class="btn" href={ templ.SafeURL(p.Cancel) }>Cancel</a>
123 </div>
124 </form>
125 }
126}
127
128templ issueNewRail() {
129 <section aria-labelledby="r-about">
130 <h2 id="r-about">Issues</h2>
131 <p class="dim">issues share their numbers with the merge requests of the repository.</p>
132 </section>
133}
134
135// IssueShow is an issue: its facts, its body, its comments and, for the
136// owner, the form to comment and the button that closes or reopens it.
137templ IssueShow(l Layout, p IssuePage) {
138 @Shell(l, issueRail(p)) {
139 <section class="mr-card">
140 <header class="show mr-h">
141 <h1><span class="mute">{ p.Number }</span> { p.Title }</h1>
142 <dl class="kv show-kv">
143 <dt>state</dt>
144 <dd class="mr-state" data-state={ p.State }>{ p.State }</dd>
145 <dt>opened</dt>
146 <dd>
147 { p.Opened.Name }
148 if p.Opened.WhenAt != "" {
149 { " · " }<time datetime={ p.Opened.WhenAt } title={ p.Opened.Abs }>{ p.Opened.When }</time>
150 }
151 </dd>
152 if p.UpdatedAt != "" {
153 <dt>updated</dt>
154 <dd><time datetime={ p.UpdatedAt }>{ p.Updated }</time></dd>
155 }
156 </dl>
157 </header>
158 if p.Desc != "" {
159 <article class="mr-desc prose" aria-label="description" hx-ignore>
160 @templ.Raw(string(p.Desc))
161 </article>
162 }
163 </section>
164 if p.Live {
165 @LiveSlot("mr-notice")
166 }
167 <h2 class="rule-h" id="comments"><span>Comments</span><span class="count">{ itoa(len(p.Comments)) }</span></h2>
168 for _, c := range p.Comments {
169 @commentView(c, "")
170 }
171 if len(p.Comments) == 0 {
172 <p class="dim">no comments yet.</p>
173 }
174 if p.Form != nil {
175 @commentForm(*p.Form, "")
176 }
177 }
178}
179
180templ issueRail(p IssuePage) {
181 if p.Transition != nil {
182 <section aria-labelledby="r-act">
183 <h2 id="r-act">Issue</h2>
184 <form method="post" action={ templ.SafeURL(p.Transition.Action) }>
185 <button type="submit" class="btn">{ p.Transition.Label }</button>
186 </form>
187 </section>
188 }
189}