repos / git-pr

a self-hosted git collaboration server
git clone https://github.com/picosh/git-pr.git

commit
a444d69
parent
603c411
author
Eric Bower
date
2025-12-28 22:41:23 -0500 EST
style: tabbed layout for PRs
5 files changed,  +73, -38
M web.go
M static/git-pr.css
+25, -0
 1@@ -101,6 +101,31 @@ details {
 2   overflow-x: scroll;
 3 }
 4 
 5+.pr-tabs {
 6+  display: flex;
 7+  gap: 0;
 8+  border-bottom: 1px solid var(--grey-light);
 9+  margin-bottom: var(--line-height);
10+}
11+
12+.pr-tab {
13+  padding: 0.5rem 1rem;
14+  text-decoration: none;
15+  border: 1px solid transparent;
16+  border-bottom: none;
17+  margin-bottom: -1px;
18+}
19+
20+.pr-tab:hover {
21+  background-color: var(--grey-light);
22+}
23+
24+.pr-tab-active {
25+  border-color: var(--grey-light);
26+  border-bottom: 1px solid var(--bg-color);
27+  background-color: var(--bg-color);
28+}
29+
30 @media only screen and (max-width: 40em) {
31   .collapse {
32     flex-direction: column;
A tmpl/components/pr-tabs.html
+6, -0
1@@ -0,0 +1,6 @@
2+{{define "pr-tabs"}}
3+<div class="pr-tabs">
4+  <a href="/prs/{{.Pr.ID}}" class="pr-tab{{if eq .Tab "timeline"}} pr-tab-active{{end}}">Timeline</a>
5+  <a href="/ps/{{.Patchset.ID}}" class="pr-tab{{if eq .Tab "patchsets"}} pr-tab-active{{end}}">Patchsets</a>
6+</div>
7+{{end}}
M tmpl/components/range-diff.html
+1, -1
1@@ -82,7 +82,7 @@
2             {{end}}
3 
4             <div>
5-              {{- if .Files -}}
6+              {{- if and .Files (ne .Type "add") -}}
7                 {{range .Files}}
8                   <div class="flex gap">
9                     <div class="flex-1" style="width: 48%;">
M tmpl/pages/pr.html
+24, -36
 1@@ -16,10 +16,27 @@
 2 {{template "pr-header" .}}
 3 
 4 <main class="group">
 5-  <div class="flex gap-2 collapse">
 6-    <div class="group text-sm" style="width: 300px;">
 7-      <h3 class="text-lg">Logs</h3>
 8-      {{range .Logs}}
 9+  {{template "pr-tabs" .}}
10+
11+  {{if eq .Tab "timeline"}}
12+  <div class="group text-sm timeline">
13+    {{range .Logs}}
14+    <div class="timeline-item">
15+      {{if .RangeDiff}}
16+      <details class="mb">
17+        <summary class="text-sm">Range Diff ↕ <code><a href="/rd/{{.Patchset.ID}}">rd-{{.Patchset.ID}}</a></code></summary>
18+        <div class="group">
19+        {{- range .RangeDiff -}}
20+          <div>
21+            <code class='{{if eq .Type "rm"}}pill-admin{{else if eq .Type "add"}}pill-success{{else if eq .Type "diff"}}pill-review{{end}}'>
22+              {{.Header}}
23+            </code>
24+          </div>
25+        {{- end -}}
26+        </div>
27+      </details>
28+      {{end}}
29+
30       <div>
31         {{template "user-pill" .UserData}}
32         <span class="font-bold">
33@@ -44,44 +61,15 @@
34         <span>on <date>{{.Date}}</date></span>
35         {{if .Data.String}}<code>{{.Data}}</code>{{end}}
36       </div>
37-      {{end}}
38-    </div>
39-
40-    <div class="group text-sm flex-1">
41-      <h3 class="text-lg">Patchsets</h3>
42-
43-      {{range .Patchsets}}
44-        {{if .RangeDiff}}
45-        <details>
46-          <summary class="text-sm">Range Diff ↕ <code><a href="/rd/{{.ID}}">rd-{{.ID}}</a></code></summary>
47-          <div class="group">
48-          {{- range .RangeDiff -}}
49-            <div>
50-              <code class='{{if eq .Type "rm"}}pill-admin{{else if eq .Type "add"}}pill-success{{else if eq .Type "diff"}}pill-review{{end}}'>
51-                {{.Header}}
52-              </code>
53-            </div>
54-          {{- end -}}
55-          </div>
56-        </details>
57-        {{end}}
58-
59-        <div>
60-          <a href="/ps/{{.Patchset.ID}}"><code class="{{if .Review}}pill-review{{end}}">{{.FormattedID}}</code></a>
61-          <span> by </span>
62-          {{template "user-pill" .UserData}}
63-          <span>on <date>{{.Date}}</date></span>
64-        </div>
65-      {{end}}
66     </div>
67+    {{end}}
68   </div>
69-
70-  <hr class="w-full" />
71-
72+  {{else}}
73   {{if .IsRangeDiff}}
74     {{template "range-diff" .}}
75   {{else}}
76     {{template "patchset" .}}
77   {{end}}
78+  {{end}}
79 </main>
80 {{end}}
M web.go
+17, -1
 1@@ -545,6 +545,7 @@ type EventLogData struct {
 2 	*Patchset
 3 	FormattedPatchsetID string
 4 	Date                string
 5+	RangeDiff           []*RangeDiffOutput
 6 }
 7 
 8 type PatchsetData struct {
 9@@ -557,6 +558,7 @@ type PatchsetData struct {
10 
11 type PrDetailData struct {
12 	Page         string
13+	Tab          string
14 	Repo         LinkData
15 	Pr           PrData
16 	Patchset     *Patchset
17@@ -805,6 +807,7 @@ func createPrDetail(page string) http.HandlerFunc {
18 				return
19 			}
20 			var logps *Patchset
21+			var rangeDiff []*RangeDiffOutput
22 			if eventlog.PatchsetID.Int64 > 0 {
23 				logps, err = web.Pr.GetPatchsetByID(eventlog.PatchsetID.Int64)
24 				if err != nil {
25@@ -812,12 +815,19 @@ func createPrDetail(page string) http.HandlerFunc {
26 					w.WriteHeader(http.StatusUnprocessableEntity)
27 					return
28 				}
29+				for _, psData := range patchsetsData {
30+					if psData.Patchset.ID == eventlog.PatchsetID.Int64 {
31+						rangeDiff = psData.RangeDiff
32+						break
33+					}
34+				}
35 			}
36 
37 			logData = append(logData, EventLogData{
38 				EventLog:            eventlog,
39 				FormattedPatchsetID: getFormattedPatchsetID(eventlog.PatchsetID.Int64),
40 				Patchset:            logps,
41+				RangeDiff:           rangeDiff,
42 				UserData: UserData{
43 					UserID:    user.ID,
44 					Name:      user.Name,
45@@ -845,8 +855,14 @@ func createPrDetail(page string) http.HandlerFunc {
46 
47 		repoNs := web.Backend.CreateRepoNs(repoOwner.Name, repo.Name)
48 		url := fmt.Sprintf("/r/%s/%s", repoOwner.Name, repo.Name)
49+		tab := "timeline"
50+		if page == "ps" || page == "rd" {
51+			tab = "patchsets"
52+		}
53+
54 		err = prTmpl.Execute(w, PrDetailData{
55-			Page: "pr",
56+			Page: page,
57+			Tab:  tab,
58 			Repo: LinkData{
59 				Url:  template.URL(url),
60 				Text: repoNs,