Skip to content

Commit cfc074d

Browse files
committed
feat(tui): add edit and delete collection functionality
1 parent 6657dff commit cfc074d

4 files changed

Lines changed: 212 additions & 39 deletions

File tree

internal/tui/app/model.go

Lines changed: 51 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
package app
22

33
import (
4+
"context"
5+
46
tea "github.com/charmbracelet/bubbletea"
7+
"github.com/maniac-en/req/internal/log"
58
"github.com/maniac-en/req/internal/tui/views"
69
)
710

@@ -14,12 +17,13 @@ const (
1417
)
1518

1619
type Model struct {
17-
ctx *Context
18-
mode ViewMode
19-
collectionsView views.CollectionsView
20-
addCollectionView views.AddCollectionView
21-
width int
22-
height int
20+
ctx *Context
21+
mode ViewMode
22+
collectionsView views.CollectionsView
23+
addCollectionView views.AddCollectionView
24+
editCollectionView views.EditCollectionView
25+
width int
26+
height int
2327
}
2428

2529
func NewModel(ctx *Context) Model {
@@ -28,6 +32,7 @@ func NewModel(ctx *Context) Model {
2832
mode: CollectionsViewMode,
2933
collectionsView: views.NewCollectionsView(ctx.Collections),
3034
addCollectionView: views.NewAddCollectionView(ctx.Collections),
35+
// editCollectionView will be created on demand
3136
}
3237
}
3338

@@ -42,7 +47,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
4247
case tea.KeyMsg:
4348
// Handle global keybinds only when not in filtering mode
4449
isFiltering := m.mode == CollectionsViewMode && m.collectionsView.IsFiltering()
45-
50+
4651
if !isFiltering {
4752
switch msg.String() {
4853
case "ctrl+c", "q":
@@ -57,6 +62,30 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
5762
m.mode = AddCollectionViewMode
5863
return m, nil
5964
}
65+
case "e":
66+
if m.mode == CollectionsViewMode {
67+
// Get selected collection and switch to edit mode
68+
if selectedItem := m.collectionsView.GetSelectedItem(); selectedItem != nil {
69+
m.mode = EditCollectionViewMode
70+
m.editCollectionView = views.NewEditCollectionView(m.ctx.Collections, *selectedItem)
71+
return m, nil
72+
} else {
73+
log.Error("issue getting currently selected collection")
74+
}
75+
}
76+
case "x":
77+
if m.mode == CollectionsViewMode {
78+
// Delete selected collection
79+
if selectedItem := m.collectionsView.GetSelectedItem(); selectedItem != nil {
80+
return m, func() tea.Msg {
81+
err := m.ctx.Collections.Delete(context.Background(), selectedItem.ID)
82+
if err != nil {
83+
return views.CollectionDeleteErrorMsg{Err: err}
84+
}
85+
return views.CollectionDeletedMsg{ID: selectedItem.ID}
86+
}
87+
}
88+
}
6089
}
6190
}
6291
case tea.WindowSizeMsg:
@@ -66,6 +95,16 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
6695
m.mode = CollectionsViewMode
6796
// Reload collections to show any changes
6897
return m, m.collectionsView.Init()
98+
case views.EditCollectionMsg:
99+
m.mode = EditCollectionViewMode
100+
m.editCollectionView = views.NewEditCollectionView(m.ctx.Collections, msg.Collection)
101+
return m, nil
102+
case views.CollectionDeletedMsg:
103+
// Collection deleted, reload collections view
104+
return m, m.collectionsView.Init()
105+
case views.CollectionDeleteErrorMsg:
106+
// Delete failed, just continue
107+
return m, nil
69108
}
70109

71110
// Forward messages to the appropriate view
@@ -74,8 +113,10 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
74113
m.collectionsView, cmd = m.collectionsView.Update(msg)
75114
case AddCollectionViewMode:
76115
m.addCollectionView, cmd = m.addCollectionView.Update(msg)
116+
case EditCollectionViewMode:
117+
m.editCollectionView, cmd = m.editCollectionView.Update(msg)
77118
}
78-
119+
79120
return m, cmd
80121
}
81122

@@ -85,8 +126,9 @@ func (m Model) View() string {
85126
return m.collectionsView.View()
86127
case AddCollectionViewMode:
87128
return m.addCollectionView.View()
129+
case EditCollectionViewMode:
130+
return m.editCollectionView.View()
88131
default:
89132
return m.collectionsView.View()
90133
}
91134
}
92-

internal/tui/views/add_collection.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -130,15 +130,15 @@ type CollectionUpdateErrorMsg struct {
130130
}
131131

132132
type CollectionDeletedMsg struct {
133-
id int64
133+
ID int64
134134
}
135135

136136
type CollectionDeleteErrorMsg struct {
137-
err error
137+
Err error
138138
}
139139

140140
type BackToCollectionsMsg struct{}
141141

142142
type EditCollectionMsg struct {
143-
collection collections.CollectionEntity
143+
Collection collections.CollectionEntity
144144
}

internal/tui/views/collections.go

Lines changed: 39 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,22 @@ import (
1111
)
1212

1313
type CollectionsView struct {
14-
layout components.Layout
15-
list components.PaginatedList
14+
layout components.Layout
15+
list components.PaginatedList
1616
collectionsManager *collections.CollectionsManager
17-
width int
18-
height int
19-
initialized bool
20-
17+
width int
18+
height int
19+
initialized bool
20+
2121
// Backend pagination state
22-
currentPage int
23-
pageSize int
24-
pagination crud.PaginationMetadata
22+
currentPage int
23+
pageSize int
24+
pagination crud.PaginationMetadata
2525
}
2626

2727
func NewCollectionsView(collectionsManager *collections.CollectionsManager) CollectionsView {
2828
return CollectionsView{
29-
layout: components.NewLayout(),
29+
layout: components.NewLayout(),
3030
collectionsManager: collectionsManager,
3131
}
3232
}
@@ -72,43 +72,43 @@ func (v CollectionsView) Update(msg tea.Msg) (CollectionsView, tea.Cmd) {
7272
v.width = msg.Width
7373
v.height = msg.Height
7474
v.layout.SetSize(v.width, v.height)
75-
75+
7676
// Only set list size if it's been initialized
7777
if v.initialized {
7878
contentHeight := v.height - 4 // Account for header/footer
7979
v.list.SetSize(v.width-4, contentHeight)
8080
}
81-
81+
8282
case collectionsLoaded:
8383
items := make([]components.ListItem, len(msg.collections))
8484
for i, collection := range msg.collections {
8585
items[i] = components.NewCollectionItem(collection)
8686
}
87-
87+
8888
// Update pagination state
8989
v.currentPage = msg.currentPage
9090
v.pageSize = msg.pageSize
9191
v.pagination = msg.pagination
92-
92+
9393
// Create list with pagination info in title
9494
title := fmt.Sprintf("Collections (Page %d/%d)", v.currentPage, v.pagination.TotalPages)
9595
v.list = components.NewPaginatedList(items, title)
96-
96+
9797
if v.width > 0 && v.height > 0 {
9898
contentHeight := v.height - 4
9999
v.list.SetSize(v.width-4, contentHeight)
100100
}
101101
v.initialized = true
102-
102+
103103
case collectionsLoadError:
104104
// Handle error - for now just mark as initialized
105105
v.initialized = true
106-
106+
107107
case tea.KeyMsg:
108108
if !v.initialized {
109109
break
110110
}
111-
111+
112112
// Handle pagination keys only when not filtering
113113
if !v.list.IsFiltering() {
114114
switch msg.String() {
@@ -130,23 +130,36 @@ func (v CollectionsView) Update(msg tea.Msg) (CollectionsView, tea.Cmd) {
130130
return v, nil
131131
}
132132
}
133-
134-
// Always forward keys to the list (handles filtering and navigation)
133+
134+
// Forward keys to the list (handles filtering and navigation)
135135
v.list, cmd = v.list.Update(msg)
136-
136+
137137
default:
138138
if v.initialized {
139139
v.list, cmd = v.list.Update(msg)
140140
}
141141
}
142-
142+
143143
return v, cmd
144144
}
145145

146146
func (v CollectionsView) IsFiltering() bool {
147147
return v.initialized && v.list.IsFiltering()
148148
}
149149

150+
func (v CollectionsView) GetSelectedItem() *collections.CollectionEntity {
151+
if !v.initialized {
152+
return nil
153+
}
154+
if selectedItem := v.list.SelectedItem(); selectedItem != nil {
155+
if collectionItem, ok := selectedItem.(components.CollectionItem); ok {
156+
collection := collectionItem.GetCollection()
157+
return &collection
158+
}
159+
}
160+
return nil
161+
}
162+
150163
func (v CollectionsView) View() string {
151164
if !v.initialized {
152165
return v.layout.FullView(
@@ -157,20 +170,19 @@ func (v CollectionsView) View() string {
157170
}
158171

159172
content := v.list.View()
160-
173+
161174
// Build instructions with pagination and filter info
162-
instructions := "↑↓: navigate • /: filter • enter: edit • d: delete • q: quit"
175+
instructions := "↑↓: navigate • /: filter • e: edit • x: delete • q: quit"
163176
if !v.list.IsFiltering() {
164-
instructions = "↑↓: navigate • a: add • /: filter • enter: edit • d: delete • q: quit"
177+
instructions = "↑↓: navigate • a: add • /: filter • e: edit • x: delete • q: quit"
165178
}
166179
if v.pagination.TotalPages > 1 && !v.list.IsFiltering() {
167180
instructions += fmt.Sprintf(" • p/n: prev/next page (%d/%d)", v.currentPage, v.pagination.TotalPages)
168181
}
169-
182+
170183
return v.layout.FullView(
171184
"Collections",
172185
content,
173186
instructions,
174187
)
175188
}
176-

0 commit comments

Comments
 (0)