feat: rollback card/list if duplicate index

This commit is contained in:
Henry
2024-11-20 18:18:57 +00:00
parent ce9a1f5148
commit b7c88e844b
4 changed files with 64 additions and 20 deletions

View File

@@ -210,7 +210,7 @@ export const boardRouter = createTRPCRouter({
deletedBy: userId, deletedBy: userId,
}); });
if (!deletedLists?.length) { if (!Array.isArray(deletedLists)) {
throw new TRPCError({ throw new TRPCError({
message: `Failed to delete lists`, message: `Failed to delete lists`,
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
@@ -223,7 +223,7 @@ export const boardRouter = createTRPCRouter({
deletedBy: userId, deletedBy: userId,
}); });
if (!deletedCards?.length) { if (!Array.isArray(deletedCards)) {
throw new TRPCError({ throw new TRPCError({
message: `Failed to delete cards`, message: `Failed to delete cards`,
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",

View File

@@ -53,7 +53,8 @@ export const listRouter = createTRPCRouter({
name: input.name, name: input.name,
createdBy: userId, createdBy: userId,
boardId: board.id, boardId: board.id,
index: latestListIndex ? latestListIndex + 1 : 0, index:
latestListIndex || latestListIndex === 0 ? latestListIndex + 1 : 0,
}); });
if (!result) if (!result)
@@ -161,7 +162,7 @@ export const listRouter = createTRPCRouter({
deletedBy: userId, deletedBy: userId,
}); });
if (!deletedCards?.length) if (!Array.isArray(deletedCards))
throw new TRPCError({ throw new TRPCError({
message: `Failed to delete cards`, message: `Failed to delete cards`,
code: "INTERNAL_SERVER_ERROR", code: "INTERNAL_SERVER_ERROR",
@@ -177,7 +178,7 @@ export const listRouter = createTRPCRouter({
await listRepo.shiftIndex(ctx.db, { await listRepo.shiftIndex(ctx.db, {
boardId: list.boardId, boardId: list.boardId,
listIndex: list.id, listIndex: list.index,
}); });
return { success: true }; return { success: true };

View File

@@ -1,6 +1,7 @@
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import { useBoard } from "~/providers/board"; import { useBoard } from "~/providers/board";
import { useModal } from "~/providers/modal"; import { useModal } from "~/providers/modal";
import { usePopup } from "~/providers/popup";
import Button from "~/components/Button"; import Button from "~/components/Button";
@@ -14,6 +15,7 @@ export function DeleteListConfirmation({
const utils = api.useUtils(); const utils = api.useUtils();
const { boardData } = useBoard(); const { boardData } = useBoard();
const { closeModal } = useModal(); const { closeModal } = useModal();
const { showPopup } = usePopup();
const refetchBoard = async () => { const refetchBoard = async () => {
if (boardData?.publicId) { if (boardData?.publicId) {
@@ -30,6 +32,14 @@ export function DeleteListConfirmation({
closeModal(); closeModal();
return refetchBoard(); return refetchBoard();
}, },
onError: async () => {
closeModal();
await refetchBoard();
showPopup({
header: "Unable to delete list",
message: "Please try again later, or contact customer support.",
});
},
}); });
return ( return (
@@ -46,7 +56,10 @@ export function DeleteListConfirmation({
<Button onClick={() => closeModal()} variant="secondary"> <Button onClick={() => closeModal()} variant="secondary">
Cancel Cancel
</Button> </Button>
<Button onClick={() => deleteList.mutate({ listPublicId })}> <Button
isLoading={deleteList.isPending}
onClick={() => deleteList.mutate({ listPublicId })}
>
Delete Delete
</Button> </Button>
</div> </div>

View File

@@ -1,20 +1,36 @@
CREATE OR REPLACE FUNCTION reorder_lists(board_id BIGINT, list_id BIGINT, current_index INT, new_index INT) CREATE OR REPLACE FUNCTION reorder_lists(board_id BIGINT, list_id BIGINT, current_index INT, new_index INT)
RETURNS VOID RETURNS BOOLEAN
LANGUAGE SQL LANGUAGE PLPGSQL
AS $$ AS $$
UPDATE list BEGIN
SET index = UPDATE list
CASE SET index =
WHEN index = current_index AND id = list_id THEN new_index CASE
WHEN current_index < new_index AND index > current_index AND index <= new_index THEN index - 1 WHEN index = current_index AND id = list_id THEN new_index
WHEN current_index > new_index AND index >= new_index AND index < current_index THEN index + 1 WHEN current_index < new_index AND index > current_index AND index <= new_index THEN index - 1
ELSE index WHEN current_index > new_index AND index >= new_index AND index < current_index THEN index + 1
END ELSE index
WHERE "boardId" = board_id; END
WHERE "boardId" = board_id;
-- Check for duplicate indices after the update
IF EXISTS (
SELECT index, COUNT(*)
FROM list
WHERE "boardId" = board_id
AND "deletedAt" IS NULL
GROUP BY index
HAVING COUNT(*) > 1
) THEN
RAISE EXCEPTION 'Duplicate indices found after reordering in board %', board_id;
END IF;
RETURN TRUE;
END;
$$; $$;
CREATE OR REPLACE FUNCTION reorder_cards(card_id BIGINT, current_list_id BIGINT, new_list_id BIGINT, current_index INT, new_index INT) CREATE OR REPLACE FUNCTION reorder_cards(card_id BIGINT, current_list_id BIGINT, new_list_id BIGINT, current_index INT, new_index INT)
RETURNS VOID RETURNS BOOLEAN
LANGUAGE PLPGSQL LANGUAGE PLPGSQL
AS $$ AS $$
DECLARE DECLARE
@@ -45,8 +61,22 @@ AS $$
SET "listId" = new_list_id, index = new_index SET "listId" = new_list_id, index = new_index
WHERE id = card_id AND "deletedAt" IS NULL; WHERE id = card_id AND "deletedAt" IS NULL;
END IF; END IF;
-- Check for duplicate indices in both affected lists
IF EXISTS (
SELECT index, COUNT(*)
FROM card
WHERE "listId" IN (current_list_id, new_list_id)
AND "deletedAt" IS NULL
GROUP BY "listId", index
HAVING COUNT(*) > 1
) THEN
RAISE EXCEPTION 'Duplicate indices found after reordering in list % or %', current_list_id, new_list_id;
END IF;
RETURN TRUE;
END; END;
$$ $$;
CREATE OR REPLACE FUNCTION shift_list_index(board_id BIGINT, list_index INT) CREATE OR REPLACE FUNCTION shift_list_index(board_id BIGINT, list_index INT)
RETURNS VOID RETURNS VOID