Skip to content

Commit b27d983

Browse files
[CAP-9324] use compact tables for task and task run lists (#319)
Switch task and task run interactive lists from bubble lists to compact tables, matching the style used by the workflow list. Versions keep using lists. GitOrigin-RevId: e35b39f32152d4c08586b8962c2ec9e52968a022
1 parent 3a0d93c commit b27d983

7 files changed

Lines changed: 146 additions & 123 deletions

File tree

pkg/task/table.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
package task
2+
3+
import (
4+
"github.com/evertras/bubble-table/table"
5+
6+
wfclient "github.com/render-oss/cli/pkg/client/workflows"
7+
"github.com/render-oss/cli/pkg/pointers"
8+
)
9+
10+
func Columns() []table.Column {
11+
return []table.Column{
12+
table.NewFlexColumn("Name", "Name", 4).WithFiltered(true),
13+
table.NewColumn("ID", "ID", 30).WithFiltered(true),
14+
table.NewFlexColumn("Created", "Created", 3),
15+
}
16+
}
17+
18+
func TableRow(t *wfclient.Task) table.Row {
19+
return table.NewRow(table.RowData{
20+
"Name": t.Name,
21+
"ID": t.Id,
22+
"Created": pointers.TimeValue(&t.CreatedAt),
23+
"task": t,
24+
})
25+
}

pkg/task/tui.go

Lines changed: 0 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,10 @@
11
package task
22

33
import (
4-
"github.com/charmbracelet/lipgloss"
5-
64
wfclient "github.com/render-oss/cli/pkg/client/workflows"
75
"github.com/render-oss/cli/pkg/pointers"
8-
"github.com/render-oss/cli/pkg/style"
96
)
107

11-
type ListItem struct {
12-
task *wfclient.Task
13-
}
14-
15-
func (i ListItem) Task() *wfclient.Task {
16-
return i.task
17-
}
18-
19-
func NewListItem(t *wfclient.Task) ListItem {
20-
return ListItem{task: t}
21-
}
22-
23-
func (i ListItem) Title() string {
24-
return style.Title.Render(i.task.Name)
25-
}
26-
27-
func (i ListItem) Description() string {
28-
statusLine := style.Status.Foreground(style.ColorDeprioritized).Render("Ready")
29-
30-
timeLine := lipgloss.JoinHorizontal(lipgloss.Left,
31-
style.FormatKeyValue("ID", i.task.Id),
32-
" ",
33-
style.FormatKeyValue("Created", pointers.TimeValue(&i.task.CreatedAt)),
34-
)
35-
36-
return lipgloss.JoinVertical(lipgloss.Left, statusLine, timeLine)
37-
}
38-
39-
func (i ListItem) FilterValue() string {
40-
return i.task.Name
41-
}
42-
43-
func (i ListItem) Height() int {
44-
return 5
45-
}
46-
478
func Header() []string {
489
return []string{"Name", "ID", "Created"}
4910
}

pkg/taskrun/list.go

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -6,26 +6,9 @@ import (
66
"github.com/charmbracelet/lipgloss"
77

88
wfclient "github.com/render-oss/cli/pkg/client/workflows"
9-
"github.com/render-oss/cli/pkg/pointers"
109
"github.com/render-oss/cli/pkg/style"
1110
)
1211

13-
type ListItem struct {
14-
taskRun *wfclient.TaskRun
15-
}
16-
17-
func (i ListItem) TaskRun() *wfclient.TaskRun {
18-
return i.taskRun
19-
}
20-
21-
func NewListItem(tr *wfclient.TaskRun) ListItem {
22-
return ListItem{taskRun: tr}
23-
}
24-
25-
func (i ListItem) Title() string {
26-
return style.Title.Render(i.taskRun.Id)
27-
}
28-
2912
func statusWithStyle(status wfclient.TaskRunStatus) lipgloss.Style {
3013
switch status {
3114
case wfclient.Failed:
@@ -36,36 +19,6 @@ func statusWithStyle(status wfclient.TaskRunStatus) lipgloss.Style {
3619
return style.Status.Foreground(style.ColorOK)
3720
}
3821

39-
func (i ListItem) Description() string {
40-
statusLine := statusWithStyle(i.taskRun.Status).Render(string(i.taskRun.Status))
41-
42-
var timeInfo string
43-
if i.taskRun.StartedAt != nil {
44-
if i.taskRun.CompletedAt != nil {
45-
duration := i.taskRun.CompletedAt.Sub(*i.taskRun.StartedAt)
46-
timeInfo = lipgloss.JoinHorizontal(lipgloss.Left,
47-
style.FormatKeyValue("Started", pointers.TimeValue(i.taskRun.StartedAt)),
48-
" ",
49-
style.FormatKeyValue("Duration", duration.String()),
50-
)
51-
} else {
52-
timeInfo = style.FormatKeyValue("Started", pointers.TimeValue(i.taskRun.StartedAt))
53-
}
54-
} else {
55-
timeInfo = style.FormatKeyValue("Status", "Not started")
56-
}
57-
58-
return lipgloss.JoinVertical(lipgloss.Left, statusLine, timeInfo)
59-
}
60-
61-
func (i ListItem) FilterValue() string {
62-
return i.taskRun.Id
63-
}
64-
65-
func (i ListItem) Height() int {
66-
return 5
67-
}
68-
6922
func Header() []string {
7023
return []string{"ID", "Status", "Started", "Completed", "Duration"}
7124
}
@@ -79,14 +32,7 @@ func Row(taskRun *wfclient.TaskRun) []string {
7932
if taskRun.CompletedAt != nil {
8033
completed = taskRun.CompletedAt.Format(time.RFC3339)
8134
duration = taskRun.CompletedAt.Sub(*taskRun.StartedAt).String()
82-
} else {
83-
completed = ""
84-
duration = ""
8535
}
86-
} else {
87-
started = ""
88-
completed = ""
89-
duration = ""
9036
}
9137

9238
return []string{

pkg/taskrun/table.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package taskrun
2+
3+
import (
4+
"github.com/evertras/bubble-table/table"
5+
6+
wfclient "github.com/render-oss/cli/pkg/client/workflows"
7+
"github.com/render-oss/cli/pkg/pointers"
8+
)
9+
10+
func Columns() []table.Column {
11+
return []table.Column{
12+
table.NewColumn("ID", "ID", 30).WithFiltered(true),
13+
table.NewFlexColumn("Status", "Status", 2).WithFiltered(true),
14+
table.NewFlexColumn("Started", "Started", 3),
15+
table.NewFlexColumn("Completed", "Completed", 3),
16+
table.NewFlexColumn("Duration", "Duration", 2),
17+
}
18+
}
19+
20+
func TableRow(tr *wfclient.TaskRun) table.Row {
21+
var started, completed, duration string
22+
23+
if tr.StartedAt != nil {
24+
started = pointers.TimeValue(tr.StartedAt)
25+
26+
if tr.CompletedAt != nil {
27+
completed = pointers.TimeValue(tr.CompletedAt)
28+
duration = tr.CompletedAt.Sub(*tr.StartedAt).String()
29+
}
30+
}
31+
32+
return table.NewRow(table.RowData{
33+
"ID": tr.Id,
34+
"Status": table.NewStyledCell(string(tr.Status), statusWithStyle(tr.Status)),
35+
"Started": started,
36+
"Completed": completed,
37+
"Duration": duration,
38+
"taskRun": tr,
39+
})
40+
}

pkg/tui/views/workflows/loader.go

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,41 @@ func (w *WorkflowLoader) LoadTaskRunList(ctx context.Context, input TaskRunListI
283283
return w.taskRepo.ListTaskRuns(ctx, params)
284284
}
285285

286+
// LoadAllTasks fetches tasks in a single request without cursor-based pagination.
287+
// The API default page size applies (typically 100 or less). The compact table
288+
// widget does not support progressive loading, so results are capped to one page.
289+
func (w *WorkflowLoader) LoadAllTasks(ctx context.Context, input TaskListInput) ([]*wfclient.Task, error) {
290+
params := &client.ListTasksParams{}
291+
292+
if input.WorkflowVersionID != "" {
293+
params.WorkflowVersionId = pointers.From([]string{input.WorkflowVersionID})
294+
} else if input.WorkflowID != "" {
295+
if input.LatestVersionOnly {
296+
versionID, err := w.latestVersionID(ctx, input.WorkflowID)
297+
if err != nil {
298+
return nil, err
299+
}
300+
params.WorkflowVersionId = pointers.From([]string{versionID})
301+
} else {
302+
params.WorkflowId = pointers.From([]string{input.WorkflowID})
303+
}
304+
}
305+
306+
_, tasks, err := w.taskRepo.ListTasks(ctx, params)
307+
return tasks, err
308+
}
309+
310+
// LoadAllTaskRuns fetches task runs in a single request without cursor-based pagination.
311+
// Results are capped at 100 items. The compact table widget does not support
312+
// progressive loading, so older runs beyond this limit will not be shown.
313+
func (w *WorkflowLoader) LoadAllTaskRuns(ctx context.Context, input TaskRunListInput) ([]*wfclient.TaskRun, error) {
314+
pageSize := 100
315+
params := &client.ListTaskRunsParams{Limit: &pageSize, TaskId: pointers.From([]string{input.TaskID})}
316+
317+
_, taskRuns, err := w.taskRepo.ListTaskRuns(ctx, params)
318+
return taskRuns, err
319+
}
320+
286321
func (w *WorkflowLoader) LoadTaskRunDetails(ctx context.Context, input *TaskRunDetailsInput) (*workflows.TaskRunDetails, error) {
287322
return w.taskRepo.GetTaskRunDetails(ctx, input.TaskRunID)
288323
}

pkg/tui/views/workflows/tasklist.go

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55

66
tea "github.com/charmbracelet/bubbletea"
7+
btable "github.com/evertras/bubble-table/table"
78

89
wfclient "github.com/render-oss/cli/pkg/client/workflows"
910
"github.com/render-oss/cli/pkg/command"
@@ -12,36 +13,44 @@ import (
1213
)
1314

1415
type TaskListView struct {
15-
list *tui.List[*wfclient.Task]
16+
table *tui.Table[*wfclient.Task]
1617
}
1718

1819
func NewTaskListView(ctx context.Context, workflowLoader *WorkflowLoader, input TaskListInput, generateCommands func(*wfclient.Task) tea.Cmd) *TaskListView {
19-
onSelect := func(selectedItem tui.ListItem) tea.Cmd {
20-
selectedTask := selectedItem.(task.ListItem).Task()
21-
return generateCommands(selectedTask)
20+
onSelect := func(rows []btable.Row) tea.Cmd {
21+
if len(rows) == 0 {
22+
return nil
23+
}
24+
25+
t, ok := rows[0].Data["task"].(*wfclient.Task)
26+
if !ok {
27+
return nil
28+
}
29+
30+
return generateCommands(t)
2231
}
2332

2433
return &TaskListView{
25-
list: tui.NewList(
26-
"",
27-
command.PaginatedLoadCmd(ctx, workflowLoader.LoadTaskList, input),
28-
func(t *wfclient.Task) tui.ListItem {
29-
return task.NewListItem(t)
34+
table: tui.NewTable(
35+
task.Columns(),
36+
command.LoadCmd(ctx, workflowLoader.LoadAllTasks, input),
37+
func(t *wfclient.Task) btable.Row {
38+
return task.TableRow(t)
3039
},
31-
tui.WithOnSelect[*wfclient.Task](onSelect),
40+
onSelect,
3241
),
3342
}
3443
}
3544

3645
func (v *TaskListView) Init() tea.Cmd {
37-
return v.list.Init()
46+
return v.table.Init()
3847
}
3948

4049
func (v *TaskListView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
41-
_, cmd := v.list.Update(msg)
50+
_, cmd := v.table.Update(msg)
4251
return v, cmd
4352
}
4453

4554
func (v *TaskListView) View() string {
46-
return v.list.View()
55+
return v.table.View()
4756
}

pkg/tui/views/workflows/taskrunlist.go

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55

66
tea "github.com/charmbracelet/bubbletea"
7+
btable "github.com/evertras/bubble-table/table"
78

89
wfclient "github.com/render-oss/cli/pkg/client/workflows"
910
"github.com/render-oss/cli/pkg/command"
@@ -12,38 +13,44 @@ import (
1213
)
1314

1415
type TaskRunListView struct {
15-
list *tui.List[*wfclient.TaskRun]
16+
table *tui.Table[*wfclient.TaskRun]
1617
}
1718

1819
func NewTaskRunListView(ctx context.Context, workflowLoader *WorkflowLoader, input TaskRunListInput, generateCommands func(*wfclient.TaskRun) tea.Cmd) *TaskRunListView {
19-
onSelect := func(selectedItem tui.ListItem) tea.Cmd {
20-
selectedTaskRun := selectedItem.(taskrun.ListItem).TaskRun()
21-
return generateCommands(selectedTaskRun)
22-
}
20+
onSelect := func(rows []btable.Row) tea.Cmd {
21+
if len(rows) == 0 {
22+
return nil
23+
}
24+
25+
tr, ok := rows[0].Data["taskRun"].(*wfclient.TaskRun)
26+
if !ok {
27+
return nil
28+
}
2329

24-
list := tui.NewList(
25-
"",
26-
command.PaginatedLoadCmd(ctx, workflowLoader.LoadTaskRunList, input),
27-
func(tr *wfclient.TaskRun) tui.ListItem {
28-
return taskrun.NewListItem(tr)
29-
},
30-
tui.WithOnSelect[*wfclient.TaskRun](onSelect),
31-
)
30+
return generateCommands(tr)
31+
}
3232

3333
return &TaskRunListView{
34-
list: list,
34+
table: tui.NewTable(
35+
taskrun.Columns(),
36+
command.LoadCmd(ctx, workflowLoader.LoadAllTaskRuns, input),
37+
func(tr *wfclient.TaskRun) btable.Row {
38+
return taskrun.TableRow(tr)
39+
},
40+
onSelect,
41+
),
3542
}
3643
}
3744

3845
func (v *TaskRunListView) Init() tea.Cmd {
39-
return v.list.Init()
46+
return v.table.Init()
4047
}
4148

4249
func (v *TaskRunListView) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
43-
_, cmd := v.list.Update(msg)
50+
_, cmd := v.table.Update(msg)
4451
return v, cmd
4552
}
4653

4754
func (v *TaskRunListView) View() string {
48-
return v.list.View()
55+
return v.table.View()
4956
}

0 commit comments

Comments
 (0)