feat: rollback card/list if duplicate index
This commit is contained in:
@@ -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",
|
||||||
|
|||||||
@@ -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 };
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user