diff --git a/internal/ui/column.go b/internal/ui/column.go index 75dcb2b..f3d5a52 100644 --- a/internal/ui/column.go +++ b/internal/ui/column.go @@ -100,6 +100,8 @@ func (c *column) update(kanban board.Board) error { } } + c.statusName = status.Name + c.SetTitle(fmt.Sprintf(" %s (%d) ", c.statusName, numCardIDs)) return nil diff --git a/internal/ui/init.go b/internal/ui/init.go index 789b717..60b420d 100644 --- a/internal/ui/init.go +++ b/internal/ui/init.go @@ -97,6 +97,7 @@ func (a *App) initCardView() { a.cardView.setDoneFunc(doneFunc) } +// initStatusbar initialises the status bar. func (a *App) initStatusbar() { changedFunc := func() { a.Draw() @@ -104,3 +105,16 @@ func (a *App) initStatusbar() { a.statusbar.SetChangedFunc(changedFunc) } + +func (a *App) initStatusForm() { + doneFunc := func(name string, success bool) { + if success { + a.editFocusedStatusColumn(name) + } + + a.pages.HidePage(statusFormPage) + a.setColumnFocus() + } + + a.statusForm.setDoneFunc(doneFunc) +} diff --git a/internal/ui/keymappings.go b/internal/ui/keymappings.go index 72e9bae..a33f90a 100644 --- a/internal/ui/keymappings.go +++ b/internal/ui/keymappings.go @@ -8,19 +8,21 @@ import ( func (a *App) inputCapture() func(event *tcell.EventKey) *tcell.EventKey { return func(event *tcell.EventKey) *tcell.EventKey { - key, letter := event.Key(), event.Rune() + key, character := event.Key(), event.Rune() switch { - case letter == 'h' || key == tcell.KeyLeft: + case character == 'h' || key == tcell.KeyLeft: a.shiftColumnFocus(previous) - case letter == 'l' || key == tcell.KeyRight: + case character == 'l' || key == tcell.KeyRight: a.shiftColumnFocus(next) - case letter == 'c': + case character == 'c': a.create() - case letter == 'm': + case character == 'm': a.move() - case letter == 'e': + case character == 'e': a.edit() + case character == 'b': + a.updateBoardMode(boardEdit) case key == tcell.KeyCtrlD: a.delete() case key == tcell.KeyCtrlQ: @@ -54,7 +56,8 @@ func (a *App) move() { } func (a *App) edit() { - if a.mode == normal { + switch a.mode { + case normal: a.cardForm.mode = edit card, ok := a.getFocusedCard() @@ -66,6 +69,12 @@ func (a *App) edit() { a.cardForm.frame.SetTitle(" Edit Card ") a.pages.ShowPage(cardFormPage) a.SetFocus(a.cardForm) + case boardEdit: + statusName := a.focusedStatusName() + a.statusForm.updateInputFields(statusName) + a.statusForm.frame.SetTitle(" Edit Status Name ") + a.pages.ShowPage(statusFormPage) + a.SetFocus(a.statusForm) } } diff --git a/internal/ui/statusform.go b/internal/ui/statusform.go new file mode 100644 index 0000000..b36324d --- /dev/null +++ b/internal/ui/statusform.go @@ -0,0 +1,103 @@ +package ui + +import ( + "github.com/gdamore/tcell/v2" + "github.com/rivo/tview" +) + +type statusForm struct { + *tview.Form + frame *tview.Frame + nameLabel string + done func(string, bool) +} + +func newStatusForm() *statusForm { + var ( + background = tcell.ColorBlack.TrueColor() + buttonBackground = tcell.ColorBlueViolet.TrueColor() + textColour = tcell.ColorWhite.TrueColor() + fieldBackground = tcell.ColorGrey.TrueColor() + labelColour = tcell.ColorGreen.TrueColor() + + form = tview.NewForm() + ) + + obj := statusForm{ + Form: form, + frame: tview.NewFrame(form), + done: nil, + nameLabel: "Name", + } + + // Stylise the buttons + obj.SetButtonsAlign(tview.AlignCenter). + SetButtonBackgroundColor(buttonBackground). + SetButtonTextColor(textColour). + SetBorderPadding(0, 0, 0, 0) + + // Stylise the form + obj.SetLabelColor(labelColour). + SetFieldBackgroundColor(fieldBackground). + SetFieldTextColor(textColour). + SetBackgroundColor(background) + + // Stylise the frame around the form + obj.frame.SetBorders(0, 0, 1, 0, 0, 0). + SetBorder(true). + SetBorderColor(tcell.ColorOrangeRed.TrueColor()). + SetBackgroundColor(background). + SetBorderPadding(1, 1, 1, 1) + + obj.AddButton("Save", func() { + if obj.done != nil { + name, ok := obj.GetFormItemByLabel(obj.nameLabel).(*tview.InputField) + if !ok { + return + } + + obj.done(name.GetText(), true) + } + }) + + obj.AddButton("Cancel", func() { + if obj.done != nil { + obj.done("", false) + } + }) + + return &obj +} + +func (s *statusForm) updateInputFields(name string) { + s.Clear(false) + s.AddInputField(s.nameLabel, name, 0, nil, nil) +} + +func (s *statusForm) setDoneFunc(handler func(string, bool)) *statusForm { + s.done = handler + + return s +} + +func (s *statusForm) Draw(screen tcell.Screen) { + buttonsWidth := 20 + screenWidth, screenHeight := screen.Size() + width := screenWidth / 3 + + if width < buttonsWidth { + width = buttonsWidth + } + + height := 20 + width += 4 + + // Set the form's position and size. + x := (screenWidth - width) / 2 + y := (screenHeight - height) / 2 + s.SetRect(x, y, width, height) + + // Draw the frame. + s.frame.SetRect(x, y, width, height) + s.frame.Draw(screen) +} diff --git a/internal/ui/ui.go b/internal/ui/ui.go index fc460d5..7777707 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -23,9 +23,13 @@ const ( cardFormPage string = "card form" deleteCardModalPage string = "delete card modal" viewPage string = "view" + statusFormPage string = "status form" +) +const ( normal boardMode = "NORMAL" selection boardMode = "SELECTION" + boardEdit boardMode = "BOARD EDIT" ) type App struct { @@ -45,6 +49,7 @@ type App struct { statusSelection statusSelection cardView *cardView statusbar *statusbar + statusForm *statusForm } // NewApp returns a new App value. @@ -70,6 +75,7 @@ func NewApp(path string) (App, error) { statusSelection: statusSelection{0, 0, 0}, cardView: newCardView(), statusbar: newStatusbar(), + statusForm: newStatusForm(), } return app, nil @@ -106,6 +112,9 @@ func (a *App) Init() error { a.initCardView() a.pages.AddPage(viewPage, a.cardView, false, false) + a.initStatusForm() + a.pages.AddPage(statusFormPage, a.statusForm, false, false) + a.SetRoot(a.pages, true) a.refresh(false) @@ -298,3 +307,25 @@ func (a *App) focusedStatusName() string { func (a *App) closeBoard() { _ = a.board.Close() } + +// editFocusedStatusColumn updates the status column in focus and saves the changes to the database. +func (a *App) editFocusedStatusColumn(newName string) { + statusID := a.focusedStatusID() + + args := board.UpdateStatusArgs{ + StatusID: statusID, + StatusArgs: board.StatusArgs{ + Name: newName, + }, + } + + if err := a.board.UpdateStatus(args); err != nil { + a.statusbar.displayMessage(errorLevel, fmt.Sprintf("Failed to edit status: %v.", err)) + + return + } + + a.statusbar.displayMessage(infoLevel, "Status updated successfully.") + + a.refresh(true) +}