refactor: mover apps TUI de fn_operations/ a apps/
Separa aplicaciones ejecutables (docker_tui, pipeline_launcher) de la librería fn_operations. La carpeta apps/ contiene módulos Go independientes, fn_operations/ queda como librería pura de models/store/operations. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package views
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/lucasdataproyects/devfactory/tui"
|
||||
)
|
||||
|
||||
type imagesState int
|
||||
|
||||
const (
|
||||
imagesLoading imagesState = iota
|
||||
imagesList
|
||||
imagesAction
|
||||
)
|
||||
|
||||
type imagesLoadedMsg []DockerImage
|
||||
type imagesActionMsg struct{ output string; err error }
|
||||
|
||||
type ImagesModel struct {
|
||||
state imagesState
|
||||
list tui.FilteredListModel
|
||||
spinner tui.SpinnerModel
|
||||
styles tui.Styles
|
||||
images []DockerImage
|
||||
err error
|
||||
}
|
||||
|
||||
func NewImagesModel(styles tui.Styles) ImagesModel {
|
||||
return ImagesModel{
|
||||
state: imagesLoading,
|
||||
list: tui.NewFilteredList(nil, "Filter images..."),
|
||||
spinner: tui.NewSpinner("Loading images..."),
|
||||
styles: styles,
|
||||
}
|
||||
}
|
||||
|
||||
func (m ImagesModel) Init() tea.Cmd {
|
||||
return tea.Batch(m.spinner.Init(), loadImages)
|
||||
}
|
||||
|
||||
func loadImages() tea.Msg {
|
||||
images, err := ListImages()
|
||||
if err != nil {
|
||||
return imagesLoadedMsg(nil)
|
||||
}
|
||||
return imagesLoadedMsg(images)
|
||||
}
|
||||
|
||||
func (m ImagesModel) Update(msg tea.Msg) (ImagesModel, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case imagesLoadedMsg:
|
||||
m.images = []DockerImage(msg)
|
||||
items := make([]tui.ListItem, len(m.images))
|
||||
for i, img := range m.images {
|
||||
tag := img.Tag
|
||||
if tag == "" {
|
||||
tag = "latest"
|
||||
}
|
||||
items[i] = tui.ListItem{
|
||||
Title: fmt.Sprintf("%s:%s", img.Repository, tag),
|
||||
Description: fmt.Sprintf("Size: %s — %s", img.Size, img.ID[:12]),
|
||||
Value: img,
|
||||
}
|
||||
}
|
||||
m.list.SetItems(items)
|
||||
m.state = imagesList
|
||||
return m, nil
|
||||
|
||||
case imagesActionMsg:
|
||||
if msg.err != nil {
|
||||
m.err = msg.err
|
||||
}
|
||||
m.state = imagesList
|
||||
return m, loadImages
|
||||
|
||||
case tea.KeyMsg:
|
||||
if m.state == imagesList {
|
||||
switch msg.String() {
|
||||
case "r":
|
||||
m.state = imagesLoading
|
||||
return m, tea.Batch(m.spinner.Init(), loadImages)
|
||||
case "d", "delete":
|
||||
if item := m.list.SelectedItem(); item != nil {
|
||||
img := item.Value.(DockerImage)
|
||||
m.state = imagesAction
|
||||
return m, func() tea.Msg {
|
||||
err := RemoveImage(img.ID)
|
||||
return imagesActionMsg{output: "Removed", err: err}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
switch m.state {
|
||||
case imagesLoading, imagesAction:
|
||||
var model tea.Model
|
||||
model, cmd = m.spinner.Update(msg)
|
||||
m.spinner = model.(tui.SpinnerModel)
|
||||
case imagesList:
|
||||
var model tea.Model
|
||||
model, cmd = m.list.Update(msg)
|
||||
m.list = model.(tui.FilteredListModel)
|
||||
}
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// HandleBack retrocede un nivel. Retorna true si ya estaba en estado base.
|
||||
func (m *ImagesModel) HandleBack() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (m ImagesModel) View() string {
|
||||
switch m.state {
|
||||
case imagesLoading, imagesAction:
|
||||
return m.spinner.View()
|
||||
case imagesList:
|
||||
if len(m.images) == 0 {
|
||||
return m.styles.Muted.Render("No images found. Press 'r' to refresh.")
|
||||
}
|
||||
help := m.styles.Muted.Render(" d: remove │ r: refresh │ /: filter")
|
||||
view := m.list.View() + "\n" + help
|
||||
if m.err != nil {
|
||||
view += "\n" + m.styles.Error.Render(fmt.Sprintf(" Error: %v", m.err))
|
||||
}
|
||||
return view
|
||||
}
|
||||
return ""
|
||||
}
|
||||
Reference in New Issue
Block a user