Skip to content

Commit

Permalink
Add poker story drag and drop arranging (#459)
Browse files Browse the repository at this point in the history
This resolves #434
  • Loading branch information
StevenWeathers authored Aug 20, 2023
1 parent 69d2694 commit e1312e1
Show file tree
Hide file tree
Showing 12 changed files with 404 additions and 113 deletions.
18 changes: 18 additions & 0 deletions db/migrations/20230820032023_add_poker_story_sorting.down.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
ALTER TABLE thunderdome.poker_story DROP COLUMN position;

CREATE OR REPLACE PROCEDURE thunderdome.poker_story_delete(IN pokerid uuid, IN storyid uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
active_storyid UUID;
BEGIN
active_storyid := (SELECT b.active_story_id FROM thunderdome.poker b WHERE b.id = pokerid);
DELETE FROM thunderdome.poker_story WHERE id = storyid;

IF active_storyid = storyid THEN
UPDATE thunderdome.poker SET last_active = NOW(), voting_locked = true, active_story_id = null
WHERE id = pokerid;
END IF;

COMMIT;
END$procedure$;
46 changes: 46 additions & 0 deletions db/migrations/20230820032023_add_poker_story_sorting.up.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
ALTER TABLE thunderdome.poker_story ADD COLUMN position DOUBLE PRECISION DEFAULT 0 NOT NULL;

DO $$ DECLARE
story RECORD;
pid UUID;
pos DOUBLE PRECISION = 0;
BEGIN
FOR story IN SELECT id, poker_id, created_date FROM thunderdome.poker_story ORDER BY poker_id, created_date
LOOP
IF pid IS NULL OR story.poker_id <> pid THEN
pid = story.poker_id;
pos = -1;
END IF;
pos = pos + 1;

UPDATE thunderdome.poker_story SET position = pos WHERE id = story.id;
END LOOP;
END$$;

ALTER TABLE thunderdome.poker_story ADD CONSTRAINT poker_story_poker_id_position UNIQUE (poker_id, position);

CREATE OR REPLACE PROCEDURE thunderdome.poker_story_delete(IN pokerid uuid, IN storyid uuid)
LANGUAGE plpgsql
AS $procedure$
DECLARE
active_storyid UUID;
story RECORD;
pos DOUBLE PRECISION = -1;
BEGIN
active_storyid := (SELECT b.active_story_id FROM thunderdome.poker b WHERE b.id = pokerid);
DELETE FROM thunderdome.poker_story WHERE id = storyid;

FOR story IN SELECT id, position FROM thunderdome.poker_story WHERE poker_id = pokerid ORDER BY position
LOOP
pos = pos + 1;

UPDATE thunderdome.poker_story SET position = pos WHERE id = story.id;
END LOOP;

IF active_storyid = storyid THEN
UPDATE thunderdome.poker SET last_active = NOW(), voting_locked = true, active_story_id = null
WHERE id = pokerid;
END IF;

COMMIT;
END$procedure$;
74 changes: 69 additions & 5 deletions db/poker/story.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,10 @@ func (d *Service) GetStories(PokerID string, UserID string) []*thunderdome.Story
var plans = make([]*thunderdome.Story, 0)
planRows, plansErr := d.DB.Query(
`SELECT
id, name, type, reference_id, link, description, acceptance_criteria, priority, points, active, skipped, votestart_time, voteend_time, votes
FROM thunderdome.poker_story WHERE poker_id = $1 ORDER BY created_date
id, name, type, reference_id, link, description, acceptance_criteria, priority,
points, active, skipped, votestart_time, voteend_time, votes,
row_number() OVER (ORDER BY position ASC) as position
FROM thunderdome.poker_story WHERE poker_id = $1 ORDER BY position
`,
PokerID,
)
Expand All @@ -33,7 +35,8 @@ func (d *Service) GetStories(PokerID string, UserID string) []*thunderdome.Story
Skipped: false,
}
if err := planRows.Scan(
&p.Id, &p.Name, &p.Type, &ReferenceID, &Link, &Description, &AcceptanceCriteria, &p.Priority, &p.Points, &p.Active, &p.Skipped, &p.VoteStartTime, &p.VoteEndTime, &v,
&p.Id, &p.Name, &p.Type, &ReferenceID, &Link, &Description, &AcceptanceCriteria, &p.Priority,
&p.Points, &p.Active, &p.Skipped, &p.VoteStartTime, &p.VoteEndTime, &v, &p.Position,
); err != nil {
d.Logger.Error("get poker stories query error", zap.Error(err))
} else {
Expand Down Expand Up @@ -70,8 +73,14 @@ func (d *Service) CreateStory(PokerID string, Name string, Type string, Referenc
Priority = 99
}
if _, err := d.DB.Exec(
`INSERT INTO thunderdome.poker_story (poker_id, name, type, reference_id, link, description, acceptance_criteria, priority)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8);`,
`INSERT INTO thunderdome.poker_story (
poker_id, name, type, reference_id, link, description, acceptance_criteria, priority, position)
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, (
coalesce(
(select max(position) from thunderdome.poker_story where poker_id = $1),
-1
) + 1
));`,
PokerID, Name, Type, ReferenceID, Link, SanitizedDescription, SanitizedAcceptanceCriteria, Priority,
); err != nil {
d.Logger.Error("error creating poker story", zap.Error(err))
Expand Down Expand Up @@ -230,6 +239,61 @@ func (d *Service) DeleteStory(PokerID string, StoryID string) ([]*thunderdome.St
return plans, nil
}

// ArrangeStory sets the position of the story relative to the story it's being placed before
func (d *Service) ArrangeStory(PokerID string, StoryID string, BeforeStoryID string) ([]*thunderdome.Story, error) {
if BeforeStoryID == "" {
_, err := d.DB.Exec(`UPDATE thunderdome.poker_story SET
position = (SELECT max(position) FROM thunderdome.poker_story WHERE poker_id = $1) + 1
WHERE id = $2;`,
PokerID, StoryID)
if err != nil {
d.Logger.Error("poker ArrangeStory get beforeStoryId error", zap.Error(err))
}
} else {
_, err := d.DB.Exec(
`UPDATE thunderdome.poker_story SET position = (
-- find position of item referenced in before argument (default to 0)
with "before_position" as (
select coalesce(
(select "position" from thunderdome.poker_story where id = $3),
-- in case item was not found, use last item in list and add 1 to add item to end of list
(select max("position") + 1 from thunderdome.poker_story where poker_id = $1),
-- in case no item exists, use 0
0
) as "position"
),
-- find position of previous item relative to "before item"
"before_prev_position" as (
select coalesce(
(
select w."position"
from thunderdome.poker_story w, "before_position" b
where
w.poker_id = $1 and
-- positions may not be integers, so we cannot simply deduct 1
-- this is why we find the first item with a smaller position
w."position" < b."position"
order by "position" desc limit 1
),
-- in case previous position does not exist (before item was first in list), simply deduct 1
(select b.position - 1 from "before_position" b)
) as "position"
)
-- average both positions to fit new row into gap
select (b.position + p.position) / 2
from "before_position" b, "before_prev_position" p
) WHERE id = $2;`,
PokerID, StoryID, BeforeStoryID)
if err != nil {
d.Logger.Error("poker ArrangeStory error", zap.Error(err))
}
}

plans := d.GetStories(PokerID, "")

return plans, nil
}

// FinalizeStory sets story to active: false and updates the points
func (d *Service) FinalizeStory(PokerID string, StoryID string, Points string) ([]*thunderdome.Story, error) {
if _, err := d.DB.Exec(
Expand Down
3 changes: 3 additions & 0 deletions docs/swagger/docs.go
Original file line number Diff line number Diff line change
Expand Up @@ -7970,6 +7970,9 @@ const docTemplate = `{
"points": {
"type": "string"
},
"position": {
"type": "integer"
},
"priority": {
"type": "integer"
},
Expand Down
3 changes: 3 additions & 0 deletions docs/swagger/swagger.json
Original file line number Diff line number Diff line change
Expand Up @@ -7961,6 +7961,9 @@
"points": {
"type": "string"
},
"position": {
"type": "integer"
},
"priority": {
"type": "integer"
},
Expand Down
2 changes: 2 additions & 0 deletions docs/swagger/swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,8 @@ definitions:
type: string
points:
type: string
position:
type: integer
priority:
type: integer
referenceId:
Expand Down
Loading

0 comments on commit e1312e1

Please sign in to comment.