From 1af006e27c43dc3742fe6fd3ddd48bf324c0d4cf Mon Sep 17 00:00:00 2001 From: viljarb Date: Wed, 11 Mar 2026 17:24:33 +0100 Subject: [PATCH 1/6] priority management task --- cmd/pm/tasks/priorityManagement.go | 69 ++++++++++++++++++++++++------ 1 file changed, 56 insertions(+), 13 deletions(-) diff --git a/cmd/pm/tasks/priorityManagement.go b/cmd/pm/tasks/priorityManagement.go index a5b2b5c..3c9ebdc 100644 --- a/cmd/pm/tasks/priorityManagement.go +++ b/cmd/pm/tasks/priorityManagement.go @@ -2,6 +2,7 @@ package tasks import ( "context" + "fmt" "github.com/LazyBachelor/LazyPM/internal/models" "github.com/LazyBachelor/LazyPM/internal/utils/check" @@ -10,16 +11,18 @@ import ( const priorityManagementDescription = `You are tasked with managing issue priorities. -A critical production issue has been reported. You need to rebalance the current sprint priorities: +A critical production issue has been reported. -1. Review all current issues and their priorities -2. Identify the most urgent production issue -3. Reprioritize existing work to accommodate the urgent fix -4. Defer lower priority items if necessary -5. Update the team on priority changes via comments -6. Ensure the critical path is clear for the urgent fix +The database is not working properly and users are not able to connect and access their data. -The production database is experiencing intermittent connection failures affecting all users.` +You need to rebalance the current sprint priorities: + +1. Assign the task Issue you are currently reading to yourself as "Me" and set status to "In Progress". +2. A new issue has appeared in the list that needs urgent attention. Change the database related issue's priority to 4 (critical). +3. Set the priority of the feature and chore issues in the list to 1 (low). +4. Change this issue status to "Closed". + +` type PriorityManagementTask struct { done bool @@ -74,30 +77,30 @@ func (t *PriorityManagementTask) Setup(ctx context.Context) error { NewIssueBuilder(). WithTitle("UI theme updates"). WithDescription("Update color scheme per new brand guidelines. Currently in progress but can wait."). - WithPriority(1). + WithPriority(2). WithStatus(models.StatusInProgress). - WithIssueType(models.TypeTask). + WithIssueType(models.TypeFeature). Build(), NewIssueBuilder(). WithTitle("Feature: Dark mode"). WithDescription("Add dark mode toggle to settings. Nice to have, can be deferred."). WithPriority(2). WithStatus(models.StatusOpen). - WithIssueType(models.TypeTask). + WithIssueType(models.TypeFeature). Build(), NewIssueBuilder(). WithTitle("API rate limiting"). WithDescription("Add rate limiting to public API endpoints. Security enhancement."). WithPriority(2). WithStatus(models.StatusInProgress). - WithIssueType(models.TypeTask). + WithIssueType(models.TypeFeature). Build(), NewIssueBuilder(). WithTitle("Documentation updates"). WithDescription("Update API documentation for v2 endpoints. Can be deferred."). WithPriority(3). WithStatus(models.StatusOpen). - WithIssueType(models.TypeTask). + WithIssueType(models.TypeChore). Build(), } @@ -116,5 +119,45 @@ func (t *PriorityManagementTask) Setup(ctx context.Context) error { func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedback { expect := check.NewExpector() + issues, err := FetchIssues(ctx, t.app, t.setupIssue) + if err != nil { + return expect.ValidationFeedback + } + + taskIssue := t.setupIssue + + if taskIssue.Assignee != "Me" { + expect.Assert(taskIssue.Assignee == "Me", + fmt.Sprintf("'%s' not assigned to you, but to '%s'", taskIssue.Title, taskIssue.Assignee)) + return expect.ValidationFeedback + } + + if taskIssue.Status == models.StatusOpen { + expect.Assert(taskIssue.Status == models.StatusInProgress, + fmt.Sprintf("'%s' status should be 'In Progress', but was '%s'", taskIssue.Title, taskIssue.Status)) + return expect.ValidationFeedback + } + + for ii := range issues { + + if issues[ii].Title == "Database connection failures" { + if issues[ii].Priority != 4 { + expect.Assert(issues[ii].Priority == 4, + fmt.Sprintf("'%s' priority should be 4 (critical), but was '%d'", issues[ii].Title, issues[ii].Priority)) + return expect.ValidationFeedback + } + } else { + if issues[ii].Priority != 1 { + expect.Assert(issues[ii].Priority == 1, + fmt.Sprintf("'%s' priority should be 1 (low), but was '%d'", issues[ii].Title, issues[ii].Priority)) + return expect.ValidationFeedback + } + } + } + expect.Assert(taskIssue.Status == models.StatusClosed, + fmt.Sprintf("'%s' is not set to closed", taskIssue.Title)) + + + return expect.Complete() } From a30c82252948a82e137d5a14d0acff5f15786a10 Mon Sep 17 00:00:00 2001 From: viljarb Date: Wed, 11 Mar 2026 18:17:52 +0100 Subject: [PATCH 2/6] fixing logic a bit --- cmd/pm/tasks/priorityManagement.go | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/cmd/pm/tasks/priorityManagement.go b/cmd/pm/tasks/priorityManagement.go index 3c9ebdc..662792e 100644 --- a/cmd/pm/tasks/priorityManagement.go +++ b/cmd/pm/tasks/priorityManagement.go @@ -124,17 +124,23 @@ func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedbac return expect.ValidationFeedback } + taskpart1 := 0 + taskpart2 := 0 taskIssue := t.setupIssue if taskIssue.Assignee != "Me" { expect.Assert(taskIssue.Assignee == "Me", fmt.Sprintf("'%s' not assigned to you, but to '%s'", taskIssue.Title, taskIssue.Assignee)) - return expect.ValidationFeedback + taskpart1++ } if taskIssue.Status == models.StatusOpen { expect.Assert(taskIssue.Status == models.StatusInProgress, fmt.Sprintf("'%s' status should be 'In Progress', but was '%s'", taskIssue.Title, taskIssue.Status)) + taskpart1++ + } + + if taskpart1>0 { return expect.ValidationFeedback } @@ -144,16 +150,21 @@ func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedbac if issues[ii].Priority != 4 { expect.Assert(issues[ii].Priority == 4, fmt.Sprintf("'%s' priority should be 4 (critical), but was '%d'", issues[ii].Title, issues[ii].Priority)) - return expect.ValidationFeedback + taskpart2++ } } else { if issues[ii].Priority != 1 { expect.Assert(issues[ii].Priority == 1, fmt.Sprintf("'%s' priority should be 1 (low), but was '%d'", issues[ii].Title, issues[ii].Priority)) - return expect.ValidationFeedback + taskpart2++ } } } + + + if taskpart2>0 { + return expect.ValidationFeedback + } expect.Assert(taskIssue.Status == models.StatusClosed, fmt.Sprintf("'%s' is not set to closed", taskIssue.Title)) From c72b15f72103d045e0d63eb9794fc0915fd5d734 Mon Sep 17 00:00:00 2001 From: viljarb Date: Wed, 11 Mar 2026 18:40:21 +0100 Subject: [PATCH 3/6] adding assignee to tui --- pkg/tui/styles/styles.go | 2 +- pkg/tui/views/dashboard/help_bar.go | 7 ++-- pkg/tui/views/dashboard/issue_detail.go | 10 +++++- pkg/tui/views/dashboard/issue_list.go | 15 +++++---- pkg/tui/views/dashboard/keys.go | 27 ++++++++++----- pkg/tui/views/dashboard/model.go | 15 +++++++++ pkg/tui/views/dashboard/operations.go | 45 +++++++++++++++++++++++++ pkg/tui/views/dashboard/view.go | 14 ++++++++ 8 files changed, 116 insertions(+), 19 deletions(-) diff --git a/pkg/tui/styles/styles.go b/pkg/tui/styles/styles.go index 53b3dfc..427513f 100644 --- a/pkg/tui/styles/styles.go +++ b/pkg/tui/styles/styles.go @@ -21,7 +21,7 @@ var ( ) const ( - ListViewRatio = 70 // Percentage of total width allocated to the list view + ListViewRatio = 55 // Percentage of total width allocated to the list view LabelWidth = 14 MarginBottomSmall = 1 ) diff --git a/pkg/tui/views/dashboard/help_bar.go b/pkg/tui/views/dashboard/help_bar.go index 39a1949..5546e18 100644 --- a/pkg/tui/views/dashboard/help_bar.go +++ b/pkg/tui/views/dashboard/help_bar.go @@ -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/t/c") + "edit ", + styles.HighlightKey("e/d/s/p/t/c/A") + "edit ", styles.HighlightKey("x") + "delete ", styles.HighlightKey("q") + "quit ", styles.HighlightKey("S") + "submit ", @@ -78,8 +78,9 @@ func (h HelpBar) fullHelp() string { renderRow("e", "edit title", "d", "edit desc"), renderRow("c", "add comment", "", ""), renderRow("s", "change status", "p", "change priority"), - renderRow("t", "change type", "x", "delete issue"), - renderRow("S", "submit task", "q", "quit"), + renderRow("t", "change type", "A", "change assignee"), + renderRow("x", "delete issue", "S", "submit task"), + renderRow("q", "quit", "", ""), } content := lipgloss.JoinVertical(lipgloss.Left, rows...) return lipgloss.NewStyle(). diff --git a/pkg/tui/views/dashboard/issue_detail.go b/pkg/tui/views/dashboard/issue_detail.go index 2899bca..6ca02a6 100644 --- a/pkg/tui/views/dashboard/issue_detail.go +++ b/pkg/tui/views/dashboard/issue_detail.go @@ -66,11 +66,19 @@ func (i *IssueDetail) refreshContent() { styles.LabelStyle.Render("Priority:") + styles.ValueStyle.Render(priorityCodeName(i.issue.Priority)), ) + assignee := i.issue.Assignee + if assignee == "" { + assignee = "—" + } + assigneeRow := styles.RowStyle.Render( + styles.LabelStyle.Render("Assignee:") + styles.ValueStyle.Render(assignee), + ) + descLabel := styles.LabelStyle.Render("Description:") descContent := styles.ValueStyle.Render(i.issue.Description) var parts []string - parts = append(parts, titleRow, idRow, typeRow, statusRow, priorityRow, descLabel, descContent) + parts = append(parts, titleRow, idRow, typeRow, statusRow, priorityRow, assigneeRow, descLabel, descContent) // Comments section commentsLabel := styles.LabelStyle.Render("Comments:") diff --git a/pkg/tui/views/dashboard/issue_list.go b/pkg/tui/views/dashboard/issue_list.go index c2eb4cb..e324258 100644 --- a/pkg/tui/views/dashboard/issue_list.go +++ b/pkg/tui/views/dashboard/issue_list.go @@ -47,15 +47,16 @@ func getTableColumns(width int) []TableColumn { return []TableColumn{ {width: 10, label: "ID", key: "id"}, {width: 20, label: "TITLE", key: "title"}, - {width: 15, label: "STATUS", key: "status"}, + {width: 13, label: "STATUS", key: "status"}, } default: return []TableColumn{ - {width: 12, label: "ID", key: "id"}, - {width: 20, label: "TITLE", key: "title"}, - {width: 15, label: "STATUS", key: "status"}, - {width: 10, label: "TYPE", key: "type"}, - {width: 15, label: "PRIORITY", key: "priority"}, + {width: 10, label: "ID", key: "id"}, + {width: 24, label: "TITLE", key: "title"}, + {width: 14, label: "STATUS", key: "status"}, + {width: 11, label: "TYPE", key: "type"}, + {width: 13, label: "PRIORITY", key: "priority"}, + {width: 13, label: "ASSIGNEE", key: "assignee"}, } } } @@ -340,6 +341,8 @@ func getColumnValue(col TableColumn, issue ListIssue) string { return string(issue.Issue.IssueType) case "priority": return priorityCodeName(issue.Issue.Priority) + case "assignee": + return issue.Issue.Assignee default: return "" } diff --git a/pkg/tui/views/dashboard/keys.go b/pkg/tui/views/dashboard/keys.go index dd0981b..a2deace 100644 --- a/pkg/tui/views/dashboard/keys.go +++ b/pkg/tui/views/dashboard/keys.go @@ -18,6 +18,7 @@ type DashboardKeyMap struct { ChangeStatus key.Binding ChangePriority key.Binding ChangeType key.Binding + ChangeAssignee key.Binding AddComment key.Binding AddIssue key.Binding DeleteIssue key.Binding @@ -73,6 +74,10 @@ var defaultDashboardKeyMap = DashboardKeyMap{ key.WithKeys("t"), key.WithHelp("t", "change type"), ), + ChangeAssignee: key.NewBinding( + key.WithKeys("A"), + key.WithHelp("A", "change assignee"), + ), AddComment: key.NewBinding( key.WithKeys("c"), key.WithHelp("c", "add comment"), @@ -116,43 +121,49 @@ func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd { case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown): d.issueDetail.ScrollDown(1) d.logAction("tui scrolled issue detail down") - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.EditTitle): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.EditTitle): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startEditTitle(selected) cmd = d.titleInput.Focus() d.logAction("tui started editing issue title") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.EditDescription): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.EditDescription): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startEditDescription(selected) cmd = d.descriptionInput.Focus() d.logAction("tui started editing issue description") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.ChangeStatus): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangeStatus): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startChooseStatus(selected) d.logAction("tui opened status picker") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.ChangePriority): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangePriority): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startChoosePriority(selected) d.logAction("tui opened priority picker") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.ChangeType): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangeType): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startChooseType(selected) d.logAction("tui opened type picker") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.AddComment): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangeAssignee): + if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { + d.startEditAssignee(selected) + cmd = d.assigneeInput.Focus() + d.logAction("tui started editing assignee") + } + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.AddComment): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startAddComment(selected) cmd = d.commentInput.Focus() } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.AddIssue): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.AddIssue): d.startCreateIssue() cmd = d.createTitleInput.Focus() d.logAction("tui started creating issue") - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.DeleteIssue): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.DeleteIssue): fl := d.FocusedIssueList() if selected := fl.SelectedItem(); selected.ID != "" { d.startConfirmDelete(selected.ID, fl.Index()) diff --git a/pkg/tui/views/dashboard/model.go b/pkg/tui/views/dashboard/model.go index de1ce4b..33a2275 100644 --- a/pkg/tui/views/dashboard/model.go +++ b/pkg/tui/views/dashboard/model.go @@ -47,6 +47,9 @@ type Model struct { priorityIssueID string choosingType bool // true while choosing a type typeIssueID string + editingAssignee bool // true while editing assignee + assigneeInput textinput.Model + assigneeIssueID string addingComment bool // true while adding a comment commentInput textarea.Model commentIssueID string @@ -88,6 +91,11 @@ func NewDashboard(app *app.App, feedbackChan chan models.ValidationFeedback, qui createTi.CharLimit = 256 m.createTitleInput = createTi + assigneeTi := textinput.New() + assigneeTi.Placeholder = "Assignee (e.g. Me, username)..." + assigneeTi.CharLimit = 128 + m.assigneeInput = assigneeTi + descTa := textarea.New() descTa.Placeholder = "Issue description..." descTa.SetWidth(56) @@ -177,6 +185,13 @@ func (m *Model) startChooseType(selected ListIssue) { m.typeIssueID = selected.ID } +func (m *Model) startEditAssignee(selected ListIssue) { + m.editingAssignee = true + m.assigneeIssueID = selected.ID + m.assigneeInput.SetValue(selected.Issue.Assignee) + m.assigneeInput.CursorEnd() +} + func (m *Model) Init() tea.Cmd { return m.listenForValidation() } diff --git a/pkg/tui/views/dashboard/operations.go b/pkg/tui/views/dashboard/operations.go index 566692e..6046821 100644 --- a/pkg/tui/views/dashboard/operations.go +++ b/pkg/tui/views/dashboard/operations.go @@ -49,6 +49,11 @@ type issueTypeUpdatedMsg struct { Err error } +type issueAssigneeUpdatedMsg struct { + IssueID string + Err error +} + type selectIssueMsg struct { IssueID string } @@ -116,6 +121,14 @@ func updateIssueTypeCmd(app *app.App, issueID string, issueType models.IssueType } } +func updateIssueAssigneeCmd(app *app.App, issueID, assignee string) tea.Cmd { + return func() tea.Msg { + updates := map[string]interface{}{"assignee": assignee} + err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") + return issueAssigneeUpdatedMsg{IssueID: issueID, Err: err} + } +} + func createIssueCmd(app *app.App, title string) tea.Cmd { return func() tea.Msg { issue := &models.Issue{ @@ -209,6 +222,17 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.logAction("tui updated issue type") return m, m.refreshIssueListsAndSelectIssue(msg.IssueID) + case issueAssigneeUpdatedMsg: + m.editingAssignee = false + m.assigneeIssueID = "" + m.assigneeInput.Blur() + if msg.Err != nil { + m.logAction("tui failed to update issue assignee") + return m, nil + } + m.logAction("tui updated issue assignee") + return m, m.refreshIssueListsAndSelectIssue(msg.IssueID) + case selectIssueMsg: m.issueList.SelectIssueID(msg.IssueID) m.closedIssueList.SelectIssueID(msg.IssueID) @@ -480,6 +504,27 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } + if m.editingAssignee { + if msg.String() == "enter" { + assignee := m.assigneeInput.Value() + issueID := m.assigneeIssueID + m.editingAssignee = false + m.assigneeIssueID = "" + m.assigneeInput.Blur() + return m, updateIssueAssigneeCmd(m.app, issueID, assignee) + } + if msg.String() == "esc" { + m.logAction("tui canceled assignee edit") + m.editingAssignee = false + m.assigneeIssueID = "" + m.assigneeInput.Blur() + return m, nil + } + var cmd tea.Cmd + m.assigneeInput, cmd = m.assigneeInput.Update(msg) + return m, cmd + } + if m.editingDescription { if msg.String() == "ctrl+s" { m.logAction("tui submitted issue description edit") diff --git a/pkg/tui/views/dashboard/view.go b/pkg/tui/views/dashboard/view.go index 74aca51..0d2d14f 100644 --- a/pkg/tui/views/dashboard/view.go +++ b/pkg/tui/views/dashboard/view.go @@ -56,6 +56,20 @@ func (m *Model) View() string { mainView := lipgloss.JoinVertical(lipgloss.Left, header, content, footer) + if m.editingAssignee { + editBoxWidth := min(60, m.width-4) + m.assigneeInput.Width = editBoxWidth - 2 + editContent := lipgloss.JoinVertical(lipgloss.Left, + styles.LabelStyle.Render("Change assignee for "+m.assigneeIssueID+" (Enter to save, Esc to cancel):"), + m.assigneeInput.View(), + ) + editBox := styles.ContainerStyle. + Width(editBoxWidth). + BorderForeground(styles.PrimaryBorder). + Render(editContent) + return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, editBox) + } + if m.editingTitle { editBoxWidth := min(60, m.width-4) m.titleInput.Width = editBoxWidth - 2 From 3f8629ab3227fc6bb24b55d9a85aa01a4c3a3eac Mon Sep 17 00:00:00 2001 From: viljarb Date: Wed, 11 Mar 2026 19:03:02 +0100 Subject: [PATCH 4/6] updating task --- cmd/pm/tasks/priorityManagement.go | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/cmd/pm/tasks/priorityManagement.go b/cmd/pm/tasks/priorityManagement.go index 662792e..de47248 100644 --- a/cmd/pm/tasks/priorityManagement.go +++ b/cmd/pm/tasks/priorityManagement.go @@ -129,16 +129,18 @@ func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedbac taskIssue := t.setupIssue if taskIssue.Assignee != "Me" { - expect.Assert(taskIssue.Assignee == "Me", - fmt.Sprintf("'%s' not assigned to you, but to '%s'", taskIssue.Title, taskIssue.Assignee)) taskpart1++ } + expect.Assert(taskIssue.Assignee == "Me", + fmt.Sprintf("'%s' should be assigned to you", taskIssue.Title)) if taskIssue.Status == models.StatusOpen { - expect.Assert(taskIssue.Status == models.StatusInProgress, - fmt.Sprintf("'%s' status should be 'In Progress', but was '%s'", taskIssue.Title, taskIssue.Status)) taskpart1++ } + if taskIssue.Status != models.StatusClosed { + expect.Assert(taskIssue.Status == models.StatusInProgress, + fmt.Sprintf("'%s' status should be 'In Progress'", taskIssue.Title)) + } if taskpart1>0 { return expect.ValidationFeedback @@ -147,15 +149,15 @@ func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedbac for ii := range issues { if issues[ii].Title == "Database connection failures" { + expect.Assert(issues[ii].Priority == 4, + fmt.Sprintf("'%s' priority should be 4 (critical)", issues[ii].Title)) if issues[ii].Priority != 4 { - expect.Assert(issues[ii].Priority == 4, - fmt.Sprintf("'%s' priority should be 4 (critical), but was '%d'", issues[ii].Title, issues[ii].Priority)) taskpart2++ } } else { + expect.Assert(issues[ii].Priority == 1, + fmt.Sprintf("'%s' priority should be 1 (low)", issues[ii].Title)) if issues[ii].Priority != 1 { - expect.Assert(issues[ii].Priority == 1, - fmt.Sprintf("'%s' priority should be 1 (low), but was '%d'", issues[ii].Title, issues[ii].Priority)) taskpart2++ } } @@ -166,7 +168,7 @@ func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedbac return expect.ValidationFeedback } expect.Assert(taskIssue.Status == models.StatusClosed, - fmt.Sprintf("'%s' is not set to closed", taskIssue.Title)) + fmt.Sprintf("'%s' should be set to closed", taskIssue.Title)) From a5b04997a7a7f63afb6373875bd54b13752e6f17 Mon Sep 17 00:00:00 2001 From: Robin Olsen Date: Wed, 11 Mar 2026 20:35:22 +0100 Subject: [PATCH 5/6] improve task logic --- cmd/pm/tasks/priorityManagement.go | 75 +++++++++--------------------- internal/utils/check/expect.go | 10 ++++ 2 files changed, 32 insertions(+), 53 deletions(-) diff --git a/cmd/pm/tasks/priorityManagement.go b/cmd/pm/tasks/priorityManagement.go index de47248..6ba3554 100644 --- a/cmd/pm/tasks/priorityManagement.go +++ b/cmd/pm/tasks/priorityManagement.go @@ -19,15 +19,14 @@ You need to rebalance the current sprint priorities: 1. Assign the task Issue you are currently reading to yourself as "Me" and set status to "In Progress". 2. A new issue has appeared in the list that needs urgent attention. Change the database related issue's priority to 4 (critical). -3. Set the priority of the feature and chore issues in the list to 1 (low). -4. Change this issue status to "Closed". - -` +3. Set the priority of the feature and chore issues in the list to 1 (low).` type PriorityManagementTask struct { - done bool - app *App - setupIssue *Issue + done bool + app *App + setupIssue *Issue + priorityIssues []*models.Issue + isInProgress bool } func NewPriorityManagementTask(app *App) *PriorityManagementTask { @@ -66,7 +65,7 @@ func (t *PriorityManagementTask) Setup(ctx context.Context) error { return err } - priorityIssues := []*models.Issue{ + t.priorityIssues = []*models.Issue{ NewIssueBuilder(). WithTitle("Database connection failures"). WithDescription("PRODUCTION CRITICAL: Intermittent DB connection failures affecting all users. Needs immediate attention."). @@ -104,7 +103,7 @@ func (t *PriorityManagementTask) Setup(ctx context.Context) error { Build(), } - if err := t.app.Issues.CreateIssues(ctx, priorityIssues, ""); err != nil { + if err := t.app.Issues.CreateIssues(ctx, t.priorityIssues, ""); err != nil { return err } @@ -121,56 +120,26 @@ func (t *PriorityManagementTask) Validate(ctx context.Context) ValidationFeedbac issues, err := FetchIssues(ctx, t.app, t.setupIssue) if err != nil { + return expect.Fatal("Failed to fetch issues for validation") + } + + expect.NotEmptyAndEqual(t.setupIssue.Assignee, "Me", + fmt.Sprintf("%s assignee", t.setupIssue.Title)) + + expect.Equal(t.setupIssue.Status, models.StatusInProgress, + fmt.Sprintf("%s status", t.setupIssue.Title)) + + if !expect.Valid() { return expect.ValidationFeedback } - taskpart1 := 0 - taskpart2 := 0 - taskIssue := t.setupIssue - - if taskIssue.Assignee != "Me" { - taskpart1++ - } - expect.Assert(taskIssue.Assignee == "Me", - fmt.Sprintf("'%s' should be assigned to you", taskIssue.Title)) - - if taskIssue.Status == models.StatusOpen { - taskpart1++ - } - if taskIssue.Status != models.StatusClosed { - expect.Assert(taskIssue.Status == models.StatusInProgress, - fmt.Sprintf("'%s' status should be 'In Progress'", taskIssue.Title)) - } - - if taskpart1>0 { - return expect.ValidationFeedback - } - - for ii := range issues { - - if issues[ii].Title == "Database connection failures" { - expect.Assert(issues[ii].Priority == 4, - fmt.Sprintf("'%s' priority should be 4 (critical)", issues[ii].Title)) - if issues[ii].Priority != 4 { - taskpart2++ - } + for _, issue := range issues { + if issue.Title == t.priorityIssues[0].Title { + expect.Equal(issue.Priority, 4, fmt.Sprintf("Priority of issue %s", issue.Title)) } else { - expect.Assert(issues[ii].Priority == 1, - fmt.Sprintf("'%s' priority should be 1 (low)", issues[ii].Title)) - if issues[ii].Priority != 1 { - taskpart2++ - } + expect.Equal(issue.Priority, 1, fmt.Sprintf("Priority of issue %s", issue.Title)) } } - - if taskpart2>0 { - return expect.ValidationFeedback - } - expect.Assert(taskIssue.Status == models.StatusClosed, - fmt.Sprintf("'%s' should be set to closed", taskIssue.Title)) - - - return expect.Complete() } diff --git a/internal/utils/check/expect.go b/internal/utils/check/expect.go index 8850d83..dbeaa29 100644 --- a/internal/utils/check/expect.go +++ b/internal/utils/check/expect.go @@ -30,11 +30,21 @@ func NewExpector() *Expector { } } +func (e *Expector) Valid() bool { + return len(e.Errors()) == 0 +} + func (e *Expector) Complete() ValidationFeedback { e.Success = len(e.Errors()) == 0 return e.ValidationFeedback } +func (e *Expector) Fatal(message string) ValidationFeedback { + e.Success = false + e.Message = message + return e.ValidationFeedback +} + func (e *Expector) CompleteWithMessage(message string) ValidationFeedback { e.Success = len(e.Errors()) == 0 if !e.Success { From 9a0cb74e39821dd0e5cecf8c67def75af6bdb85b Mon Sep 17 00:00:00 2001 From: Robin Olsen Date: Wed, 11 Mar 2026 20:37:05 +0100 Subject: [PATCH 6/6] Revert "adding assignee to tui" This reverts commit c72b15f72103d045e0d63eb9794fc0915fd5d734. --- pkg/tui/styles/styles.go | 2 +- pkg/tui/views/dashboard/help_bar.go | 7 ++-- pkg/tui/views/dashboard/issue_detail.go | 10 +----- pkg/tui/views/dashboard/issue_list.go | 15 ++++----- pkg/tui/views/dashboard/keys.go | 27 +++++---------- pkg/tui/views/dashboard/model.go | 15 --------- pkg/tui/views/dashboard/operations.go | 45 ------------------------- pkg/tui/views/dashboard/view.go | 14 -------- 8 files changed, 19 insertions(+), 116 deletions(-) diff --git a/pkg/tui/styles/styles.go b/pkg/tui/styles/styles.go index 427513f..53b3dfc 100644 --- a/pkg/tui/styles/styles.go +++ b/pkg/tui/styles/styles.go @@ -21,7 +21,7 @@ var ( ) const ( - ListViewRatio = 55 // Percentage of total width allocated to the list view + ListViewRatio = 70 // Percentage of total width allocated to the list view LabelWidth = 14 MarginBottomSmall = 1 ) diff --git a/pkg/tui/views/dashboard/help_bar.go b/pkg/tui/views/dashboard/help_bar.go index 5546e18..39a1949 100644 --- a/pkg/tui/views/dashboard/help_bar.go +++ b/pkg/tui/views/dashboard/help_bar.go @@ -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/t/c/A") + "edit ", + styles.HighlightKey("e/d/s/p/t/c") + "edit ", styles.HighlightKey("x") + "delete ", styles.HighlightKey("q") + "quit ", styles.HighlightKey("S") + "submit ", @@ -78,9 +78,8 @@ func (h HelpBar) fullHelp() string { renderRow("e", "edit title", "d", "edit desc"), renderRow("c", "add comment", "", ""), renderRow("s", "change status", "p", "change priority"), - renderRow("t", "change type", "A", "change assignee"), - renderRow("x", "delete issue", "S", "submit task"), - renderRow("q", "quit", "", ""), + renderRow("t", "change type", "x", "delete issue"), + renderRow("S", "submit task", "q", "quit"), } content := lipgloss.JoinVertical(lipgloss.Left, rows...) return lipgloss.NewStyle(). diff --git a/pkg/tui/views/dashboard/issue_detail.go b/pkg/tui/views/dashboard/issue_detail.go index 6ca02a6..2899bca 100644 --- a/pkg/tui/views/dashboard/issue_detail.go +++ b/pkg/tui/views/dashboard/issue_detail.go @@ -66,19 +66,11 @@ func (i *IssueDetail) refreshContent() { styles.LabelStyle.Render("Priority:") + styles.ValueStyle.Render(priorityCodeName(i.issue.Priority)), ) - assignee := i.issue.Assignee - if assignee == "" { - assignee = "—" - } - assigneeRow := styles.RowStyle.Render( - styles.LabelStyle.Render("Assignee:") + styles.ValueStyle.Render(assignee), - ) - descLabel := styles.LabelStyle.Render("Description:") descContent := styles.ValueStyle.Render(i.issue.Description) var parts []string - parts = append(parts, titleRow, idRow, typeRow, statusRow, priorityRow, assigneeRow, descLabel, descContent) + parts = append(parts, titleRow, idRow, typeRow, statusRow, priorityRow, descLabel, descContent) // Comments section commentsLabel := styles.LabelStyle.Render("Comments:") diff --git a/pkg/tui/views/dashboard/issue_list.go b/pkg/tui/views/dashboard/issue_list.go index e324258..c2eb4cb 100644 --- a/pkg/tui/views/dashboard/issue_list.go +++ b/pkg/tui/views/dashboard/issue_list.go @@ -47,16 +47,15 @@ func getTableColumns(width int) []TableColumn { return []TableColumn{ {width: 10, label: "ID", key: "id"}, {width: 20, label: "TITLE", key: "title"}, - {width: 13, label: "STATUS", key: "status"}, + {width: 15, label: "STATUS", key: "status"}, } default: return []TableColumn{ - {width: 10, label: "ID", key: "id"}, - {width: 24, label: "TITLE", key: "title"}, - {width: 14, label: "STATUS", key: "status"}, - {width: 11, label: "TYPE", key: "type"}, - {width: 13, label: "PRIORITY", key: "priority"}, - {width: 13, label: "ASSIGNEE", key: "assignee"}, + {width: 12, label: "ID", key: "id"}, + {width: 20, label: "TITLE", key: "title"}, + {width: 15, label: "STATUS", key: "status"}, + {width: 10, label: "TYPE", key: "type"}, + {width: 15, label: "PRIORITY", key: "priority"}, } } } @@ -341,8 +340,6 @@ func getColumnValue(col TableColumn, issue ListIssue) string { return string(issue.Issue.IssueType) case "priority": return priorityCodeName(issue.Issue.Priority) - case "assignee": - return issue.Issue.Assignee default: return "" } diff --git a/pkg/tui/views/dashboard/keys.go b/pkg/tui/views/dashboard/keys.go index a2deace..dd0981b 100644 --- a/pkg/tui/views/dashboard/keys.go +++ b/pkg/tui/views/dashboard/keys.go @@ -18,7 +18,6 @@ type DashboardKeyMap struct { ChangeStatus key.Binding ChangePriority key.Binding ChangeType key.Binding - ChangeAssignee key.Binding AddComment key.Binding AddIssue key.Binding DeleteIssue key.Binding @@ -74,10 +73,6 @@ var defaultDashboardKeyMap = DashboardKeyMap{ key.WithKeys("t"), key.WithHelp("t", "change type"), ), - ChangeAssignee: key.NewBinding( - key.WithKeys("A"), - key.WithHelp("A", "change assignee"), - ), AddComment: key.NewBinding( key.WithKeys("c"), key.WithHelp("c", "add comment"), @@ -121,49 +116,43 @@ func (d *Model) handleKeyMsg(msg tea.KeyMsg) tea.Cmd { case d.IsFocusedOnDetail() && key.Matches(msg, d.keyMap.ScrollDown): d.issueDetail.ScrollDown(1) d.logAction("tui scrolled issue detail down") - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.EditTitle): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.EditTitle): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startEditTitle(selected) cmd = d.titleInput.Focus() d.logAction("tui started editing issue title") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.EditDescription): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.EditDescription): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startEditDescription(selected) cmd = d.descriptionInput.Focus() d.logAction("tui started editing issue description") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangeStatus): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.ChangeStatus): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startChooseStatus(selected) d.logAction("tui opened status picker") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangePriority): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.ChangePriority): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startChoosePriority(selected) d.logAction("tui opened priority picker") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangeType): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.ChangeType): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startChooseType(selected) d.logAction("tui opened type picker") } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.ChangeAssignee): - if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { - d.startEditAssignee(selected) - cmd = d.assigneeInput.Focus() - d.logAction("tui started editing assignee") - } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.AddComment): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.AddComment): if selected := d.FocusedIssueList().SelectedItem(); selected.ID != "" { d.startAddComment(selected) cmd = d.commentInput.Focus() } - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.AddIssue): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.AddIssue): d.startCreateIssue() cmd = d.createTitleInput.Focus() d.logAction("tui started creating issue") - case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.editingAssignee && !d.addingComment && key.Matches(msg, d.keyMap.DeleteIssue): + case !d.editingTitle && !d.creatingIssue && !d.editingDescription && !d.choosingStatus && !d.choosingPriority && !d.confirmingDelete && !d.choosingType && !d.addingComment && key.Matches(msg, d.keyMap.DeleteIssue): fl := d.FocusedIssueList() if selected := fl.SelectedItem(); selected.ID != "" { d.startConfirmDelete(selected.ID, fl.Index()) diff --git a/pkg/tui/views/dashboard/model.go b/pkg/tui/views/dashboard/model.go index 33a2275..de1ce4b 100644 --- a/pkg/tui/views/dashboard/model.go +++ b/pkg/tui/views/dashboard/model.go @@ -47,9 +47,6 @@ type Model struct { priorityIssueID string choosingType bool // true while choosing a type typeIssueID string - editingAssignee bool // true while editing assignee - assigneeInput textinput.Model - assigneeIssueID string addingComment bool // true while adding a comment commentInput textarea.Model commentIssueID string @@ -91,11 +88,6 @@ func NewDashboard(app *app.App, feedbackChan chan models.ValidationFeedback, qui createTi.CharLimit = 256 m.createTitleInput = createTi - assigneeTi := textinput.New() - assigneeTi.Placeholder = "Assignee (e.g. Me, username)..." - assigneeTi.CharLimit = 128 - m.assigneeInput = assigneeTi - descTa := textarea.New() descTa.Placeholder = "Issue description..." descTa.SetWidth(56) @@ -185,13 +177,6 @@ func (m *Model) startChooseType(selected ListIssue) { m.typeIssueID = selected.ID } -func (m *Model) startEditAssignee(selected ListIssue) { - m.editingAssignee = true - m.assigneeIssueID = selected.ID - m.assigneeInput.SetValue(selected.Issue.Assignee) - m.assigneeInput.CursorEnd() -} - func (m *Model) Init() tea.Cmd { return m.listenForValidation() } diff --git a/pkg/tui/views/dashboard/operations.go b/pkg/tui/views/dashboard/operations.go index 6046821..566692e 100644 --- a/pkg/tui/views/dashboard/operations.go +++ b/pkg/tui/views/dashboard/operations.go @@ -49,11 +49,6 @@ type issueTypeUpdatedMsg struct { Err error } -type issueAssigneeUpdatedMsg struct { - IssueID string - Err error -} - type selectIssueMsg struct { IssueID string } @@ -121,14 +116,6 @@ func updateIssueTypeCmd(app *app.App, issueID string, issueType models.IssueType } } -func updateIssueAssigneeCmd(app *app.App, issueID, assignee string) tea.Cmd { - return func() tea.Msg { - updates := map[string]interface{}{"assignee": assignee} - err := app.Issues.UpdateIssue(context.Background(), issueID, updates, "tui") - return issueAssigneeUpdatedMsg{IssueID: issueID, Err: err} - } -} - func createIssueCmd(app *app.App, title string) tea.Cmd { return func() tea.Msg { issue := &models.Issue{ @@ -222,17 +209,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { m.logAction("tui updated issue type") return m, m.refreshIssueListsAndSelectIssue(msg.IssueID) - case issueAssigneeUpdatedMsg: - m.editingAssignee = false - m.assigneeIssueID = "" - m.assigneeInput.Blur() - if msg.Err != nil { - m.logAction("tui failed to update issue assignee") - return m, nil - } - m.logAction("tui updated issue assignee") - return m, m.refreshIssueListsAndSelectIssue(msg.IssueID) - case selectIssueMsg: m.issueList.SelectIssueID(msg.IssueID) m.closedIssueList.SelectIssueID(msg.IssueID) @@ -504,27 +480,6 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { return m, cmd } - if m.editingAssignee { - if msg.String() == "enter" { - assignee := m.assigneeInput.Value() - issueID := m.assigneeIssueID - m.editingAssignee = false - m.assigneeIssueID = "" - m.assigneeInput.Blur() - return m, updateIssueAssigneeCmd(m.app, issueID, assignee) - } - if msg.String() == "esc" { - m.logAction("tui canceled assignee edit") - m.editingAssignee = false - m.assigneeIssueID = "" - m.assigneeInput.Blur() - return m, nil - } - var cmd tea.Cmd - m.assigneeInput, cmd = m.assigneeInput.Update(msg) - return m, cmd - } - if m.editingDescription { if msg.String() == "ctrl+s" { m.logAction("tui submitted issue description edit") diff --git a/pkg/tui/views/dashboard/view.go b/pkg/tui/views/dashboard/view.go index 0d2d14f..74aca51 100644 --- a/pkg/tui/views/dashboard/view.go +++ b/pkg/tui/views/dashboard/view.go @@ -56,20 +56,6 @@ func (m *Model) View() string { mainView := lipgloss.JoinVertical(lipgloss.Left, header, content, footer) - if m.editingAssignee { - editBoxWidth := min(60, m.width-4) - m.assigneeInput.Width = editBoxWidth - 2 - editContent := lipgloss.JoinVertical(lipgloss.Left, - styles.LabelStyle.Render("Change assignee for "+m.assigneeIssueID+" (Enter to save, Esc to cancel):"), - m.assigneeInput.View(), - ) - editBox := styles.ContainerStyle. - Width(editBoxWidth). - BorderForeground(styles.PrimaryBorder). - Render(editContent) - return lipgloss.Place(m.width, m.height, lipgloss.Center, lipgloss.Center, editBox) - } - if m.editingTitle { editBoxWidth := min(60, m.width-4) m.titleInput.Width = editBoxWidth - 2