jira-cli/internal/view/sprint.go

228 lines
5.6 KiB
Go

package view
import (
"fmt"
"io"
"os"
"strings"
"text/tabwriter"
"time"
"github.com/ankitpokhrel/jira-cli/api"
"github.com/ankitpokhrel/jira-cli/internal/cmdutil"
"github.com/ankitpokhrel/jira-cli/pkg/jira"
"github.com/ankitpokhrel/jira-cli/pkg/jira/filter/issue"
"github.com/ankitpokhrel/jira-cli/pkg/tui"
)
// SprintIssueFunc provides issues in the sprint.
type SprintIssueFunc func(boardID, sprintID int) []*jira.Issue
// SprintList is a list view for sprints.
type SprintList struct {
Project string
Board string
Server string
Data []*jira.Sprint
Issues SprintIssueFunc
Display DisplayFormat
}
// Render renders the sprint explorer view.
//
//nolint:dupl
func (sl *SprintList) Render() error {
renderer, err := MDRenderer()
if err != nil {
return err
}
data := sl.data()
view := tui.NewPreview(
tui.WithPreviewFooterText(
fmt.Sprintf(
"Showing %d results from board %q of project %q",
len(sl.Data), sl.Board, sl.Project,
),
),
tui.WithInitialText(helpText),
tui.WithContentTableOpts(
tui.WithFixedColumns(sl.Display.FixedColumns),
tui.WithTableStyle(sl.Display.TableStyle),
tui.WithSelectedFunc(navigate(sl.Server)),
tui.WithViewModeFunc(func(r, c int, d interface{}) (func() interface{}, func(interface{}) (string, error)) {
dataFn := func() interface{} {
data := d.(tui.TableData)
ci := data.GetIndex(fieldKey)
iss, _ := api.ProxyGetIssue(api.DefaultClient(false), data.Get(r, ci), issue.NewNumCommentsFilter(1))
return iss
}
renderFn := func(i interface{}) (string, error) {
iss := Issue{
Server: sl.Server,
Data: i.(*jira.Issue),
Options: IssueOption{NumComments: 1},
}
return iss.RenderedOut(renderer)
}
return dataFn, renderFn
}),
tui.WithCopyFunc(copyURL(sl.Server)),
tui.WithCopyKeyFunc(copyKey()),
),
)
return view.Paint(data)
}
// RenderInTable renders the list in table view.
func (sl *SprintList) RenderInTable() error {
if sl.Display.Plain || tui.IsDumbTerminal() || tui.IsNotTTY() {
w := tabwriter.NewWriter(os.Stdout, 0, tabWidth, 1, '\t', 0)
return sl.renderPlain(w)
}
data := sl.tableData()
view := tui.NewTable(
tui.WithFixedColumns(sl.Display.FixedColumns),
tui.WithTableStyle(sl.Display.TableStyle),
tui.WithTableFooterText(
fmt.Sprintf(
"Showing %d results from board %q of project %q",
len(sl.Data), sl.Board, sl.Project,
),
),
)
return view.Paint(data)
}
// renderPlain renders the issue in plain view.
func (sl *SprintList) renderPlain(w io.Writer) error {
return renderPlain(w, sl.tableData())
}
func (sl *SprintList) data() []tui.PreviewData {
data := make([]tui.PreviewData, 0, len(sl.Data))
data = append(data, tui.PreviewData{
Key: "help",
Menu: "?",
Contents: func(s string) interface{} {
return helpText
},
})
for _, s := range sl.Data {
bid, sid := s.BoardID, s.ID
data = append(data, tui.PreviewData{
Key: fmt.Sprintf("%d-%d-%s", bid, sid, s.StartDate),
Menu: fmt.Sprintf(
"➤ #%d %s: ⦗%s - %s⦘",
s.ID,
prepareTitle(s.Name),
cmdutil.FormatDateTimeHuman(s.StartDate, time.RFC3339),
cmdutil.FormatDateTimeHuman(s.EndDate, time.RFC3339),
),
Contents: func(key string) interface{} {
issues := sl.Issues(bid, sid)
return sl.tabularize(issues)
},
})
}
return data
}
func (sl *SprintList) tabularize(issues []*jira.Issue) tui.TableData {
var data tui.TableData
data = append(data, ValidIssueColumns())
for _, issue := range issues {
data = append(data, []string{
issue.Fields.IssueType.Name,
issue.Key,
prepareTitle(issue.Fields.Summary),
issue.Fields.Status.Name,
issue.Fields.Assignee.Name,
issue.Fields.Reporter.Name,
issue.Fields.Priority.Name,
issue.Fields.Resolution.Name,
formatDateTime(issue.Fields.Created, jira.RFC3339, sl.Display.Timezone),
formatDateTime(issue.Fields.Updated, jira.RFC3339, sl.Display.Timezone),
strings.Join(issue.Fields.Labels, ","),
})
}
return data
}
func (sl *SprintList) validColumnsMap() map[string]struct{} {
columns := ValidSprintColumns()
out := make(map[string]struct{}, len(columns))
for _, c := range columns {
out[c] = struct{}{}
}
return out
}
func (sl *SprintList) tableHeader() []string {
if len(sl.Display.Columns) == 0 {
return ValidSprintColumns()
}
var headers []string
columnsMap := sl.validColumnsMap()
for _, c := range sl.Display.Columns {
c = strings.ToUpper(c)
if _, ok := columnsMap[c]; ok {
headers = append(headers, strings.ToUpper(c))
}
}
return headers
}
func (sl *SprintList) tableData() tui.TableData {
var data tui.TableData
headers := sl.tableHeader()
if !(sl.Display.Plain && sl.Display.NoHeaders) {
data = append(data, headers)
}
if len(headers) == 0 {
headers = ValidSprintColumns()
}
for _, s := range sl.Data {
data = append(data, sl.assignColumns(headers, s))
}
return data
}
func (sl *SprintList) assignColumns(columns []string, sprint *jira.Sprint) []string {
var bucket []string
for _, column := range columns {
switch column {
case fieldID:
bucket = append(bucket, fmt.Sprintf("%d", sprint.ID))
case fieldName:
bucket = append(bucket, sprint.Name)
case fieldStartDate:
bucket = append(bucket, formatDateTime(sprint.StartDate, time.RFC3339, sl.Display.Timezone))
case fieldEndDate:
bucket = append(bucket, formatDateTime(sprint.EndDate, time.RFC3339, sl.Display.Timezone))
case fieldCompleteDate:
bucket = append(bucket, formatDateTime(sprint.CompleteDate, time.RFC3339, sl.Display.Timezone))
case fieldState:
bucket = append(bucket, sprint.Status)
}
}
return bucket
}