Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: boards page and add cards #146

Merged
merged 1 commit into from
May 11, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 {
Expand All @@ -18,17 +17,6 @@ export interface GetBoardApplicationInterface {
page?: number,
size?: number,
): Promise<BoardsAndPage | null>;
getBoard(
boardId: string,
userId: string,
): Promise<
| { board: LeanDocument<BoardDocument> }
| null
| {
board: LeanDocument<BoardDocument>;
mainBoardData: LeanDocument<BoardDocument>;
}
| null
>;
getBoard(boardId: string, userId: string): Promise<GetBoardInterface>;
countBoards(userId: string): Promise<number>;
}
10 changes: 10 additions & 0 deletions backend/src/modules/boards/interfaces/getBoard.interface.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { LeanDocument } from 'mongoose';
import { BoardDocument } from '../schemas/board.schema';

export type GetBoardInterface =
| { board: LeanDocument<BoardDocument> }
| {
board: LeanDocument<BoardDocument>;
mainBoardData: LeanDocument<BoardDocument>;
}
| null;
Original file line number Diff line number Diff line change
@@ -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';

Expand All @@ -21,17 +22,6 @@ export interface GetBoardServiceInterface {
getBoardFromRepo(
boardId: string,
): Promise<LeanDocument<BoardDocument> | null>;
getBoard(
boardId: string,
userId: string,
): Promise<
| { board: LeanDocument<BoardDocument> }
| null
| {
board: LeanDocument<BoardDocument>;
mainBoardData: LeanDocument<BoardDocument>;
}
| null
>;
getBoard(boardId: string, userId: string): Promise<GetBoardInterface>;
countBoards(userId: string): Promise<number>;
}
17 changes: 8 additions & 9 deletions backend/src/modules/boards/services/create.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
});
});
}

Expand Down
23 changes: 16 additions & 7 deletions frontend/api/boardService.tsx
Original file line number Diff line number Diff line change
@@ -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";
Expand All @@ -26,8 +26,8 @@ export const updateBoardRequest = ({ board }: UpdateBoardDto): Promise<BoardType
export const getBoardRequest = (
id: string,
context?: GetServerSidePropsContext
): Promise<BoardType> => {
return fetchData<BoardType>(`/boards/${id}`, { context, serverSide: !!context });
): Promise<GetBoardResponse> => {
return fetchData<GetBoardResponse>(`/boards/${id}`, { context, serverSide: !!context });
};

export const getStakeholders = (): Promise<string[]> => {
Expand Down Expand Up @@ -75,6 +75,10 @@ export const updateCardRequest = (updateCard: UpdateCardDto): Promise<BoardType>
);
};

export const mergeBoardRequest = (subBoardId: string): Promise<BoardType> => {
return fetchData<BoardType>(`/boards/${subBoardId}/merge`, { method: "PUT" });
};

export const updateCardPositionRequest = (
updateCardPosition: UpdateCardPositionDto
): Promise<BoardType> => {
Expand All @@ -85,10 +89,15 @@ export const updateCardPositionRequest = (
};

export const deleteCardRequest = (deleteCardDto: DeleteCardDto): Promise<BoardType> => {
return fetchData<BoardType>(`/boards/${deleteCardDto.boardId}/card/${deleteCardDto.cardId}`, {
method: "DELETE",
data: deleteCardDto,
});
return fetchData<BoardType>(
deleteCardDto.isCardGroup
? `/boards/${deleteCardDto.boardId}/card/${deleteCardDto.cardId}`
: `/boards/${deleteCardDto.boardId}/card/${deleteCardDto.cardId}/items/${deleteCardDto.cardItemId}`,
{
method: "DELETE",
data: deleteCardDto,
}
);
};

// #endregion
Expand Down
199 changes: 199 additions & 0 deletions frontend/components/Board/AddCardOrComments.tsx
Original file line number Diff line number Diff line change
@@ -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<AddCardProps>(
({
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 (
<ActionButton css={{ display: "flex" }} onClick={() => setIsOpen(true)}>
<PlusIcon size="16" css={{ size: "$12", color: "white" }} />
Add new card
</ActionButton>
);

return (
<StyledForm
direction="column"
align="center"
justify="center"
gap="8"
tabIndex={0}
onSubmit={methods.handleSubmit(({ text }) => {
handleSubmit(text);
})}
>
<FormProvider {...methods}>
<TextArea id="text" floatPlaceholder={false} placeholder="Write your comment here..." />
<Flex justify="end" gap="4" css={{ width: "100%" }}>
<ActionButton
size="sm"
variant={!isUpdate && isCard ? "lightOutline" : "primaryOutline"}
onClick={handleClear}
>
<CrossIcon size="16" />
</ActionButton>
<ActionButton size="sm" type="submit" variant="primary">
<CheckIcon />
</ActionButton>
</Flex>
</FormProvider>
</StyledForm>
);
}
);
export default AddCardOrComments;
Loading