fleet/tools/github-manage/pkg/ghapi/views.go
George Karr 6ebbef874b
adding sum of estimates and fixing workflow progress menu (#32694)
What’s in this PR

1) Smarter default sorting for issues (used by the TUI)

New ghapi.SortIssuesForDisplay helper that orders issues by:

Priority label (P0 → P1 → P2 → none)

Presence of customer-* / prospect-* labels

Type labels (story → bug → ~sub-task → others)

Issue number (descending)
This is applied before filtering so views start in a meaningful order. 
[GitHub](https://github.com/fleetdm/fleet/pull/32694/files)

Implementation lives in tools/github-manage/pkg/ghapi/sort.go.
Comprehensive tests cover all combinations, tie-breakers, and stability.
GitHub
+1

2) Estimates: show the sum for the current selection

The header now displays Σest sel=<sum> for the currently selected
issues, both in filtered and unfiltered views, making quick capacity
checks easier.
[GitHub](https://github.com/fleetdm/fleet/pull/32694/files)

3) Better progress UI for workflows

Task list is now windowed (last ~10 items) with auto-scroll to the
currently running or most recently finished task, plus “earlier/more
tasks” ellipses and a progress counter at the bottom. This keeps the
view focused during long runs.
[GitHub](https://github.com/fleetdm/fleet/pull/32694/files)

4) Project estimates fetch now includes total count

Switched from GetEstimatedTicketsForProject to
GetEstimatedTicketsForProjectWithTotal, so we can show totalAvailable
alongside rawFetched/limit.
[GitHub](https://github.com/fleetdm/fleet/pull/32694/files)

---------

Co-authored-by: Jordan Montgomery <elijah.jordan.montgomery@gmail.com>
2025-09-11 13:47:46 -05:00

91 lines
2.7 KiB
Go

// Package ghapi provides views for managed searches and display templates for GitHub issues.
package ghapi
import (
"fmt"
)
type ViewType string
const (
ISSUE_LIST ViewType = "issue_list"
ISSUE_DETAIL ViewType = "issue_detail"
PROJECT_DETAIL ViewType = "project_detail"
MDM_LABEL = "#g-mdm"
)
type View struct {
Type ViewType `json:"type"`
Title string `json:"title"`
Filters []string `json:"filters,omitempty"` // Search filters for issues
}
// NewView creates a new view with the specified type, title, and optional filters.
func NewView(viewType ViewType, title string, filters ...string) *View {
return &View{
Type: viewType,
Title: title,
Filters: filters,
}
}
// GetMDMTicketsEstimated returns estimated tickets from the MDM project.
func GetMDMTicketsEstimated() ([]ProjectItem, error) {
return GetEstimatedTicketsForProject(58, 500)
}
// GetEstimatedTicketsForProject gets estimated tickets from the drafting project filtered by the project's label.
func GetEstimatedTicketsForProject(projectID, limit int) ([]ProjectItem, error) {
items, _, err := GetEstimatedTicketsForProjectWithTotal(projectID, limit)
return items, err
}
// GetEstimatedTicketsForProjectWithTotal returns filtered estimated issues and the total
// number of items in the drafting project (unfiltered). This allows callers to warn when
// the drafting project's total exceeds the fetch limit (some estimated items might be
// beyond the first page).
func GetEstimatedTicketsForProjectWithTotal(projectID, limit int) ([]ProjectItem, int, error) {
// Get the label for this project
label, exists := ProjectLabels[projectID]
if !exists {
return nil, 0, fmt.Errorf("no label mapping found for project ID %d. Available projects: %v", projectID, getProjectIDsWithLabels())
}
// Grab issues from Drafting project
draftingProjectID := Aliases["draft"]
estimatedName, err := FindFieldValueByName(draftingProjectID, "Status", "estimated")
if err != nil {
return nil, 0, err
}
issues, total, err := GetProjectItemsWithTotal(draftingProjectID, limit)
if err != nil {
return nil, 0, err
}
// filter down to issues that are estimated with the specified label
var estimatedIssues []ProjectItem
for _, issue := range issues {
if issue.Labels != nil {
for _, issueLabel := range issue.Labels {
if issueLabel == label {
if issue.Status == estimatedName {
estimatedIssues = append(estimatedIssues, issue)
}
break
}
}
}
}
return estimatedIssues, total, nil
}
// getProjectIDsWithLabels returns a slice of project IDs that have label mappings.
func getProjectIDsWithLabels() []int {
ids := make([]int, 0, len(ProjectLabels))
for id := range ProjectLabels {
ids = append(ids, id)
}
return ids
}