rin
Public Open-source lightweight git hosting.
pull_detail.html · 343 lines · 15.9 KB
Raw
1 {{define "content"}}
2 <div class="container">
3 {{template "repo_header" .}}
4
5 <!-- PR Header -->
6 <div class="mb-4 pb-3" style="border-bottom:1px solid hsl(var(--border));">
7 <div class="flex items-start justify-between flex-wrap gap-3">
8 <div class="flex items-center gap-2 flex-wrap min-w-0">
9 <h1 class="text-lg font-bold" style="margin:0;">{{.PR.Title}}</h1>
10 <span class="font-mono text-base text-muted">#{{.PR.Number}}</span>
11 </div>
12
13 {{if .CanToggle}}
14 {{if ne .PR.Status "merged"}}
15 <form method="post" action="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}/state" style="margin:0;">
16 <input type="hidden" name="_csrf" value="{{.CSRFToken}}">
17 {{if eq .PR.Status "closed"}}
18 <input type="hidden" name="state" value="open">
19 <button type="submit" class="btn btn-outline btn-sm">
20 {{icon "git-pull-request" "size-3.5"}}
21 <span>{{t .Lang "pulls.reopen_btn"}}</span>
22 </button>
23 {{else}}
24 <input type="hidden" name="state" value="closed">
25 <button type="submit" class="btn btn-outline btn-sm">
26 {{icon "close" "size-3.5"}}
27 <span>{{t .Lang "pulls.close_btn"}}</span>
28 </button>
29 {{end}}
30 </form>
31 {{end}}
32 {{end}}
33 </div>
34
35 <div class="flex items-center gap-2 mt-2 text-xs text-muted flex-wrap">
36 {{if eq .PR.Status "merged"}}
37 <span class="badge font-medium flex items-center gap-1.5" style="background:hsl(var(--diff-add-bg));color:hsl(var(--diff-add-fg));border-color:hsl(var(--diff-add-fg)/0.35);">
38 {{icon "git-merge" "size-3.5"}} {{t .Lang "pulls.status.merged"}}
39 </span>
40 {{else if eq .PR.Status "closed"}}
41 <span class="badge badge-outline flex items-center gap-1.5">
42 {{icon "close" "size-3.5"}} {{t .Lang "pulls.status.closed"}}
43 </span>
44 {{else}}
45 <span class="badge badge-primary flex items-center gap-1.5">
46 {{icon "git-pull-request" "size-3.5"}} {{t .Lang "pulls.status.open"}}
47 </span>
48 {{end}}
49
50 <a href="/{{.PR.AuthorUsername}}" class="font-medium" style="color:hsl(var(--foreground));">@{{.PR.AuthorUsername}}</a>
51 <span>{{t .Lang "pulls.wants_to_merge"}}</span>
52 <span class="badge badge-outline font-mono text-xs">{{if .PR.HeadRepoID}}{{.PR.HeadRepoOwner}}/{{.PR.HeadRepoName}}:{{end}}{{.PR.HeadBranch}}</span>
53 <span>{{t .Lang "pulls.into"}}</span>
54 <span class="badge badge-outline font-mono text-xs">{{.PR.BaseBranch}}</span>
55 <span>·</span>
56 <span>{{timeAgo .PR.CreatedAt .Lang}}</span>
57 </div>
58
59 <!-- Sub-tabs: Conversation, Commits, Files Changed -->
60 <div class="flex items-center gap-2 mt-3 pt-2" style="border-top:1px solid hsl(var(--border));">
61 <a href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}?tab=conversation" class="btn {{if eq .SubTab "conversation"}}btn-secondary{{else}}btn-ghost{{end}} btn-sm">
62 {{icon "message" "size-3.5"}}
63 <span>{{t .Lang "pulls.conversation"}}</span>
64 </a>
65 <a href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}?tab=commits" class="btn {{if eq .SubTab "commits"}}btn-secondary{{else}}btn-ghost{{end}} btn-sm">
66 {{icon "git-commit" "size-3.5"}}
67 <span>{{t .Lang "pulls.commits"}} ({{len .CompareCommits}})</span>
68 </a>
69 <a href="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}?tab=files" class="btn {{if eq .SubTab "files"}}btn-secondary{{else}}btn-ghost{{end}} btn-sm">
70 {{icon "file-code" "size-3.5"}}
71 <span>{{t .Lang "pulls.files_changed"}} {{if .Compare}}({{.Compare.FilesChanged}}){{end}}</span>
72 </a>
73 </div>
74 </div>
75
76 {{if eq .SubTab "commits"}}
77 <!-- Commits list -->
78 <div class="card mb-4">
79 <div class="dense-list">
80 {{range .CompareCommits}}
81 <div class="dense-row" style="min-height:2.8rem;padding:0.5rem 0.85rem;">
82 <div class="flex items-center gap-2 min-w-0">
83 <span class="text-muted">{{icon "git-commit" "size-3.5"}}</span>
84 <a href="/{{$.Repo.OwnerUsername}}/{{$.Repo.Name}}/commit/{{.Hash}}" class="font-semibold text-sm truncate">{{.Subject}}</a>
85 </div>
86 <div class="flex items-center gap-2 text-xs text-muted flex-shrink-0 font-mono">
87 <span>{{.AuthorName}}</span>
88 <span class="badge badge-outline">{{.ShortHash}}</span>
89 </div>
90 </div>
91 {{else}}
92 <div class="card-body text-xs text-muted">{{t .Lang "pulls.no_commits"}}</div>
93 {{end}}
94 </div>
95 </div>
96
97 {{else if eq .SubTab "files"}}
98 <!-- Files changed diff viewer -->
99 {{if .Compare}}
100 <div class="flex items-center justify-between mb-3 text-xs text-muted font-mono">
101 <span><strong>{{.Compare.FilesChanged}}</strong> {{pluralT .Lang .Compare.FilesChanged "repo.files_changed"}}</span>
102 <div class="flex items-center gap-2">
103 <span style="color:hsl(var(--diff-add-fg));">+{{.Compare.TotalAdditions}}</span>
104 <span style="color:hsl(var(--diff-del-fg));">-{{.Compare.TotalDeletions}}</span>
105 </div>
106 </div>
107
108 <div class="flex flex-col gap-4">
109 {{range .Compare.Files}}
110 <section class="card">
111 <div class="card-header" style="padding:0.5rem 0.8rem;">
112 <div class="flex items-center gap-2 font-mono text-xs min-w-0">
113 {{icon "file-code" "size-4"}}
114 {{if eq .Status "renamed"}}
115 <span>{{.OldPath}} → <strong>{{.NewPath}}</strong></span>
116 {{else}}
117 <strong>{{.NewPath}}</strong>
118 {{end}}
119 </div>
120 <div class="flex items-center gap-2 font-mono text-xs">
121 <span style="color:hsl(var(--diff-add-fg));">+{{.Additions}}</span>
122 <span style="color:hsl(var(--diff-del-fg));">-{{.Deletions}}</span>
123 </div>
124 </div>
125
126 {{if .IsBinary}}
127 <div class="card-body text-xs text-muted">{{t $.Lang "repo.binary_notice"}}</div>
128 {{else}}
129 <div style="overflow-x:auto;">
130 <table class="diff-table">
131 <tbody>
132 {{range .Lines}}
133 {{if eq .Type "hunk"}}
134 <tr class="diff-row-hunk">
135 <td class="diff-num">...</td>
136 <td class="diff-num">...</td>
137 <td class="diff-code">{{.Content}}</td>
138 </tr>
139 {{else if eq .Type "add"}}
140 <tr class="diff-row-add">
141 <td class="diff-num"></td>
142 <td class="diff-num">{{.NewNum}}</td>
143 <td class="diff-code">+{{.Content}}</td>
144 </tr>
145 {{else if eq .Type "del"}}
146 <tr class="diff-row-del">
147 <td class="diff-num">{{.OldNum}}</td>
148 <td class="diff-num"></td>
149 <td class="diff-code">-{{.Content}}</td>
150 </tr>
151 {{else}}
152 <tr>
153 <td class="diff-num">{{.OldNum}}</td>
154 <td class="diff-num">{{.NewNum}}</td>
155 <td class="diff-code"> {{.Content}}</td>
156 </tr>
157 {{end}}
158 {{end}}
159 </tbody>
160 </table>
161 </div>
162 {{end}}
163 </section>
164 {{else}}
165 <div class="card p-4 text-xs text-muted">{{t .Lang "pulls.no_changes"}}</div>
166 {{end}}
167 </div>
168 {{end}}
169
170 {{else}}
171 <!-- Conversation Tab -->
172
173 <!-- Initial PR Description Card -->
174 <div class="card mb-4">
175 <div class="card-header text-xs" style="padding:0.5rem 0.85rem;background:hsl(var(--muted)/0.3);">
176 <div class="flex items-center gap-2">
177 {{avatar .PR.AuthorUsername .PR.AuthorAvatar}}
178 <a href="/{{.PR.AuthorUsername}}" class="font-semibold">@{{.PR.AuthorUsername}}</a>
179 <span class="text-muted">{{timeAgo .PR.CreatedAt .Lang}}</span>
180 </div>
181 </div>
182 <div class="card-body markdown-body">
183 {{if .PR.Body}}
184 {{renderMD .PR.Body}}
185 {{else}}
186 <span class="text-xs text-muted">—</span>
187 {{end}}
188 </div>
189 </div>
190
191 <!-- Comments List -->
192 {{if .Comments}}
193 <div class="flex flex-col gap-3 mb-4">
194 {{range .Comments}}
195 <div class="card">
196 <div class="card-header text-xs" style="padding:0.5rem 0.85rem;background:hsl(var(--muted)/0.2);">
197 <div class="flex items-center gap-2">
198 {{avatar .AuthorUsername .AuthorAvatar}}
199 <a href="/{{.AuthorUsername}}" class="font-semibold">@{{.AuthorUsername}}</a>
200 <span class="text-muted">{{timeAgo .CreatedAt $.Lang}}</span>
201 </div>
202 </div>
203 <div class="card-body markdown-body">
204 {{renderMD .Body}}
205 </div>
206 </div>
207 {{end}}
208 </div>
209 {{end}}
210
211 <!-- Merge Box / State Banner -->
212 <div class="card mb-4">
213 {{if eq .PR.Status "merged"}}
214 <div class="card-body flex items-center gap-3" style="background:hsl(var(--muted)/0.2);">
215 <span style="color:hsl(var(--diff-add-fg));">{{icon "git-merge" "size-6"}}</span>
216 <div class="flex flex-col gap-0.5">
217 <div class="font-semibold text-sm">{{t .Lang "pulls.merged_message"}}</div>
218 <div class="text-xs text-muted">
219 {{if .PR.MergedByUsername}}
220 <span>@{{.PR.MergedByUsername}}</span>
221 <span>·</span>
222 {{end}}
223 {{if .PR.MergedAt}}
224 <span>{{formatDate .PR.MergedAt}}</span>
225 {{end}}
226 </div>
227 </div>
228 </div>
229
230 {{else if eq .PR.Status "closed"}}
231 <div class="card-body flex items-center gap-3" style="background:hsl(var(--muted)/0.2);">
232 <span class="text-muted">{{icon "close" "size-6"}}</span>
233 <div class="flex flex-col gap-0.5">
234 <div class="font-semibold text-sm">{{t .Lang "pulls.closed_message"}}</div>
235 <div class="text-xs text-muted">{{t .Lang "pulls.closed_desc"}}</div>
236 </div>
237 </div>
238
239 {{else}}
240 <!-- PR is open -->
241 {{if .CanMergeAction}}
242 <div class="card-body flex flex-col gap-3" style="background:hsl(var(--muted)/0.15);">
243 <div class="flex items-center gap-2 text-sm font-semibold" style="color:hsl(var(--diff-add-fg));">
244 {{icon "check" "size-5"}}
245 <span>{{t .Lang "pulls.able_to_merge"}}</span>
246 </div>
247 <div class="text-xs text-muted">{{t .Lang "pulls.merge_clean"}}</div>
248 <form method="post" action="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}/merge" class="flex items-center gap-2">
249 <input type="hidden" name="_csrf" value="{{.CSRFToken}}">
250 <button type="submit" class="btn btn-primary btn-sm">
251 {{icon "git-merge" "size-3.5"}}
252 <span>{{t .Lang "pulls.merge_btn"}}</span>
253 </button>
254 </form>
255 </div>
256 {{else if not .CanMerge}}
257 <div class="card-body flex items-center gap-3" style="background:hsl(var(--muted)/0.15);">
258 <span style="color:hsl(var(--diff-del-fg));">{{icon "alert-circle" "size-5"}}</span>
259 <div class="flex flex-col gap-0.5">
260 <div class="font-semibold text-sm" style="color:hsl(var(--diff-del-fg));">{{t .Lang "pulls.conflicts_title"}}</div>
261 <div class="text-xs text-muted">{{t .Lang "pulls.conflicts_desc"}}</div>
262 </div>
263 </div>
264 {{else}}
265 <div class="card-body flex items-center gap-2 text-xs text-muted" style="background:hsl(var(--muted)/0.15);">
266 {{icon "check" "size-4"}}
267 <span>{{t .Lang "pulls.only_write_merge"}}</span>
268 </div>
269 {{end}}
270 {{end}}
271 </div>
272
273 <!-- Reviews Panel -->
274 {{if or .Reviews .CanReview}}
275 <div class="card mb-4">
276 <div class="card-header" style="padding:0.55rem 0.85rem;">
277 <span class="font-semibold text-xs flex items-center gap-2">
278 {{icon "shield" "size-4"}}
279 <span>{{t .Lang "reviews.title"}}</span>
280 {{if gt .ApprovedCount 0}}<span class="badge" style="padding:0 0.35rem;font-size:0.68rem;color:hsl(var(--diff-add-fg));">{{.ApprovedCount}} ✓</span>{{end}}
281 {{if gt .ChangesRequestedCount 0}}<span class="badge" style="padding:0 0.35rem;font-size:0.68rem;color:hsl(var(--diff-del-fg));">{{.ChangesRequestedCount}} ✗</span>{{end}}
282 </span>
283 </div>
284 {{if .Reviews}}
285 <div class="dense-list">
286 {{range .Reviews}}
287 <div class="dense-row" style="min-height:2.5rem;padding:0.45rem 0.85rem;">
288 <div class="flex items-center gap-2 min-w-0">
289 {{avatar .ReviewerUsername .ReviewerAvatar}}
290 <a href="/{{.ReviewerUsername}}" class="font-semibold text-sm">@{{.ReviewerUsername}}</a>
291 {{if eq .State "approved"}}
292 <span class="flex items-center gap-1 text-xs font-medium" style="color:hsl(var(--diff-add-fg));">{{icon "check" "size-3.5"}} {{t $.Lang "reviews.approved"}}</span>
293 {{else}}
294 <span class="flex items-center gap-1 text-xs font-medium" style="color:hsl(var(--diff-del-fg));">{{icon "alert-circle" "size-3.5"}} {{t $.Lang "reviews.changes_requested"}}</span>
295 {{end}}
296 </div>
297 <div class="flex items-center gap-2 text-xs text-muted">
298 {{if .Body}}<span class="truncate" style="max-width:22rem;">{{.Body}}</span>{{end}}
299 <span>{{timeAgo .UpdatedAt $.Lang}}</span>
300 </div>
301 </div>
302 {{end}}
303 </div>
304 {{end}}
305 {{if .CanReview}}
306 <div class="card-body" style="border-top:1px solid hsl(var(--border));">
307 <form method="post" action="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}/review" class="flex flex-col gap-2.5">
308 <input type="hidden" name="_csrf" value="{{.CSRFToken}}">
309 <textarea name="body" class="textarea" rows="2" placeholder="{{t .Lang "reviews.body_ph"}}" maxlength="10000"></textarea>
310 <div class="flex items-center gap-2 justify-end">
311 <button type="submit" name="state" value="changes_requested" class="btn btn-outline btn-sm">
312 {{icon "alert-circle" "size-3.5"}}
313 <span>{{t .Lang "reviews.request_changes"}}</span>
314 </button>
315 <button type="submit" name="state" value="approved" class="btn btn-primary btn-sm">
316 {{icon "check" "size-3.5"}}
317 <span>{{t .Lang "reviews.approve"}}</span>
318 </button>
319 </div>
320 </form>
321 </div>
322 {{end}}
323 </div>
324 {{end}}
325
326 <!-- Add Comment Form --> {{if .CurrentUser}}
327 <div class="card">
328 <form method="post" action="/{{.Repo.OwnerUsername}}/{{.Repo.Name}}/pulls/{{.PR.Number}}/comments" class="card-body flex flex-col gap-2.5">
329 <input type="hidden" name="_csrf" value="{{.CSRFToken}}">
330 <textarea name="body" class="textarea" rows="4" placeholder="{{t .Lang "pulls.comment_ph"}}" required maxlength="20000"></textarea>
331 <div class="flex items-center justify-end">
332 <button type="submit" class="btn btn-primary btn-sm">
333 {{icon "message" "size-3.5"}}
334 <span>{{t .Lang "pulls.comment_btn"}}</span>
335 </button>
336 </div>
337 </form>
338 </div>
339 {{end}}
340
341 {{end}}
342 </div>
343 {{end}}