From 19b32dca1bfef0b8eafa750e5c79563a8c483a9b Mon Sep 17 00:00:00 2001 From: Robin Olsen Date: Tue, 10 Feb 2026 20:11:36 +0100 Subject: [PATCH] add dashboard --- pkg/tui/views/dashboard/keys.go | 30 ++++-------- pkg/tui/views/dashboard/model.go | 73 +++++++++++++++++++++++------ pkg/tui/views/dashboard/update.go | 62 ++++++++----------------- pkg/tui/views/dashboard/view.go | 77 ++++++++----------------------- pkg/tui/views/views.go | 2 +- 5 files changed, 106 insertions(+), 138 deletions(-) diff --git a/pkg/tui/views/dashboard/keys.go b/pkg/tui/views/dashboard/keys.go index 6a832ea..dfdeb03 100644 --- a/pkg/tui/views/dashboard/keys.go +++ b/pkg/tui/views/dashboard/keys.go @@ -41,34 +41,22 @@ var defaultDashboardKeyMap = DashboardKeyMap{ ), } -func (m DashboardKeyMap) ShortHelp() []key.Binding { - return []key.Binding{m.ScrollDown, m.ScrollUp, m.Quit, m.Help} -} - -func (m DashboardKeyMap) FullHelp() [][]key.Binding { - return [][]key.Binding{ - {m.SelectIssue, m.BackToList}, - {m.ScrollUp, m.ScrollDown}, - {m.Help, m.Quit}, - } -} - func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd { var cmd tea.Cmd switch { case key.Matches(msg, d.keyMap.Help): - d.help.ShowAll = !d.help.ShowAll + d.helpBar.ToggleHelp() case key.Matches(msg, d.keyMap.Quit): return tea.Quit - case !d.focusedOnIssue && key.Matches(msg, d.keyMap.SelectIssue): - d.focusedOnIssue = true - case d.focusedOnIssue && key.Matches(msg, d.keyMap.BackToList): - d.focusedOnIssue = false - case d.focusedOnIssue && key.Matches(msg, d.keyMap.ScrollUp): - d.issueView.Viewport.ScrollUp(1) - case d.focusedOnIssue && key.Matches(msg, d.keyMap.ScrollDown): - d.issueView.Viewport.ScrollDown(1) + case d.IsFocusedOnList() && key.Matches(msg, d.keyMap.SelectIssue): + d.FocusDetail() + case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.BackToList): + d.FocusList() + case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollUp): + d.issueDetail.ScrollUp(1) + case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown): + d.issueDetail.ScrollDown(1) } return cmd diff --git a/pkg/tui/views/dashboard/model.go b/pkg/tui/views/dashboard/model.go index d327bd1..702cb8f 100644 --- a/pkg/tui/views/dashboard/model.go +++ b/pkg/tui/views/dashboard/model.go @@ -1,26 +1,69 @@ package dashboard import ( - "github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/service" - "github.com/LazyBachelor/LazyPM/pkg/tui/views/issue" - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/list" + tea "github.com/charmbracelet/bubbletea" ) type Model struct { - help help.Model - issueView issue.Model - issueList list.Model - keyMap DashboardKeyMap - svc *service.Services - focusedOnIssue bool + header Header + issueList IssueList + issueDetail IssueDetail + helpBar HelpBar + keyMap DashboardKeyMap + svc *service.Services + width int + height int + focusedPane int // 0 = list, 1 = detail } -type ListIssue struct { - models.Issue +func NewDashboard(svc *service.Services) *Model { + m := &Model{ + header: NewHeader("Project Manager Dashboard"), + keyMap: defaultDashboardKeyMap, + svc: svc, + width: 80, + height: 24, + focusedPane: 0, + } + + m.issueList = NewIssueList(svc, 0, 0) + m.issueDetail = NewIssueDetail() + m.helpBar = NewHelpBar(m.keyMap) + + if selected := m.issueList.SelectedItem(); selected.ID != "" { + m.issueDetail.SetIssue(selected.Issue) + } + + return m } -func (i ListIssue) Title() string { return i.Issue.Title } -func (i ListIssue) Description() string { return i.Issue.Description } -func (i ListIssue) FilterValue() string { return i.Issue.ID + " " + i.Issue.Title } +func (m *Model) Init() tea.Cmd { + return nil +} + +func (m *Model) IsFocusedOnList() bool { + return m.focusedPane == 0 +} + +func (m *Model) IsFocusedOnDetail() bool { + return m.focusedPane == 1 +} + +func (m *Model) FocusList() { + m.focusedPane = 0 + m.issueDetail.SetFocused(false) +} + +func (m *Model) FocusDetail() { + m.focusedPane = 1 + m.issueDetail.SetFocused(true) +} + +func (m *Model) ToggleFocus() { + if m.focusedPane == 0 { + m.FocusDetail() + } else { + m.FocusList() + } +} diff --git a/pkg/tui/views/dashboard/update.go b/pkg/tui/views/dashboard/update.go index b8dd033..b2f7d9a 100644 --- a/pkg/tui/views/dashboard/update.go +++ b/pkg/tui/views/dashboard/update.go @@ -1,60 +1,34 @@ package dashboard import ( - "fmt" - - "github.com/LazyBachelor/LazyPM/pkg/tui/styles" - "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" ) -func (d Model) Update(m tea.Msg) (tea.Model, tea.Cmd) { - switch msg := m.(type) { +func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { + switch msg := msg.(type) { case tea.KeyMsg: - if d.issueList.FilterState() == list.Filtering { - break + if m.issueList.FilterState() == 1 { + cmd, _ := m.issueList.Update(msg) + return m, cmd } - cmd := d.handleKeyMsg(msg) + + cmd := m.handleKeyMsg(msg) if cmd != nil { - return d, cmd + return m, cmd } + case tea.WindowSizeMsg: - d.updateSizes(msg.Width, msg.Height) + m.width = msg.Width + m.height = msg.Height + return m, nil } - var cmd tea.Cmd - oldIndex := d.issueList.Index() - d.issueList, cmd = d.issueList.Update(m) - - if d.issueList.Index() != oldIndex { - d.updateIssueView() + cmd, changed := m.issueList.Update(msg) + if changed { + if selected := m.issueList.SelectedItem(); selected.ID != "" { + m.issueDetail.SetIssue(selected.Issue) + } } - return d, cmd -} - -func (d *Model) updateIssueView() { - if item, ok := d.issueList.SelectedItem().(ListIssue); ok { - d.issueView.ID = item.ID - d.issueView.Title = item.Issue.Title - d.issueView.Description = item.Issue.Description - d.issueView.Status = string(item.Issue.Status) - d.issueView.IssueType = string(item.Issue.IssueType) - - content := fmt.Sprintf("ID: %s\nTitle: %s\nDescription: %s\nStatus: %s\nType: %s", - d.issueView.ID, d.issueView.Title, d.issueView.Description, d.issueView.Status, d.issueView.IssueType) - - d.issueView.Viewport.SetContent(content) - } -} - -func (d *Model) updateSizes(width, height int) { - listWidth := width / 2 - issueWidth := width - listWidth - - w, h := styles.AppStyle.GetFrameSize() - d.issueList.SetSize(listWidth-w, height-h) - d.issueView.SetSize(issueWidth, height-h) - d.issueView.Viewport.Width = issueWidth - d.issueView.Viewport.Height = height - h + return m, cmd } diff --git a/pkg/tui/views/dashboard/view.go b/pkg/tui/views/dashboard/view.go index 6700adf..18d207e 100644 --- a/pkg/tui/views/dashboard/view.go +++ b/pkg/tui/views/dashboard/view.go @@ -1,73 +1,36 @@ package dashboard import ( - "context" - - "github.com/charmbracelet/bubbles/help" - "github.com/charmbracelet/bubbles/list" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" - - "github.com/LazyBachelor/LazyPM/internal/service" "github.com/LazyBachelor/LazyPM/pkg/tui/styles" - "github.com/LazyBachelor/LazyPM/pkg/tui/views/issue" + "github.com/charmbracelet/lipgloss" ) -func NewDashboard(svc *service.Services) Model { - - issues, err := svc.Beads.AllIssues(context.Background()) - - listIssues := []ListIssue{} - for _, issue := range issues { - listIssues = append(listIssues, ListIssue{Issue: issue}) +func (m *Model) View() string { + if m.width == 0 || m.height == 0 { + return "Loading..." } - if err != nil { - listIssues = []ListIssue{} - } + m.helpBar.SetWidth(m.width) - items := make([]list.Item, len(listIssues)) - for i, issue := range listIssues { - items[i] = issue - } + header := m.header.View(m.width) + headerHeight := m.header.Height() - issueList := list.New(items, IssueListDelegate{}, 0, 0) + bottomView := m.helpBar.View() + bottomHeight := m.helpBar.Height() - issueList.Title = "Issues" - issueList.Styles.Title = styles.TitleStyle - issueList.SetShowHelp(false) + contentHeight := m.height - headerHeight - bottomHeight - issueView := issue.NewIssueView(issue.Model{}) + totalContentWidth := m.width - 1 + listWidth := totalContentWidth * styles.ListViewRatio / 100 + detailWidth := totalContentWidth - listWidth - m := Model{ - help: help.New(), - issueView: issueView, - issueList: issueList, - keyMap: defaultDashboardKeyMap, - svc: svc, - } + m.issueList.SetSize(listWidth, contentHeight) + m.issueDetail.SetSize(detailWidth, contentHeight) - m.updateIssueView() + listView := m.issueList.View() + detailView := m.issueDetail.View() - return m -} - -func (d Model) Init() tea.Cmd { - return nil -} - -func (d Model) View() string { - issueView := d.issueView.View() - - if d.focusedOnIssue { - issueView = styles.FocusedIssueStyle.Render(issueView) - } else { - issueView = styles.IssueStyle.Render(issueView) - } - - help := d.help.View(d.keyMap) - - str := lipgloss.JoinHorizontal(lipgloss.Left, styles.AppStyle.Render(d.issueList.View()), issueView) + "\n" + help - - return str + content := lipgloss.JoinHorizontal(lipgloss.Left, listView, detailView) + + return lipgloss.JoinVertical(lipgloss.Left, header, content, bottomView) } diff --git a/pkg/tui/views/views.go b/pkg/tui/views/views.go index a246942..c7bc138 100644 --- a/pkg/tui/views/views.go +++ b/pkg/tui/views/views.go @@ -5,6 +5,6 @@ import ( "github.com/LazyBachelor/LazyPM/pkg/tui/views/dashboard" ) -func NewDashboardView(svc *service.Services) dashboard.Model { +func NewDashboardView(svc *service.Services) *dashboard.Model { return dashboard.NewDashboard(svc) }