diff --git a/.golangci.yaml b/.golangci.yaml index db69838..4278331 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -13,6 +13,16 @@ output: sort-results: true linters-settings: + depguard: + rules: + main: + files: + - $all + allow: + - $gostd + - codeflow.dananglin.me.uk/apollo/pelican + - github.com/gdamore/tcell/v2 + - github.com/rivo/tview exhaustivestruct: struct-patterns: - 'forge.dananglin.me.uk/code/dananglin/pelican.Status' diff --git a/internal/ui/modalinput.go b/internal/ui/cardmodal.go similarity index 69% rename from internal/ui/modalinput.go rename to internal/ui/cardmodal.go index 2b2b91b..2cfca1a 100644 --- a/internal/ui/modalinput.go +++ b/internal/ui/cardmodal.go @@ -5,15 +5,23 @@ import ( "github.com/rivo/tview" ) -type modalInput struct { +type cardModalMode int + +const ( + create cardModalMode = iota + edit +) + +type cardModal struct { *tview.Form frame *tview.Frame title string description string - done func(string, string, bool) + done func(string, string, bool, cardModalMode) + mode cardModalMode } -func newModalInput() *modalInput { +func newCardModal() *cardModal { var ( background = tcell.ColorBlack.TrueColor() buttonBackground = tcell.ColorBlueViolet.TrueColor() @@ -24,12 +32,13 @@ func newModalInput() *modalInput { form = tview.NewForm() ) - modal := modalInput{ + modal := cardModal{ Form: form, frame: tview.NewFrame(form), title: "", description: "", done: nil, + mode: create, } // Stylise the buttons @@ -51,49 +60,39 @@ func newModalInput() *modalInput { SetBackgroundColor(background). SetBorderPadding(1, 1, 1, 1) - modal.AddButton("Create card", func() { + modal.AddButton("Save", func() { if modal.done != nil { - modal.done(modal.title, modal.description, true) + modal.done(modal.title, modal.description, true, modal.mode) } - modal.reset() }) modal.AddButton("Cancel", func() { if modal.done != nil { - modal.done(modal.title, modal.description, false) + modal.done(modal.title, modal.description, false, modal.mode) } - modal.reset() }) - modal.addInputFields() - - modal.frame.SetTitle(" New Card ") - return &modal } -func (m *modalInput) reset() { +func (m *cardModal) updateInputFields(title, description string) { m.Clear(false) - m.addInputFields() -} - -func (m *modalInput) addInputFields() { - m.AddInputField("Title", "", 60, nil, func(text string) { + m.AddInputField("Title", title, 60, nil, func(text string) { m.title = text }) - m.AddTextArea("Description", "", 60, 10, 0, func(text string) { + m.AddTextArea("Description", description, 60, 10, 0, func(text string) { m.description = text }) } -func (m *modalInput) SetDoneFunc(handler func(string, string, bool)) *modalInput { +func (m *cardModal) setDoneFunc(handler func(string, string, bool, cardModalMode)) *cardModal { m.done = handler return m } -func (m *modalInput) Draw(screen tcell.Screen) { +func (m *cardModal) Draw(screen tcell.Screen) { buttonsWidth := 20 screenWidth, screenHeight := screen.Size() width := screenWidth / 3 diff --git a/internal/ui/ui.go b/internal/ui/ui.go index f87ecb5..df221ac 100644 --- a/internal/ui/ui.go +++ b/internal/ui/ui.go @@ -24,10 +24,10 @@ const ( ) const ( - mainPage string = "main" - quitPage string = "quit" - addPage string = "add" - deleteCardPage string = "delete card" + mainPage string = "main" + quitPage string = "quit" + cardModalPage string = "card modal" + deleteCardModalPage string = "delete card modal" ) type UI struct { @@ -40,7 +40,7 @@ type UI struct { board board.Board mode boardMode quitModal *tview.Modal - addModal *modalInput + cardModal *cardModal deleteCardModal *tview.Modal statusSelection statusSelection } @@ -57,7 +57,7 @@ func NewUI(path string) (UI, error) { pages: tview.NewPages(), flex: tview.NewFlex(), quitModal: tview.NewModal(), - addModal: newModalInput(), + cardModal: newCardModal(), focusedColumn: 0, columns: nil, board: kanban, @@ -95,8 +95,10 @@ func (u *UI) init() error { u.shiftColumnFocus(next) case letter == 'c': if u.mode == normal { - u.pages.ShowPage(addPage) - u.SetFocus(u.addModal) + u.cardModal.mode = create + u.cardModal.updateInputFields("", "") + u.pages.ShowPage(cardModalPage) + u.SetFocus(u.cardModal) } case letter == 'm': if u.mode == normal { @@ -105,9 +107,19 @@ func (u *UI) init() error { u.statusSelection.currentStatusID = u.columns[u.focusedColumn].statusID u.mode = selection } + case letter == 'e': + if u.mode == normal { + u.cardModal.mode = edit + focusedCard := u.columns[u.focusedColumn].focusedCard + cardID := u.columns[u.focusedColumn].cards[focusedCard].id + card, _ := u.board.Card(cardID) + u.cardModal.updateInputFields(card.Title, card.Description) + u.pages.ShowPage(cardModalPage) + u.SetFocus(u.cardModal) + } case key == tcell.KeyCtrlD: if u.mode == normal { - u.pages.ShowPage(deleteCardPage) + u.pages.ShowPage(deleteCardModalPage) u.SetFocus(u.deleteCardModal) } case key == tcell.KeyCtrlQ: @@ -139,11 +151,11 @@ func (u *UI) init() error { u.initQuitModal() u.pages.AddPage(quitPage, u.quitModal, false, false) - u.initAddInputModal() - u.pages.AddPage(addPage, u.addModal, false, false) + u.initCardModal() + u.pages.AddPage(cardModalPage, u.cardModal, false, false) u.initDeleteCardModal() - u.pages.AddPage(deleteCardPage, u.deleteCardModal, false, false) + u.pages.AddPage(deleteCardModalPage, u.deleteCardModal, false, false) u.SetRoot(u.pages, true) @@ -154,18 +166,25 @@ func (u *UI) init() error { return nil } -// initAddInputModal initialises the add input modal. -func (u *UI) initAddInputModal() { - doneFunc := func(text, description string, success bool) { +// initCardModal initialises the card modal. +func (u *UI) initCardModal() { + doneFunc := func(title, description string, success bool, mode cardModalMode) { if success { - _ = u.newCard(text, description) + switch mode { + case create: + _ = u.newCard(title, description) + case edit: + focusedCard := u.columns[u.focusedColumn].focusedCard + cardID := u.columns[u.focusedColumn].cards[focusedCard].id + _ = u.editCard(cardID, title, description) + } } - u.pages.HidePage(addPage) + u.pages.HidePage(cardModalPage) u.setColumnFocus() } - u.addModal.SetDoneFunc(doneFunc) + u.cardModal.setDoneFunc(doneFunc) } // initDeleteCardModal initialises the modal for deleting cards. @@ -176,7 +195,7 @@ func (u *UI) initDeleteCardModal() { _ = u.refresh(true) } - u.pages.HidePage(deleteCardPage) + u.pages.HidePage(deleteCardModalPage) u.setColumnFocus() } @@ -185,20 +204,6 @@ func (u *UI) initDeleteCardModal() { SetDoneFunc(doneFunc) } -// deleteCard deletes a card from the board. -func (u *UI) deleteCard() { - statusID := u.columns[u.focusedColumn].statusID - focusedCard := u.columns[u.focusedColumn].focusedCard - cardID := u.columns[u.focusedColumn].cards[focusedCard].id - - args := board.DeleteCardArgs{ - CardID: cardID, - StatusID: statusID, - } - - _ = u.board.DeleteCard(args) -} - // initQuitModal initialises the quit modal. func (u *UI) initQuitModal() { doneFunc := func(_ int, buttonLabel string) { @@ -217,10 +222,10 @@ func (u *UI) initQuitModal() { } // newCard creates and saves a new card to the database. -func (u *UI) newCard(title, content string) error { +func (u *UI) newCard(title, description string) error { args := board.CardArgs{ - NewTitle: title, - NewDescription: content, + NewTitle: title, + NewDescription: description, } if _, err := u.board.CreateCard(args); err != nil { @@ -232,6 +237,40 @@ func (u *UI) newCard(title, content string) error { return nil } +// editCard saves and edited card to the database. +func (u *UI) editCard(cardID int, title, description string) error { + args := board.UpdateCardArgs{ + CardID: cardID, + CardArgs: board.CardArgs{ + NewTitle: title, + NewDescription: description, + }, + } + + if err := u.board.UpdateCard(args); err != nil { + return fmt.Errorf("unable to edit card with ID: %d; %w", cardID, err) + } + + _ = u.refresh(true) + + return nil +} + +// deleteCard deletes a card from the board. +func (u *UI) deleteCard() { + statusID := u.columns[u.focusedColumn].statusID + focusedCard := u.columns[u.focusedColumn].focusedCard + cardID := u.columns[u.focusedColumn].cards[focusedCard].id + + args := board.DeleteCardArgs{ + CardID: cardID, + StatusID: statusID, + } + + _ = u.board.DeleteCard(args) +} + +// initColumns initialises the columns of the Kanban board. func (u *UI) initColumns() error { u.flex.Clear() @@ -317,6 +356,7 @@ func (u *UI) shutdown() { u.Stop() } +// boardMode returns the current board mode. func (u *UI) boardMode() boardMode { return u.mode }