using codenames for priority levels mapped to digits
This commit is contained in:
@@ -36,7 +36,7 @@ func (h HelpBar) shortHelp() string {
|
||||
styles.HighlightKey("↓/j") + " down",
|
||||
styles.HighlightKey("pgup/pgdn") + " page",
|
||||
styles.HighlightKey("a") + " add",
|
||||
styles.HighlightKey("e/d/s/p") + " edit",
|
||||
styles.HighlightKey("e/d/s/p/t") + " edit",
|
||||
styles.HighlightKey("x") + " delete",
|
||||
styles.HighlightKey("q") + " quit",
|
||||
styles.HighlightKey("?") + " help",
|
||||
@@ -76,8 +76,8 @@ func (h HelpBar) fullHelp() string {
|
||||
renderRow("b", "back to list", "a", "add issue"),
|
||||
renderRow("e", "edit title", "d", "edit desc"),
|
||||
renderRow("s", "change status", "p", "change priority"),
|
||||
renderRow("x", "delete issue", "?", "help"),
|
||||
renderRow("q", "quit", "", ""),
|
||||
renderRow("t", "change type", "x", "delete issue"),
|
||||
renderRow("?", "help", "q", "quit"),
|
||||
}
|
||||
content := lipgloss.JoinVertical(lipgloss.Left, rows...)
|
||||
return lipgloss.NewStyle().
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package dashboard
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/LazyBachelor/LazyPM/internal/models"
|
||||
"github.com/LazyBachelor/LazyPM/pkg/tui/styles"
|
||||
"github.com/charmbracelet/bubbles/viewport"
|
||||
@@ -56,7 +54,7 @@ func (i *IssueDetail) refreshContent() {
|
||||
)
|
||||
|
||||
priorityRow := styles.RowStyle.Render(
|
||||
styles.LabelStyle.Render("Priority:") + styles.ValueStyle.Render(fmt.Sprintf("%d", i.issue.Priority)),
|
||||
styles.LabelStyle.Render("Priority:") + styles.ValueStyle.Render(priorityCodeName(i.issue.Priority)),
|
||||
)
|
||||
|
||||
descLabel := styles.LabelStyle.Render("Description:")
|
||||
|
||||
@@ -158,8 +158,8 @@ func ClosedOnly(issues []models.Issue) []models.Issue {
|
||||
return out
|
||||
}
|
||||
|
||||
// sortByPriorityDesc sorts issues by priority, highest first.
|
||||
func sortByPriorityDesc(issues []models.Issue) {
|
||||
// sorts issues by priority, highest first.
|
||||
sort.Slice(issues, func(i, j int) bool {
|
||||
return issues[i].Priority > issues[j].Priority
|
||||
})
|
||||
@@ -313,6 +313,21 @@ func renderRow(issue ListIssue, isSelected bool, cols []TableColumn) string {
|
||||
return lipgloss.JoinHorizontal(lipgloss.Left, parts...)
|
||||
}
|
||||
|
||||
var priorityCodeNames = map[int]string{
|
||||
0: "irrelevant",
|
||||
1: "low",
|
||||
2: "normal",
|
||||
3: "high",
|
||||
4: "critical",
|
||||
}
|
||||
|
||||
func priorityCodeName(priority int) string {
|
||||
if name, ok := priorityCodeNames[priority]; ok {
|
||||
return name
|
||||
}
|
||||
return fmt.Sprintf("%d", priority)
|
||||
}
|
||||
|
||||
func getColumnValue(col TableColumn, issue ListIssue) string {
|
||||
switch col.key {
|
||||
case "id":
|
||||
@@ -324,7 +339,7 @@ func getColumnValue(col TableColumn, issue ListIssue) string {
|
||||
case "type":
|
||||
return string(issue.Issue.IssueType)
|
||||
case "priority":
|
||||
return fmt.Sprintf("%d", issue.Issue.Priority)
|
||||
return priorityCodeName(issue.Issue.Priority)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ type DashboardKeyMap struct {
|
||||
EditDescription key.Binding
|
||||
ChangeStatus key.Binding
|
||||
ChangePriority key.Binding
|
||||
ChangeType key.Binding
|
||||
AddIssue key.Binding
|
||||
DeleteIssue key.Binding
|
||||
}
|
||||
@@ -66,6 +67,10 @@ var defaultDashboardKeyMap = DashboardKeyMap{
|
||||
key.WithKeys("p"),
|
||||
key.WithHelp("p", "change priority"),
|
||||
),
|
||||
ChangeType: key.NewBinding(
|
||||
key.WithKeys("t"),
|
||||
key.WithHelp("t", "change type"),
|
||||
),
|
||||
AddIssue: key.NewBinding(
|
||||
key.WithKeys("a"),
|
||||
key.WithHelp("a", "add issue"),
|
||||
@@ -94,28 +99,32 @@ func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd {
|
||||
d.issueDetail.ScrollUp(1)
|
||||
case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown):
|
||||
d.issueDetail.ScrollDown(1)
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.EditTitle):
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && key.Matches(msg, d.keyMap.EditTitle):
|
||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||
d.startEditTitle(selected)
|
||||
cmd = d.titleInput.Focus()
|
||||
}
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.EditDescription):
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && key.Matches(msg, d.keyMap.EditDescription):
|
||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||
d.startEditDescription(selected)
|
||||
cmd = d.descriptionInput.Focus()
|
||||
}
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.ChangeStatus):
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && key.Matches(msg, d.keyMap.ChangeStatus):
|
||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||
d.startChooseStatus(selected)
|
||||
}
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.ChangePriority):
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && 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):
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && key.Matches(msg, d.keyMap.ChangeType):
|
||||
if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" {
|
||||
d.startChooseType(selected)
|
||||
}
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && key.Matches(msg, d.keyMap.AddIssue):
|
||||
d.startCreateIssue()
|
||||
cmd = d.createTitleInput.Focus()
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && key.Matches(msg, d.keyMap.DeleteIssue):
|
||||
case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && key.Matches(msg, d.keyMap.DeleteIssue):
|
||||
fl := d.FocusedIssueList()
|
||||
if selected := fl.SelectedItem(); selected.ID != "" {
|
||||
d.startConfirmDelete(selected.ID, fl.Index())
|
||||
|
||||
@@ -41,10 +41,12 @@ type Model struct {
|
||||
deleteConfirmID string
|
||||
deleteConfirmIndex int
|
||||
|
||||
choosingStatus bool
|
||||
choosingStatus bool // true while choosing a status
|
||||
statusIssueID string
|
||||
choosingPriority bool
|
||||
priorityIssueID string
|
||||
choosingPriority bool // true while choosing a priority
|
||||
priorityIssueID string
|
||||
choosingType bool // true while choosing a type
|
||||
typeIssueID string
|
||||
feedbackChan chan task.ValidationFeedback
|
||||
quitChan chan bool
|
||||
currentFeedback task.ValidationFeedback
|
||||
@@ -132,6 +134,11 @@ func (m *Model) startChoosePriority(selected ListIssue) {
|
||||
m.priorityIssueID = selected.ID
|
||||
}
|
||||
|
||||
func (m *Model) startChooseType(selected ListIssue) {
|
||||
m.choosingType = true
|
||||
m.typeIssueID = selected.ID
|
||||
}
|
||||
|
||||
func (m *Model) Init() tea.Cmd {
|
||||
return m.listenForValidation()
|
||||
}
|
||||
|
||||
@@ -29,6 +29,11 @@ type issuePriorityUpdatedMsg struct {
|
||||
Err error
|
||||
}
|
||||
|
||||
type issueTypeUpdatedMsg struct {
|
||||
IssueID string
|
||||
Err error
|
||||
}
|
||||
|
||||
type selectIssueMsg struct {
|
||||
IssueID string
|
||||
}
|
||||
@@ -76,12 +81,21 @@ func updateIssuePriorityCmd(svc *service.Services, issueID string, priority int)
|
||||
}
|
||||
}
|
||||
|
||||
func updateIssueTypeCmd(svc *service.Services, issueID string, issueType models.IssueType) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
updates := map[string]interface{}{"issue_type": string(issueType)}
|
||||
err := svc.Beads.UpdateIssue(context.Background(), issueID, updates, "tui")
|
||||
return issueTypeUpdatedMsg{IssueID: issueID, Err: err}
|
||||
}
|
||||
}
|
||||
|
||||
func createIssueCmd(svc *service.Services, title string) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
issue := &models.Issue{
|
||||
Title: title,
|
||||
Status: models.StatusOpen,
|
||||
IssueType: models.TypeTask,
|
||||
Priority: 2,
|
||||
}
|
||||
err := svc.Beads.CreateIssue(context.Background(), issue, "tui")
|
||||
return issueCreatedMsg{Issue: issue, Err: err}
|
||||
@@ -150,6 +164,14 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
return m, m.refreshIssueListsAndSelectIssue(msg.IssueID)
|
||||
|
||||
case issueTypeUpdatedMsg:
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
if msg.Err != nil {
|
||||
return m, nil
|
||||
}
|
||||
return m, m.refreshIssueListsAndSelectIssue(msg.IssueID)
|
||||
|
||||
case selectIssueMsg:
|
||||
m.issueList.SelectIssueID(msg.IssueID)
|
||||
m.closedIssueList.SelectIssueID(msg.IssueID)
|
||||
@@ -294,6 +316,42 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
if m.choosingType {
|
||||
switch msg.String() {
|
||||
case "b":
|
||||
issueID := m.typeIssueID
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
return m, updateIssueTypeCmd(m.svc, issueID, models.TypeBug)
|
||||
case "f":
|
||||
issueID := m.typeIssueID
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
return m, updateIssueTypeCmd(m.svc, issueID, models.TypeFeature)
|
||||
case "t":
|
||||
issueID := m.typeIssueID
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
return m, updateIssueTypeCmd(m.svc, issueID, models.TypeTask)
|
||||
case "e":
|
||||
issueID := m.typeIssueID
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
return m, updateIssueTypeCmd(m.svc, issueID, models.TypeEpic)
|
||||
case "c":
|
||||
issueID := m.typeIssueID
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
return m, updateIssueTypeCmd(m.svc, issueID, models.TypeChore)
|
||||
case "esc":
|
||||
m.choosingType = false
|
||||
m.typeIssueID = ""
|
||||
return m, nil
|
||||
default:
|
||||
return m, nil
|
||||
}
|
||||
}
|
||||
|
||||
if m.creatingIssue {
|
||||
if msg.String() == "enter" {
|
||||
title := m.createTitleInput.Value()
|
||||
@@ -356,6 +414,11 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
// On main dashboard, ESC does nothing; only q quits; like in lazybeads.
|
||||
if msg.String() == "esc" {
|
||||
return m, nil
|
||||
}
|
||||
|
||||
cmd := m.handleKeyMsg(msg)
|
||||
if cmd != nil {
|
||||
return m, cmd
|
||||
|
||||
@@ -129,7 +129,7 @@ func (m *Model) View() string {
|
||||
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("0 = irrelevant 1 = low 2 = normal 3 = high 4 = critical"),
|
||||
lipgloss.NewStyle().Foreground(styles.FaintText).Render("Esc = cancel"),
|
||||
)
|
||||
priorityBoxWidth := min(60, m.width-4)
|
||||
@@ -140,6 +140,20 @@ func (m *Model) View() string {
|
||||
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, priorityBox)
|
||||
}
|
||||
|
||||
if m.choosingType {
|
||||
typeContent := lipgloss.JoinVertical(lipgloss.Left,
|
||||
styles.LabelStyle.Render("Change type for "+m.typeIssueID+":"),
|
||||
lipgloss.NewStyle().Foreground(styles.FaintText).Render("b = bug f = feature t = task e = epic c = chore"),
|
||||
lipgloss.NewStyle().Foreground(styles.FaintText).Render("Esc = cancel"),
|
||||
)
|
||||
typeBoxWidth := min(65, m.width-4)
|
||||
typeBox := styles.ContainerStyle.
|
||||
Width(typeBoxWidth).
|
||||
BorderForeground(styles.PrimaryBorder).
|
||||
Render(typeContent)
|
||||
return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, typeBox)
|
||||
}
|
||||
|
||||
return mainView
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user