From 01cba04a59cbfd459d54757020fa1a72f401d9e3 Mon Sep 17 00:00:00 2001 From: Nuno Caseiro Date: Wed, 4 May 2022 20:18:49 +0100 Subject: [PATCH] feat: boards page and add cards --- .../get.board.application.interface.ts | 16 +- .../boards/interfaces/getBoard.interface.ts | 10 + .../services/get.board.service.interface.ts | 14 +- .../boards/services/create.board.service.ts | 17 +- frontend/api/boardService.tsx | 23 +- .../components/Board/AddCardOrComments.tsx | 199 +++++++++++++ frontend/components/Board/Card/AddCard.tsx | 98 ------- frontend/components/Board/Column/Column.tsx | 95 ++++-- frontend/components/Primitives/TextArea.tsx | 195 +++++++------ frontend/hooks/useCards.tsx | 123 +++++++- frontend/pages/boards/[boardId].tsx | 270 ++++++++++++------ .../store/mergeCard/atoms/merge-card.atom.tsx | 7 + frontend/types/board/useBoard.ts | 4 +- frontend/types/card/deleteCard.dto.ts | 4 + frontend/types/column.ts | 19 +- 15 files changed, 733 insertions(+), 361 deletions(-) create mode 100644 backend/src/modules/boards/interfaces/getBoard.interface.ts create mode 100644 frontend/components/Board/AddCardOrComments.tsx delete mode 100644 frontend/components/Board/Card/AddCard.tsx create mode 100644 frontend/store/mergeCard/atoms/merge-card.atom.tsx diff --git a/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts b/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts index 5bf555e9e..e1489373f 100644 --- a/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts +++ b/backend/src/modules/boards/interfaces/applications/get.board.application.interface.ts @@ -1,5 +1,4 @@ -import { LeanDocument } from 'mongoose'; -import { BoardDocument } from '../../schemas/board.schema'; +import { GetBoardInterface } from '../getBoard.interface'; import { BoardsAndPage } from '../boards-page.interface'; export interface GetBoardApplicationInterface { @@ -18,17 +17,6 @@ export interface GetBoardApplicationInterface { page?: number, size?: number, ): Promise; - getBoard( - boardId: string, - userId: string, - ): Promise< - | { board: LeanDocument } - | null - | { - board: LeanDocument; - mainBoardData: LeanDocument; - } - | null - >; + getBoard(boardId: string, userId: string): Promise; countBoards(userId: string): Promise; } diff --git a/backend/src/modules/boards/interfaces/getBoard.interface.ts b/backend/src/modules/boards/interfaces/getBoard.interface.ts new file mode 100644 index 000000000..66ce8d934 --- /dev/null +++ b/backend/src/modules/boards/interfaces/getBoard.interface.ts @@ -0,0 +1,10 @@ +import { LeanDocument } from 'mongoose'; +import { BoardDocument } from '../schemas/board.schema'; + +export type GetBoardInterface = + | { board: LeanDocument } + | { + board: LeanDocument; + mainBoardData: LeanDocument; + } + | null; diff --git a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts index 3bc944f15..d099bcd04 100644 --- a/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts +++ b/backend/src/modules/boards/interfaces/services/get.board.service.interface.ts @@ -1,4 +1,5 @@ import { LeanDocument } from 'mongoose'; +import { GetBoardInterface } from '../getBoard.interface'; import { BoardDocument } from '../../schemas/board.schema'; import { BoardsAndPage } from '../boards-page.interface'; @@ -21,17 +22,6 @@ export interface GetBoardServiceInterface { getBoardFromRepo( boardId: string, ): Promise | null>; - getBoard( - boardId: string, - userId: string, - ): Promise< - | { board: LeanDocument } - | null - | { - board: LeanDocument; - mainBoardData: LeanDocument; - } - | null - >; + getBoard(boardId: string, userId: string): Promise; countBoards(userId: string): Promise; } diff --git a/backend/src/modules/boards/services/create.board.service.ts b/backend/src/modules/boards/services/create.board.service.ts index af4903df0..f005acf01 100644 --- a/backend/src/modules/boards/services/create.board.service.ts +++ b/backend/src/modules/boards/services/create.board.service.ts @@ -79,15 +79,14 @@ export default class CreateBoardServiceImpl implements CreateBoardService { const teamUsers = await this.getTeamService.getUsersOfTeam(team); teamUsers.forEach((teamUser) => { const user = teamUser.user as UserDocument; - if (!usersIds.includes(user._id.toString())) { - newUsers.push({ - user: user._id.toString(), - role: !stakeHolders.includes(user.email) - ? BoardRoles.MEMBER - : BoardRoles.STAKEHOLDER, - votesCount: 0, - }); - } + if (usersIds.includes(user._id.toString())) return; + newUsers.push({ + user: user._id.toString(), + role: !stakeHolders.includes(user.email) + ? BoardRoles.MEMBER + : BoardRoles.STAKEHOLDER, + votesCount: 0, + }); }); } diff --git a/frontend/api/boardService.tsx b/frontend/api/boardService.tsx index e4c9eaa46..4b362dcb3 100644 --- a/frontend/api/boardService.tsx +++ b/frontend/api/boardService.tsx @@ -1,6 +1,6 @@ import { GetServerSidePropsContext } from "next"; import fetchData from "../utils/fetchData"; -import BoardType, { CreateBoardDto } from "../types/board/board"; +import BoardType, { CreateBoardDto, GetBoardResponse } from "../types/board/board"; import UpdateCardPositionDto from "../types/card/updateCardPosition.dto"; import UpdateBoardDto from "../types/board/updateBoard"; import AddCardDto from "../types/card/addCard.dto"; @@ -26,8 +26,8 @@ export const updateBoardRequest = ({ board }: UpdateBoardDto): Promise => { - return fetchData(`/boards/${id}`, { context, serverSide: !!context }); +): Promise => { + return fetchData(`/boards/${id}`, { context, serverSide: !!context }); }; export const getStakeholders = (): Promise => { @@ -75,6 +75,10 @@ export const updateCardRequest = (updateCard: UpdateCardDto): Promise ); }; +export const mergeBoardRequest = (subBoardId: string): Promise => { + return fetchData(`/boards/${subBoardId}/merge`, { method: "PUT" }); +}; + export const updateCardPositionRequest = ( updateCardPosition: UpdateCardPositionDto ): Promise => { @@ -85,10 +89,15 @@ export const updateCardPositionRequest = ( }; export const deleteCardRequest = (deleteCardDto: DeleteCardDto): Promise => { - return fetchData(`/boards/${deleteCardDto.boardId}/card/${deleteCardDto.cardId}`, { - method: "DELETE", - data: deleteCardDto, - }); + return fetchData( + deleteCardDto.isCardGroup + ? `/boards/${deleteCardDto.boardId}/card/${deleteCardDto.cardId}` + : `/boards/${deleteCardDto.boardId}/card/${deleteCardDto.cardId}/items/${deleteCardDto.cardItemId}`, + { + method: "DELETE", + data: deleteCardDto, + } + ); }; // #endregion diff --git a/frontend/components/Board/AddCardOrComments.tsx b/frontend/components/Board/AddCardOrComments.tsx new file mode 100644 index 000000000..675d2699a --- /dev/null +++ b/frontend/components/Board/AddCardOrComments.tsx @@ -0,0 +1,199 @@ +import * as z from "zod"; +import React, { useState } from "react"; +import { FormProvider, useForm } from "react-hook-form"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { styled } from "../../stitches.config"; +import { CardToAdd } from "../../types/card/card"; +import AddCardDto from "../../types/card/addCard.dto"; +import Button from "../Primitives/Button"; +import Flex from "../Primitives/Flex"; +import useCards from "../../hooks/useCards"; +import TextArea from "../Primitives/TextArea"; +import CrossIcon from "../icons/CrossIcon"; +import CheckIcon from "../icons/Check"; +import PlusIcon from "../icons/PlusIcon"; +import UpdateCardDto from "../../types/card/updateCard.dto"; +import useComments from "../../hooks/useComments"; +import AddCommentDto from "../../types/comment/addComment.dto"; +import UpdateCommentDto from "../../types/comment/updateComment.dto"; + +const ActionButton = styled(Button, { width: "$48", height: "$36" }); + +const StyledForm = styled("form", Flex, { width: "100%" }); + +interface AddCardProps { + isUpdate: boolean; + isCard: boolean; + colId: string; + boardId: string; + socketId: string; + cardId?: string; + cardItemId?: string; + cardText?: string; + commentId?: string; + cancelUpdate?: () => void; + defaultOpen?: boolean; +} + +const AddCardOrComments = React.memo( + ({ + isUpdate, + colId, + boardId, + socketId, + cardId, + cardItemId, + cardText, + cancelUpdate, + isCard, + commentId, + defaultOpen, + }) => { + const { addCardInColumn, updateCard } = useCards(); + const { addCommentInCard, updateComment } = useComments(); + const [isOpen, setIsOpen] = useState(!!isUpdate || !!cancelUpdate || defaultOpen); + + const methods = useForm<{ text: string }>({ + mode: "onSubmit", + reValidateMode: "onChange", + defaultValues: { + text: cardText || "", + }, + resolver: zodResolver(z.object({ text: z.string().min(1) })), + }); + + const handleAddCard = (text: string) => { + const newCard: CardToAdd = { + items: [ + { + text: text.trim(), + votes: [], + comments: [], + }, + ], + text: text.trim(), + votes: [], + comments: [], + }; + const changes: AddCardDto = { + colIdToAdd: colId, + boardId, + card: newCard, + socketId, + }; + + addCardInColumn.mutate(changes); + }; + + const handleUpdateCard = (text: string) => { + if (!cardId || !cancelUpdate) return; + const cardUpdated: UpdateCardDto = { + cardId, + cardItemId: cardItemId ?? "", + text, + boardId, + socketId, + isCardGroup: !cardItemId, + }; + + updateCard.mutate(cardUpdated); + cancelUpdate(); + }; + + const handleAddComment = (text: string) => { + if (!cardId || !cancelUpdate) return; + const commentDto: AddCommentDto = { + cardId, + cardItemId, + text, + boardId, + socketId, + isCardGroup: !cardItemId, + }; + + addCommentInCard.mutate(commentDto); + cancelUpdate(); + }; + + const handleUpdateComment = (text: string) => { + if (!cardId || !cancelUpdate || !commentId) return; + const updateCommentDto: UpdateCommentDto = { + cardId, + cardItemId, + text, + boardId, + socketId, + isCardGroup: !cardItemId, + commentId, + }; + + updateComment.mutate(updateCommentDto); + cancelUpdate(); + }; + + const handleClear = () => { + if ((isUpdate || !isCard) && cancelUpdate) { + cancelUpdate(); + return; + } + + methods.reset({ text: "" }); + setIsOpen(false); + }; + + const handleSubmit = (text: string) => { + if (isCard) { + if (!isUpdate) { + handleAddCard(text); + return; + } + handleUpdateCard(text); + } + if (!isCard) { + if (!isUpdate) { + handleAddComment(text); + return; + } + handleUpdateComment(text); + } + }; + + if (!isOpen) + return ( + setIsOpen(true)}> + + Add new card + + ); + + return ( + { + handleSubmit(text); + })} + > + +