internal/web/templates/mr.html
166 lines · 9401 bytes
1{{define "mrrange"}}<code>{{if .SourcePath}}{{.SourcePath}}:{{end}}{{.SourceRef}}</code> into <code>{{.TargetRef}}</code>{{end}}
2{{define "title"}}!{{.MR.Number}} · {{.Repo.OwnerName}}/{{.Repo.Name}}{{end}}
3{{define "content"}}
4{{$base := printf "/%s/%s/mrs/%d" .Repo.OwnerName .Repo.Name .MR.Number}}
5<h1 class="issuetitle">{{.MR.Title}} <span class="issuenumber">!{{.MR.Number}}</span></h1>
6<p class="issuemeta">{{if .MR.Draft}}<span class="chip chip-neutral">draft</span> {{end}}<span class="chip chip-{{.MR.State}}">{{.MR.State}}</span>
7{{if and (eq .MR.State "merged") .MR.MergedAt}}
8 {{if .MR.MergedBy}}<a href="/{{.MR.MergedBy}}">{{.MR.MergedBy}}</a> merged{{else}}Merged{{end}}
9 {{template "mrrange" .MR}} on {{when .MR.MergedAt}}
10{{else if and (eq .MR.State "closed") .MR.ClosedAt}}
11 {{if .MR.ClosedBy}}<a href="/{{.MR.ClosedBy}}">{{.MR.ClosedBy}}</a> closed this{{else}}Closed{{end}}
12 without merging {{template "mrrange" .MR}} on {{when .MR.ClosedAt}}
13{{else if or (eq .MR.State "merged") (eq .MR.State "closed")}}
14 {{template "mrrange" .MR}}
15{{else}}
16 <a href="/{{.MR.Author}}">{{.MR.Author}}</a> wants to merge {{template "mrrange" .MR}}
17{{end}}</p>
18{{with field . "StackedOn"}}<p class="meta">Stacked on <a href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/mrs/{{.Number}}">!{{.Number}} {{.Title}}</a>: merges into its branch until that lands, then onto its target.</p>{{end}}
19{{with field . "Stacked"}}<p class="meta">Builds on this: {{range $i, $k := .}}{{if $i}}, {{end}}<a href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/mrs/{{$k.Number}}">!{{$k.Number}} {{$k.Title}}</a>{{end}}. Merging with squash or rebase is refused while they are open.</p>{{end}}
20
21{{if .Notice}}<p class="error" role="alert">{{.Notice}}</p>{{end}}
22
23<div class="withaside">
24<div class="mainside">
25
26<nav class="subtabs" aria-label="Merge request">
27 <a {{if eq .View "conversation"}}aria-current="page" {{end}}href="{{$base}}">Conversation{{if .Comments}} <i>{{len .Comments}}</i>{{end}}</a>
28 <a {{if eq .View "commits"}}aria-current="page" {{end}}href="{{$base}}?view=commits">Commits{{if .Commits}} <i>{{len .Commits}}</i>{{end}}</a>
29 <a {{if eq .View "diff"}}aria-current="page" {{end}}href="{{$base}}?view=diff">Files changed{{if .Stat.Files}} <i>{{.Stat.Files}}</i>{{end}}</a>
30</nav>
31
32{{if eq .View "conversation"}}
33{{if .CanEdit}}<details class="editbox"><summary>Edit</summary>
34<form method="post" action="{{$base}}/edit" class="commentform">
35<p><input type="text" name="title" aria-label="Title" value="{{.MR.Title}}" required></p>
36<p><textarea name="body" aria-label="Description" rows="8">{{.MR.Body}}</textarea></p>
37<p><button type="submit">Save</button></p>
38</form></details>{{end}}
39{{if .BodyHTML}}<article class="comment">
40 <header class="commenthead"><strong><a href="/{{.MR.Author}}">{{.MR.Author}}</a></strong></header>
41 <div class="rendered">{{.BodyHTML}}</div>
42</article>{{end}}
43{{range .Comments}}
44{{if eq .Kind "system"}}<div class="syscomment">{{.BodyHTML}} <span class="when">{{when .CreatedAt}}</span></div>
45{{else}}<article class="comment">
46 <header class="commenthead"><strong><a href="/{{.Author}}">{{.Author}}</a></strong> <span class="when">{{when .CreatedAt}}</span></header>
47 <div class="rendered">{{.BodyHTML}}</div>
48</article>{{end}}
49{{end}}
50{{if .DetachedThreads}}<h2>Threads on earlier revisions</h2>
51{{range .DetachedThreads}}{{template "thread" dict "T" . "Base" $base "Viewer" $.Viewer "Class" "stale"}}{{end}}{{end}}
52{{if .Viewer}}
53<form method="post" action="{{$base}}/comment" class="commentform">
54<p><textarea name="body" aria-label="Comment" rows="4" placeholder="Comment as {{.Viewer}}"></textarea></p>
55<p><button type="submit">Comment</button></p>
56</form>
57{{end}}
58
59{{else if eq .View "commits"}}
60{{if gt .CommitsTotal (len .Commits)}}<p class="meta">first {{len .Commits}} of {{.CommitsTotal}} commits; the rest are in the branch</p>{{end}}
61<ul class="loglist">
62{{range .Commits}}<li>
63 <div class="commitmain">
64 <p class="subject"><a href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/commit/{{.SHA}}">{{.Subject}}</a></p>
65 <p class="meta">{{template "authorname" dict "Name" .AuthorName "User" .AuthorUser "Email" ""}} · {{.Date}}</p>
66 </div>
67 <div class="commitside">
68 {{template "sigbadge" .Sig}}
69 <code><a href="/{{$.Repo.OwnerName}}/{{$.Repo.Name}}/commit/{{.SHA}}">{{.ShortSHA}}</a></code>
70 </div>
71</li>
72{{else}}<li class="empty">no commits on this merge request</li>
73{{end}}
74</ul>
75
76{{else}}
77<p class="diffstat">{{.Stat.Files}} file{{if ne .Stat.Files 1}}s{{end}} changed, <span class="add">+{{.Stat.Adds}}</span> <span class="del">−{{.Stat.Dels}}</span>{{if .DiffTruncated}} · shown up to 4 MiB; the counts and the last file are partial{{end}}</p>
78{{if .DiffTruncated}}<p class="error" role="alert">This diff is larger than 4 MiB and is cut off below. Fetch the branch to see all of it.</p>{{end}}
79{{template "difffiles" dict "Files" .DiffFiles "Base" $base "Viewer" .Viewer}}
80{{end}}
81
82</div>
83
84<aside class="aside">
85 {{if and .CanWrite (or (eq .MR.State "open") (eq .MR.State "source_gone"))}}
86 <div class="grp">
87 <h2>Review</h2>
88 <form method="post" action="{{$base}}/review" class="actions">
89 <button type="submit" name="verdict" value="approve">Approve</button>
90 <button type="submit" name="verdict" value="request-changes">Request changes</button>
91 </form>
92 </div>
93 <div class="grp">
94 <h2>Merge</h2>
95 {{if .Unresolved}}<p class="row none">{{.Unresolved}} unresolved thread{{if ne .Unresolved 1}}s{{end}}</p>{{end}}
96 <form method="post" action="{{$base}}/merge" class="actions">
97 <label class="none" for="strategy">Strategy</label>
98 <select id="strategy" name="strategy">
99 <option value="auto">Automatic</option>
100 <option value="ff">Fast-forward</option>
101 <option value="merge">Merge commit</option>
102 <option value="squash">Squash</option>
103 <option value="rebase">Rebase</option>
104 </select>
105 <button type="submit">Merge</button>
106 </form>
107 <form method="post" action="{{$base}}/close" class="actions">
108 <button type="submit">Close without merging</button>
109 </form>
110 <form method="post" action="{{$base}}/draft" class="actions">
111 {{if .MR.Draft}}<button type="submit">Ready for review</button>
112 {{else}}<input type="hidden" name="draft" value="on"><button type="submit">Convert to draft</button>{{end}}
113 </form>
114 </div>
115 {{end}}
116 {{if and .CanEdit (or (eq .MR.State "open") (eq .MR.State "source_gone"))}}
117 <div class="grp">
118 <h2>Target</h2>
119 <form method="post" action="{{$base}}/retarget" class="actions">
120 <label class="none" for="target">Branch</label>
121 <select id="target" name="target">
122 {{range .Branches}}<option value="{{.Name}}"{{if eq .Name $.MR.TargetRef}} selected{{end}}>{{.Name}}</option>{{end}}
123 </select>
124 <button type="submit">Retarget</button>
125 </form>
126 <p class="row none">Retargeting stales existing reviews.</p>
127 </div>
128 {{end}}
129 <div class="grp">
130 <h2>Reviewers</h2>
131 {{if .MR.ReviewRequests}}<p class="row">{{range .MR.ReviewRequests}}<a href="/{{.}}">{{.}}</a> {{end}}</p>
132 {{else}}<p class="none">Nobody asked yet</p>{{end}}
133 {{if and .CanWrite (or (eq .MR.State "open") (eq .MR.State "source_gone"))}}
134 <form method="post" action="{{$base}}/review-request" class="actions">
135 <input type="text" name="add" aria-label="Add reviewers" placeholder="add, space-separated">
136 <input type="text" name="remove" aria-label="Remove reviewers" placeholder="remove">
137 <button type="submit">Apply</button>
138 </form>
139 {{end}}
140 </div>
141 <div class="grp">
142 <h2>Reviews</h2>
143 {{range .Reviews}}<p class="row"><span class="dot {{if eq .Verdict "approve"}}ok{{else}}pend{{end}}"></span><a href="/{{.Reviewer}}">{{.Reviewer}}</a> {{.Verdict}}{{if .Stale}} <span class="chip chip-stale">stale</span>{{end}}{{if not .Counts}} <span class="chip chip-neutral" title="This reviewer has no write access, so the merge gates do not count it">advisory</span>{{end}}<span class="sub">{{when .CreatedAt}}</span></p>
144 {{else}}<p class="none">No reviews yet</p>{{end}}
145 {{if gt (len .Revisions) 1}}<p class="row none">{{len .Revisions}} revisions pushed. What changed between the last two:
146 <code>gitbay mr range-diff {{.Repo.OwnerName}}/{{.Repo.Name}} {{.MR.Number}}</code></p>{{end}}
147 </div>
148 <div class="grp">
149 <h2>Checks</h2>
150 {{if .Checks}}<p class="row"><span class="badge check-{{.Combined}}">{{.Combined}}</span></p>
151 {{range .Checks}}<p class="row"><span class="dot {{if eq .State "success"}}ok{{else if eq .State "failure"}}bad{{else}}pend{{end}}"></span>{{if .TargetURL}}<a href="{{.TargetURL}}" rel="nofollow">{{.Context}}</a>{{else}}{{.Context}}{{end}} <span class="none">{{.State}}</span><span class="sub">{{when .UpdatedAt}}{{if .Duration}} · {{.Duration}}{{end}}</span></p>{{end}}
152 {{else}}<p class="none">No checks reported</p>{{end}}
153 </div>
154 <div class="grp">
155 <h2>Source</h2>
156 <p class="row"><code>{{if .MR.SourcePath}}{{.MR.SourcePath}}:{{end}}{{.MR.SourceRef}}</code></p>
157 <p class="row none">into <code>{{.MR.TargetRef}}</code> at <code>{{short .MR.HeadSHA}}</code></p>
158 </div>
159 {{if .MR.Milestone}}<div class="grp">
160 <h2>Milestone</h2>
161 <p class="row"><a href="/{{.Repo.OwnerName}}/{{.Repo.Name}}/milestones">{{.MR.Milestone}}</a></p>
162 </div>{{end}}
163</aside>
164</div>
165{{end}}
166