feat: create new card immediately displays new card

This commit is contained in:
Henry
2024-08-13 14:26:24 +01:00
parent ff5802b953
commit 1e3a903211
3 changed files with 104 additions and 40 deletions

View File

@@ -6,10 +6,12 @@ import React, {
} from "react"; } from "react";
import { api } from "~/utils/api"; import { api } from "~/utils/api";
import { generateUID } from "~/utils/generateUID";
import { usePopup } from "~/providers/popup"; import { usePopup } from "~/providers/popup";
import { import {
type GetBoardByIdOutput, type GetBoardByIdOutput,
type NewCardInput,
type ReorderCardInput, type ReorderCardInput,
type ReorderListInput, type ReorderListInput,
} from "~/types/router.types"; } from "~/types/router.types";
@@ -19,6 +21,8 @@ interface BoardContextProps {
setBoardData: React.Dispatch<React.SetStateAction<GetBoardByIdOutput>>; setBoardData: React.Dispatch<React.SetStateAction<GetBoardByIdOutput>>;
updateList: (params: ReorderListInput) => void; updateList: (params: ReorderListInput) => void;
updateCard: (params: ReorderCardInput) => void; updateCard: (params: ReorderCardInput) => void;
addCard: (params: NewCardInput) => void;
refetchBoard: () => void;
} }
const initialBoardData: GetBoardByIdOutput = { const initialBoardData: GetBoardByIdOutput = {
@@ -44,12 +48,18 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
const { showPopup } = usePopup(); const { showPopup } = usePopup();
const refetchBoard = async () => { const refetchBoard = async () => {
if (boardData?.publicId) { if (!boardData?.publicId) return;
try {
await utils.board.byId.refetch({ boardPublicId: boardData.publicId }); try {
} catch (e) { const data = await utils.board.byId.fetch({
console.error(e); boardPublicId: boardData.publicId,
} });
if (data) setBoardData(data);
} catch (e) {
showPopup({
header: "Error fetching board",
message: "Please try again later, or contact customer support.",
});
} }
}; };
@@ -75,6 +85,50 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
}, },
}); });
const addCard = ({
title,
listPublicId,
labelPublicIds,
memberPublicIds,
position,
}: {
title: string;
listPublicId: string;
labelPublicIds: string[];
memberPublicIds: string[];
position: "start" | "end";
}) => {
if (!boardData) return;
const updatedLists = boardData.lists.map((list) => {
if (list.publicId === listPublicId) {
const newCard = {
publicId: generateUID(),
title,
listId: 2,
description: "",
labels: boardData.labels.filter((label) =>
labelPublicIds.includes(label.publicId),
),
members:
boardData.workspace?.members.filter((member) =>
memberPublicIds.includes(member.publicId),
) || [],
index: position === "start" ? 0 : list.cards.length,
};
const updatedCards =
position === "start"
? [newCard, ...list.cards]
: [...list.cards, newCard];
return { ...list, cards: updatedCards };
}
return list;
});
setBoardData({ ...boardData, lists: updatedLists });
};
const updateList = ({ const updateList = ({
boardId, boardId,
listId, listId,
@@ -99,16 +153,23 @@ export const BoardProvider: React.FC<{ children: ReactNode }> = ({
return ( return (
<BoardContext.Provider <BoardContext.Provider
value={{ boardData, setBoardData, updateList, updateCard }} value={{
boardData,
setBoardData,
updateList,
updateCard,
addCard,
refetchBoard,
}}
> >
{children} {children}
</BoardContext.Provider> </BoardContext.Provider>
); );
}; };
export const useBoard = (): BoardContextProps => { export const useBoard = () => {
const context = useContext(BoardContext); const context = useContext(BoardContext);
if (!context) { if (context === undefined) {
throw new Error("useBoard must be used within a BoardProvider"); throw new Error("useBoard must be used within a BoardProvider");
} }
return context; return context;

View File

@@ -11,8 +11,10 @@ import {
import { Switch } from "@headlessui/react"; import { Switch } from "@headlessui/react";
import CheckboxDropdown from "~/components/CheckboxDropdown"; import CheckboxDropdown from "~/components/CheckboxDropdown";
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 { type NewCardInput } from "~/types/router.types"; import { type NewCardInput } from "~/types/router.types";
@@ -30,7 +32,8 @@ function classNames(...classes: string[]): string {
export function NewCardForm({ listPublicId }: NewCardFormProps) { export function NewCardForm({ listPublicId }: NewCardFormProps) {
const utils = api.useUtils(); const utils = api.useUtils();
const { boardData } = useBoard(); const { boardData, addCard, refetchBoard } = useBoard();
const { showPopup } = usePopup();
const { closeModal } = useModal(); const { closeModal } = useModal();
const { register, handleSubmit, reset, setValue, watch } = const { register, handleSubmit, reset, setValue, watch } =
@@ -50,31 +53,17 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled"); const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
const position = watch("position"); const position = watch("position");
const refetchBoard = async () => {
if (boardData?.publicId) {
try {
await utils.board.byId.refetch({ boardPublicId: boardData.publicId });
} catch (e) {
console.error(e);
}
}
};
const createCard = api.card.create.useMutation({ const createCard = api.card.create.useMutation({
onSuccess: async () => { onSuccess: async () => {
try { refetchBoard();
await refetchBoard(); },
if (!isCreateAnotherEnabled) closeModal(); onError: async () => {
reset({ closeModal();
title: "", refetchBoard();
listPublicId: watch("listPublicId"), showPopup({
labelPublicIds: [], header: "Unable to create card",
memberPublicIds: [], message: "Please try again later, or contact customer support.",
isCreateAnotherEnabled: watch("isCreateAnotherEnabled"), });
});
} catch (e) {
console.log(e);
}
}, },
}); });
@@ -106,6 +95,17 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
})) ?? []; })) ?? [];
const onSubmit = (data: NewCardInput) => { const onSubmit = (data: NewCardInput) => {
addCard(data);
const isCreateAnotherEnabled = watch("isCreateAnotherEnabled");
if (!isCreateAnotherEnabled) closeModal();
reset({
title: "",
listPublicId: watch("listPublicId"),
labelPublicIds: [],
memberPublicIds: [],
isCreateAnotherEnabled,
});
createCard.mutate({ createCard.mutate({
title: data.title, title: data.title,
listPublicId: data.listPublicId, listPublicId: data.listPublicId,
@@ -277,9 +277,10 @@ export function NewCardForm({ listPublicId }: NewCardFormProps) {
</CheckboxDropdown> </CheckboxDropdown>
</div> </div>
<button <button
onClick={() => onClick={(e) => {
setValue("position", position === "start" ? "end" : "start") e.preventDefault();
} setValue("position", position === "start" ? "end" : "start");
}}
className="flex h-auto items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-1.5 py-1 text-left text-xs text-light-800 hover:bg-light-300 dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500" className="flex h-auto items-center rounded-[5px] border-[1px] border-light-600 bg-light-200 px-1.5 py-1 text-left text-xs text-light-800 hover:bg-light-300 dark:border-dark-600 dark:bg-dark-400 dark:text-dark-1000 dark:hover:bg-dark-500"
> >
{position === "start" ? ( {position === "start" ? (

View File

@@ -1,4 +1,4 @@
import { useState } from "react"; import { useState, useEffect } from "react";
import Link from "next/link"; import Link from "next/link";
import { useParams } from "next/navigation"; import { useParams } from "next/navigation";
import { HiOutlinePlusSmall } from "react-icons/hi2"; import { HiOutlinePlusSmall } from "react-icons/hi2";
@@ -61,9 +61,11 @@ export default function BoardPage() {
}, },
); );
if (isSuccess && data) { useEffect(() => {
setBoardData(data); if (isSuccess && data) {
} setBoardData(data);
}
}, [isSuccess, data, setBoardData]);
if (!boardId || !boardData) return <></>; if (!boardId || !boardData) return <></>;