adding functionality for editing priority; adding priority to the list of displayed issues; sorting the list of issues by their priority
This commit is contained in:
@@ -36,7 +36,7 @@ func (h HelpBar) shortHelp() string {
|
|||||||
styles.HighlightKey("↓/j") + " down",
|
styles.HighlightKey("↓/j") + " down",
|
||||||
styles.HighlightKey("pgup/pgdn") + " page",
|
styles.HighlightKey("pgup/pgdn") + " page",
|
||||||
styles.HighlightKey("a") + " add",
|
styles.HighlightKey("a") + " add",
|
||||||
styles.HighlightKey("e/d/s") + " edit",
|
styles.HighlightKey("e/d/s/p") + " edit",
|
||||||
styles.HighlightKey("x") + " delete",
|
styles.HighlightKey("x") + " delete",
|
||||||
styles.HighlightKey("q") + " quit",
|
styles.HighlightKey("q") + " quit",
|
||||||
styles.HighlightKey("?") + " help",
|
styles.HighlightKey("?") + " help",
|
||||||
@@ -75,8 +75,9 @@ func (h HelpBar) fullHelp() string {
|
|||||||
renderRow("pgup", "page up", "pgdn", "page down"),
|
renderRow("pgup", "page up", "pgdn", "page down"),
|
||||||
renderRow("b", "back to list", "a", "add issue"),
|
renderRow("b", "back to list", "a", "add issue"),
|
||||||
renderRow("e", "edit title", "d", "edit desc"),
|
renderRow("e", "edit title", "d", "edit desc"),
|
||||||
renderRow("s", "change status", "x", "delete issue"),
|
renderRow("s", "change status", "p", "change priority"),
|
||||||
renderRow("?", "help", "q", "quit"),
|
renderRow("x", "delete issue", "?", "help"),
|
||||||
|
renderRow("q", "quit", "", ""),
|
||||||
}
|
}
|
||||||
content := lipgloss.JoinVertical(lipgloss.Left, rows...)
|
content := lipgloss.JoinVertical(lipgloss.Left, rows...)
|
||||||
return lipgloss.NewStyle().
|
return lipgloss.NewStyle().
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package dashboard
|
package dashboard
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
"github.com/LazyBachelor/LazyPM/pkg/tui/styles"
|
"github.com/LazyBachelor/LazyPM/pkg/tui/styles"
|
||||||
"github.com/charmbracelet/bubbles/viewport"
|
"github.com/charmbracelet/bubbles/viewport"
|
||||||
@@ -53,6 +55,10 @@ func (i *IssueDetail) refreshContent() {
|
|||||||
styles.LabelStyle.Render("Status:") + styles.StatusStyle(string(i.issue.Status)).Render(string(i.issue.Status)),
|
styles.LabelStyle.Render("Status:") + styles.StatusStyle(string(i.issue.Status)).Render(string(i.issue.Status)),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
priorityRow := styles.RowStyle.Render(
|
||||||
|
styles.LabelStyle.Render("Priority:") + styles.ValueStyle.Render(fmt.Sprintf("%d", i.issue.Priority)),
|
||||||
|
)
|
||||||
|
|
||||||
descLabel := styles.LabelStyle.Render("Description:")
|
descLabel := styles.LabelStyle.Render("Description:")
|
||||||
descContent := styles.ValueStyle.Render(i.issue.Description)
|
descContent := styles.ValueStyle.Render(i.issue.Description)
|
||||||
|
|
||||||
@@ -61,6 +67,7 @@ func (i *IssueDetail) refreshContent() {
|
|||||||
idRow,
|
idRow,
|
||||||
typeRow,
|
typeRow,
|
||||||
statusRow,
|
statusRow,
|
||||||
|
priorityRow,
|
||||||
descLabel,
|
descLabel,
|
||||||
descContent,
|
descContent,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,7 +2,9 @@ package dashboard
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||||
"github.com/LazyBachelor/LazyPM/internal/service"
|
"github.com/LazyBachelor/LazyPM/internal/service"
|
||||||
@@ -53,6 +55,7 @@ func getTableColumns(width int) []TableColumn {
|
|||||||
{width: 20, label: "TITLE", key: "title"},
|
{width: 20, label: "TITLE", key: "title"},
|
||||||
{width: 15, label: "STATUS", key: "status"},
|
{width: 15, label: "STATUS", key: "status"},
|
||||||
{width: 10, label: "TYPE", key: "type"},
|
{width: 10, label: "TYPE", key: "type"},
|
||||||
|
{width: 15, label: "PRIORITY", key: "priority"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -139,6 +142,7 @@ func OpenAndInProgressOnly(issues []models.Issue) []models.Issue {
|
|||||||
out = append(out, issue)
|
out = append(out, issue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sortByPriorityDesc(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,9 +154,17 @@ func ClosedOnly(issues []models.Issue) []models.Issue {
|
|||||||
out = append(out, issue)
|
out = append(out, issue)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
sortByPriorityDesc(out)
|
||||||
return out
|
return out
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// sortByPriorityDesc sorts issues by priority, highest first.
|
||||||
|
func sortByPriorityDesc(issues []models.Issue) {
|
||||||
|
sort.Slice(issues, func(i, j int) bool {
|
||||||
|
return issues[i].Priority > issues[j].Priority
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func (l *IssueList) Update(msg tea.Msg) (tea.Cmd, bool) {
|
func (l *IssueList) Update(msg tea.Msg) (tea.Cmd, bool) {
|
||||||
var cmd tea.Cmd
|
var cmd tea.Cmd
|
||||||
oldIndex := l.list.Index()
|
oldIndex := l.list.Index()
|
||||||
@@ -311,6 +323,8 @@ func getColumnValue(col TableColumn, issue ListIssue) string {
|
|||||||
return string(issue.Issue.Status)
|
return string(issue.Issue.Status)
|
||||||
case "type":
|
case "type":
|
||||||
return string(issue.Issue.IssueType)
|
return string(issue.Issue.IssueType)
|
||||||
|
case "priority":
|
||||||
|
return fmt.Sprintf("%d", issue.Issue.Priority)
|
||||||
default:
|
default:
|
||||||
return ""
|
return ""
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ type DashboardKeyMap struct {
|
|||||||
EditTitle key.Binding
|
EditTitle key.Binding
|
||||||
EditDescription key.Binding
|
EditDescription key.Binding
|
||||||
ChangeStatus key.Binding
|
ChangeStatus key.Binding
|
||||||
|
ChangePriority key.Binding
|
||||||
AddIssue key.Binding
|
AddIssue key.Binding
|
||||||
DeleteIssue key.Binding
|
DeleteIssue key.Binding
|
||||||
}
|
}
|
||||||
@@ -61,6 +62,10 @@ var defaultDashboardKeyMap = DashboardKeyMap{
|
|||||||
key.WithKeys("s"),
|
key.WithKeys("s"),
|
||||||
key.WithHelp("s", "change status"),
|
key.WithHelp("s", "change status"),
|
||||||
),
|
),
|
||||||
|
ChangePriority: key.NewBinding(
|
||||||
|
key.WithKeys("p"),
|
||||||
|
key.WithHelp("p", "change priority"),
|
||||||
|
),
|
||||||
AddIssue: key.NewBinding(
|
AddIssue: key.NewBinding(
|
||||||
key.WithKeys("a"),
|
key.WithKeys("a"),
|
||||||
key.WithHelp("a", "add issue"),
|
key.WithHelp("a", "add issue"),
|
||||||
@@ -89,24 +94,28 @@ func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
|
|||||||
d.issueDetail.ScrollUp(1)
|
d.issueDetail.ScrollUp(1)
|
||||||
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown):
|
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown):
|
||||||
d.issueDetail.ScrollDown(1)
|
d.issueDetail.ScrollDown(1)
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.EditTitle):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.EditTitle):
|
||||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
d.startEditTitle(selected)
|
d.startEditTitle(selected)
|
||||||
cmd = d.titleInput.Focus()
|
cmd = d.titleInput.Focus()
|
||||||
}
|
}
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.EditDescription):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.EditDescription):
|
||||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
d.startEditDescription(selected)
|
d.startEditDescription(selected)
|
||||||
cmd = d.descriptionInput.Focus()
|
cmd = d.descriptionInput.Focus()
|
||||||
}
|
}
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.ChangeStatus):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.ChangeStatus):
|
||||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
d.startChooseStatus(selected)
|
d.startChooseStatus(selected)
|
||||||
}
|
}
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.AddIssue):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.ChangePriority):
|
||||||
|
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||||
|
d.startChoosePriority(selected)
|
||||||
|
}
|
||||||
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.AddIssue):
|
||||||
d.startCreateIssue()
|
d.startCreateIssue()
|
||||||
cmd = d.createTitleInput.Focus()
|
cmd = d.createTitleInput.Focus()
|
||||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.confirmingDelete && key.Matches(msg, d.keyMap.DeleteIssue):
|
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.DeleteIssue):
|
||||||
fl := d.FocusedIssueList()
|
fl := d.FocusedIssueList()
|
||||||
if selected := fl.SelectedItem(); selected.ID != "" {
|
if selected := fl.SelectedItem(); selected.ID != "" {
|
||||||
d.startConfirmDelete(selected.ID, fl.Index())
|
d.startConfirmDelete(selected.ID, fl.Index())
|
||||||
|
|||||||
@@ -24,9 +24,9 @@ type Model struct {
|
|||||||
svc *service.Services
|
svc *service.Services
|
||||||
width int
|
width int
|
||||||
height int
|
height int
|
||||||
focusedWindow int // 0 = main (display issues), 1 = closed issues
|
focusedWindow int // 0 = main (display issues), 1 = closed issues
|
||||||
focusedPaneMain int // 0 = list, 1 = detail
|
focusedPaneMain int // 0 = list, 1 = detail
|
||||||
focusedPaneClosed int
|
focusedPaneClosed int
|
||||||
editingTitle bool // true while we are editing a title
|
editingTitle bool // true while we are editing a title
|
||||||
titleInput textinput.Model
|
titleInput textinput.Model
|
||||||
editingIssueID string
|
editingIssueID string
|
||||||
@@ -41,8 +41,10 @@ type Model struct {
|
|||||||
deleteConfirmID string
|
deleteConfirmID string
|
||||||
deleteConfirmIndex int
|
deleteConfirmIndex int
|
||||||
|
|
||||||
choosingStatus bool
|
choosingStatus bool
|
||||||
statusIssueID string
|
statusIssueID string
|
||||||
|
choosingPriority bool
|
||||||
|
priorityIssueID string
|
||||||
feedbackChan chan task.ValidationFeedback
|
feedbackChan chan task.ValidationFeedback
|
||||||
quitChan chan bool
|
quitChan chan bool
|
||||||
currentFeedback task.ValidationFeedback
|
currentFeedback task.ValidationFeedback
|
||||||
@@ -125,6 +127,11 @@ func (m *Model) startChooseStatus(selected ListIssue) {
|
|||||||
m.statusIssueID = selected.ID
|
m.statusIssueID = selected.ID
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *Model) startChoosePriority(selected ListIssue) {
|
||||||
|
m.choosingPriority = true
|
||||||
|
m.priorityIssueID = selected.ID
|
||||||
|
}
|
||||||
|
|
||||||
func (m *Model) Init() tea.Cmd {
|
func (m *Model) Init() tea.Cmd {
|
||||||
return m.listenForValidation()
|
return m.listenForValidation()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,11 @@ type issueStatusUpdatedMsg struct {
|
|||||||
Err error
|
Err error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type issuePriorityUpdatedMsg struct {
|
||||||
|
IssueID string
|
||||||
|
Err error
|
||||||
|
}
|
||||||
|
|
||||||
type selectIssueMsg struct {
|
type selectIssueMsg struct {
|
||||||
IssueID string
|
IssueID string
|
||||||
}
|
}
|
||||||
@@ -63,6 +68,14 @@ func updateIssueStatusCmd(svc *service.Services, issueID, status string) tea.Cmd
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateIssuePriorityCmd(svc *service.Services, issueID string, priority int) tea.Cmd {
|
||||||
|
return func() tea.Msg {
|
||||||
|
updates := map[string]interface{}{"priority": priority}
|
||||||
|
err := svc.Beads.UpdateIssue(context.Background(), issueID, updates, "tui")
|
||||||
|
return issuePriorityUpdatedMsg{IssueID: issueID, Err: err}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func createIssueCmd(svc *service.Services, title string) tea.Cmd {
|
func createIssueCmd(svc *service.Services, title string) tea.Cmd {
|
||||||
return func() tea.Msg {
|
return func() tea.Msg {
|
||||||
issue := &models.Issue{
|
issue := &models.Issue{
|
||||||
@@ -129,6 +142,14 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
return m, m.refreshIssueListsAndSelectIssue(msg.IssueID)
|
return m, m.refreshIssueListsAndSelectIssue(msg.IssueID)
|
||||||
|
|
||||||
|
case issuePriorityUpdatedMsg:
|
||||||
|
m.choosingPriority = false
|
||||||
|
m.priorityIssueID = ""
|
||||||
|
if msg.Err != nil {
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
return m, m.refreshIssueListsAndSelectIssue(msg.IssueID)
|
||||||
|
|
||||||
case selectIssueMsg:
|
case selectIssueMsg:
|
||||||
m.issueList.SelectIssueID(msg.IssueID)
|
m.issueList.SelectIssueID(msg.IssueID)
|
||||||
m.closedIssueList.SelectIssueID(msg.IssueID)
|
m.closedIssueList.SelectIssueID(msg.IssueID)
|
||||||
@@ -256,6 +277,23 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.choosingPriority {
|
||||||
|
switch msg.String() {
|
||||||
|
case "0", "1", "2", "3", "4":
|
||||||
|
issueID := m.priorityIssueID
|
||||||
|
priority := int(msg.String()[0] - '0')
|
||||||
|
m.choosingPriority = false
|
||||||
|
m.priorityIssueID = ""
|
||||||
|
return m, updateIssuePriorityCmd(m.svc, issueID, priority)
|
||||||
|
case "esc":
|
||||||
|
m.choosingPriority = false
|
||||||
|
m.priorityIssueID = ""
|
||||||
|
return m, nil
|
||||||
|
default:
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if m.creatingIssue {
|
if m.creatingIssue {
|
||||||
if msg.String() == "enter" {
|
if msg.String() == "enter" {
|
||||||
title := m.createTitleInput.Value()
|
title := m.createTitleInput.Value()
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
|
|
||||||
func (m *Model) View() string {
|
func (m *Model) View() string {
|
||||||
if m.width == 0 || m.height == 0 {
|
if m.width == 0 || m.height == 0 {
|
||||||
|
// if there is no space just print a loading message
|
||||||
return "Loading..."
|
return "Loading..."
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,6 +126,20 @@ func (m *Model) View() string {
|
|||||||
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, statusBox)
|
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, statusBox)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if m.choosingPriority {
|
||||||
|
priorityContent := lipgloss.JoinVertical(lipgloss.Left,
|
||||||
|
styles.LabelStyle.Render("Change priority for "+m.priorityIssueID+":"),
|
||||||
|
lipgloss.NewStyle().Foreground(styles.FaintText).Render("0 = lowest 1 = low 2 = medium 3 = high 4 = highest"),
|
||||||
|
lipgloss.NewStyle().Foreground(styles.FaintText).Render("Esc = cancel"),
|
||||||
|
)
|
||||||
|
priorityBoxWidth := min(60, m.width-4)
|
||||||
|
priorityBox := styles.ContainerStyle.
|
||||||
|
Width(priorityBoxWidth).
|
||||||
|
BorderForeground(styles.PrimaryBorder).
|
||||||
|
Render(priorityContent)
|
||||||
|
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, priorityBox)
|
||||||
|
}
|
||||||
|
|
||||||
return mainView
|
return mainView
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user