feat(ui): add support for editing cards #15
3 changed files with 107 additions and 58 deletions
|
@ -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'
|
||||
|
|
|
@ -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
|
|
@ -26,8 +26,8 @@ const (
|
|||
const (
|
||||
mainPage string = "main"
|
||||
quitPage string = "quit"
|
||||
addPage string = "add"
|
||||
deleteCardPage string = "delete card"
|
||||
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,
|
||||
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
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue