From 4286d8fcc2c8cce5b5f47291312af6fbaaf3d40b Mon Sep 17 00:00:00 2001 From: Dan Anglin Date: Tue, 23 Jan 2024 23:58:04 +0000 Subject: [PATCH] checkpoint: column repositioning implementation --- internal/board/board.go | 41 ++++++++++++++++++++++++++------ internal/board/shuffle.go | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 7 deletions(-) create mode 100644 internal/board/shuffle.go diff --git a/internal/board/board.go b/internal/board/board.go index a4cc16e..45c10ad 100644 --- a/internal/board/board.go +++ b/internal/board/board.go @@ -11,6 +11,7 @@ import ( bolt "go.etcd.io/bbolt" ) +// Board is probably the heart of Pelican. type Board struct { db *bolt.DB } @@ -90,9 +91,7 @@ func (b *Board) StatusList() ([]Status, error) { return statuses, nil } -// Status returns a single status from the db. -// TODO: Add a test case that handles when a status does not exist. -// Or use in delete status case. +// Status returns a single status from the database. func (b *Board) Status(statusID int) (Status, error) { data, err := db.Read(b.db, db.StatusBucket, statusID) if err != nil { @@ -116,6 +115,7 @@ func (b *Board) Status(statusID int) (Status, error) { return status, nil } +// StatusArgs is an argument type for creating or updating statuses. type StatusArgs struct { Name string Position int @@ -198,21 +198,44 @@ func (b *Board) DeleteStatus(statusID int) error { return fmt.Errorf("unable to delete the status from the database; %w", err) } - if err := b.normaliseStatusesPositionValues(); err != nil { + if err := b.normaliseStatusesPositionValuesFromDatabase(); err != nil { return fmt.Errorf("unable to normalise the statuses position values; %w", err) } return nil } -// normaliseStatusesPositionValues retrieves the ordered list of statuses from the database and sets -// each status' positional value based on its position in the list. -func (b *Board) normaliseStatusesPositionValues() error { +// RepositionStatus re-positions a status on the Kanban board. +func (b *Board) RepositionStatus(currentPosition, targetPosition int) error { statuses, err := b.StatusList() if err != nil { return fmt.Errorf("unable to get the list of statuses; %w", err) } + statuses = moveAndShuffle(statuses, currentPosition, targetPosition) + + if err := b.normaliseStatusesPositionValues(statuses); err != nil { + return fmt.Errorf("unable to normalise the statuses position values; %w", err) + } + + return nil +} + +// normaliseStatusesPositionValuesFromDatabase retrieves the ordered list of statuses from the database and sets +// each status' positional value based on its position in the list before saving the updates to the database. +func (b *Board) normaliseStatusesPositionValuesFromDatabase() error { + statuses, err := b.StatusList() + if err != nil { + return fmt.Errorf("unable to get the list of statuses; %w", err) + } + + return b.normaliseStatusesPositionValues(statuses) +} + +// normaliseStatusesPositionValues takes a list of statuses and sets +// each status' positional value based on its position in the list before +// saving the updates to the database. +func (b *Board) normaliseStatusesPositionValues(statuses []Status) error { for i, status := range statuses { updateArgs := UpdateStatusArgs{ StatusID: status.ID, @@ -230,6 +253,7 @@ func (b *Board) normaliseStatusesPositionValues() error { return nil } +// MoveToStatusArgs is an argument type for moving a card between statuses. type MoveToStatusArgs struct { CardID int CurrentStatusID int @@ -260,6 +284,7 @@ func (b *Board) MoveToStatus(args MoveToStatusArgs) error { return nil } +// CardArgs is an argument type for creating or updating cards. type CardArgs struct { NewTitle string NewDescription string @@ -360,6 +385,7 @@ func (b *Board) CardList(ids []int) ([]Card, error) { return cards, nil } +// UpdateCardArgs is an argument type for updating a card. type UpdateCardArgs struct { CardID int CardArgs @@ -387,6 +413,7 @@ func (b *Board) UpdateCard(args UpdateCardArgs) error { return nil } +// DeleteCardArgs is an argument type for deleting a card. type DeleteCardArgs struct { CardID int StatusID int diff --git a/internal/board/shuffle.go b/internal/board/shuffle.go new file mode 100644 index 0000000..8375736 --- /dev/null +++ b/internal/board/shuffle.go @@ -0,0 +1,49 @@ +package board + +type shuffleDirection int + +const ( + forwards shuffleDirection = iota + backwards +) + +// moveAndShuffle creates a new list where the element specified at the index of the current +// position is moved to the index of the target position. Elements within the range of the +// old and new positions will then shuffle forwards or backwards in the list depending on the +// direction of the move. +// This is currently used to move specified columns forwards or backwards on the Kanban board. +// When a column changes position the other columns shuffle forward or backwards as required. +func moveAndShuffle(input []Status, currentPosition, targetPosition int) []Status { + var shuffle shuffleDirection + + if targetPosition < currentPosition { + // affected elements will need to shuffle backwards if the focused + // element moves towards the beginning of the list... + shuffle = backwards + } else { + // ...or else the elements need to shuffle forwards if the focused + // element moves towards the end of the list. + shuffle = forwards + } + + output := make([]Status, len(input)) + + for ind := range input { + switch { + case (shuffle == backwards) && (ind < targetPosition || ind > currentPosition): + output[ind] = input[ind] + case (shuffle == backwards) && (ind == currentPosition): + output[targetPosition] = input[ind] + case (shuffle == backwards): + output[ind+1] = input[ind] + case (shuffle == forwards) && (ind < currentPosition || ind > targetPosition): + output[ind] = input[ind] + case (shuffle == forwards) && (ind == currentPosition): + output[targetPosition] = input[ind] + case (shuffle == forwards): + output[ind-1] = input[ind] + } + } + + return output +}