feat(web): show dynamic status in bugs list

Amolith and Crush created

Bugs list now displays "Closed" or "Reopened" instead of
generic "Updated" when the last event was a status change.
Other events (comments, title changes, label changes) still
show "Updated".

Implements: bug-f3568dc
Co-authored-by: Crush <crush@charm.land>

Change summary

pkg/web/templates/bugs.html |  2 
pkg/web/webui_bugs.go       | 90 +++++++++++++++++++++++++++++---------
2 files changed, 69 insertions(+), 23 deletions(-)

Detailed changes

pkg/web/templates/bugs.html 🔗

@@ -34,7 +34,7 @@
         {{if .AuthorAvatar}}<img src="{{.AuthorAvatar}}" alt="" class="avatar" loading="lazy" referrerpolicy="no-referrer">{{end}}<strong>{{.Author}}</strong> opened <time datetime="{{.CreatedAt | rfc3339}}" data-tooltip="{{.CreatedAt | formatDate}}">{{.CreatedAt | relativeTime}}</time>
         {{- if gt .CommentCount 0}}<span class="separator">•</span>{{.CommentCount}} {{if eq .CommentCount 1}}comment{{else}}comments{{end}}
         {{- end}}
-        {{- if .HasActivity}}<span class="separator">•</span>Updated <time datetime="{{.LastActivity | rfc3339}}" data-tooltip="{{.LastActivity | formatDate}}">{{.LastActivity | relativeTime}}</time>
+        {{- if .HasActivity}}<span class="separator">•</span>{{if eq .LastEventAction "closed"}}Closed{{else if eq .LastEventAction "reopened"}}Reopened{{else}}Updated{{end}} <time datetime="{{.LastActivity | rfc3339}}" data-tooltip="{{.LastActivity | formatDate}}">{{.LastActivity | relativeTime}}</time>
         {{- end}}
      </footer>
   </article>

pkg/web/webui_bugs.go 🔗

@@ -32,17 +32,18 @@ type BugsData struct {
 }
 
 type BugListItem struct {
-	ID           string
-	FullID       string
-	Title        string
-	Author       string
-	AuthorAvatar string
-	Status       string
-	CreatedAt    time.Time
-	LastActivity time.Time
-	HasActivity  bool
-	CommentCount int
-	Labels       []Label
+	ID              string
+	FullID          string
+	Title           string
+	Author          string
+	AuthorAvatar    string
+	Status          string
+	CreatedAt       time.Time
+	LastActivity    time.Time
+	HasActivity     bool
+	LastEventAction string
+	CommentCount    int
+	Labels          []Label
 }
 
 type BugData struct {
@@ -131,17 +132,18 @@ func getBugsList(rc *cache.RepoCache, status string) ([]BugListItem, error) {
 		}
 
 		bugs = append(bugs, BugListItem{
-			ID:           snap.Id().Human(),
-			FullID:       snap.Id().String(),
-			Title:        snap.Title,
-			Author:       snap.Author.DisplayName(),
-			AuthorAvatar: snap.Author.AvatarUrl(),
-			Status:       snap.Status.String(),
-			CreatedAt:    snap.CreateTime,
-			LastActivity: getLastActivity(snap),
-			HasActivity:  len(snap.Timeline) > 1,
-			CommentCount: countComments(snap),
-			Labels:       labels,
+			ID:              snap.Id().Human(),
+			FullID:          snap.Id().String(),
+			Title:           snap.Title,
+			Author:          snap.Author.DisplayName(),
+			AuthorAvatar:    snap.Author.AvatarUrl(),
+			Status:          snap.Status.String(),
+			CreatedAt:       snap.CreateTime,
+			LastActivity:    getLastActivity(snap),
+			HasActivity:     len(snap.Timeline) > 1,
+			LastEventAction: getLastEventAction(snap),
+			CommentCount:    countComments(snap),
+			Labels:          labels,
 		})
 	}
 
@@ -179,6 +181,50 @@ func getLastActivity(snap *bug.Snapshot) time.Time {
 	return lastTime
 }
 
+func getLastEventAction(snap *bug.Snapshot) string {
+	var lastTime time.Time
+	var lastAction string
+
+	for _, item := range snap.Timeline {
+		var itemTime time.Time
+
+		switch op := item.(type) {
+		case *bug.CreateTimelineItem:
+			itemTime = op.CreatedAt.Time()
+			if itemTime.After(lastTime) {
+				lastTime = itemTime
+				lastAction = "created"
+			}
+		case *bug.AddCommentTimelineItem:
+			itemTime = op.CreatedAt.Time()
+			if itemTime.After(lastTime) {
+				lastTime = itemTime
+				lastAction = "commented"
+			}
+		case *bug.SetTitleTimelineItem:
+			itemTime = op.UnixTime.Time()
+			if itemTime.After(lastTime) {
+				lastTime = itemTime
+				lastAction = "updated"
+			}
+		case *bug.SetStatusTimelineItem:
+			itemTime = op.UnixTime.Time()
+			if itemTime.After(lastTime) {
+				lastTime = itemTime
+				lastAction = op.Status.Action()
+			}
+		case *bug.LabelChangeTimelineItem:
+			itemTime = op.UnixTime.Time()
+			if itemTime.After(lastTime) {
+				lastTime = itemTime
+				lastAction = "updated"
+			}
+		}
+	}
+
+	return lastAction
+}
+
 func countComments(snap *bug.Snapshot) int {
 	count := 0
 	for _, item := range snap.Timeline {