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: create regular board action frontend #786

Merged
merged 10 commits into from
Jan 4, 2023
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Inject, Injectable } from '@nestjs/common';
import UserDto from 'src/modules/users/dto/user.dto';
import { DeleteBoardApplicationInterface } from '../interfaces/applications/delete.board.application.interface';
import { DeleteBoardServiceInterface } from '../interfaces/services/delete.board.service.interface';
import { TYPES } from '../interfaces/types';
Expand All @@ -10,7 +11,7 @@ export class DeleteBoardApplication implements DeleteBoardApplicationInterface {
private deleteBoardService: DeleteBoardServiceInterface
) {}

delete(boardId: string, userId: string): Promise<boolean> {
return this.deleteBoardService.delete(boardId, userId);
delete(boardId: string, user: UserDto): Promise<boolean> {
return this.deleteBoardService.delete(boardId, user);
}
}
2 changes: 1 addition & 1 deletion backend/src/modules/boards/controller/boards.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export default class BoardsController {
@Query() { socketId }: BaseParamWSocket,
@Req() request: RequestWithUser
) {
const result = await this.deleteBoardApp.delete(boardId, request.user._id);
const result = await this.deleteBoardApp.delete(boardId, request.user);

if (socketId && teamId) {
this.socketService.sendUpdatedBoards(socketId, teamId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import UserDto from 'src/modules/users/dto/user.dto';

export interface DeleteBoardApplicationInterface {
delete(boardId: string, userId: string): Promise<boolean>;
delete(boardId: string, user: UserDto): Promise<boolean>;
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import UserDto from 'src/modules/users/dto/user.dto';

export interface DeleteBoardServiceInterface {
delete(boardId: string, userId: string): Promise<boolean>;
delete(boardId: string, user: UserDto): Promise<boolean>;
deleteBoardsByTeamId(teamId: string): Promise<boolean>;
}
41 changes: 30 additions & 11 deletions backend/src/modules/boards/services/create.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,14 +58,16 @@ export default class CreateBoardServiceImpl implements CreateBoardService {
) {}

saveBoardUsers(newUsers: BoardUserDto[], newBoardId: string) {
Promise.all(newUsers.map((user) => this.boardUserModel.create({ ...user, board: newBoardId })));
return Promise.all(
newUsers.map((user) => this.boardUserModel.create({ ...user, board: newBoardId }))
);
}

async createDividedBoards(boards: BoardDto[], userId: string) {
const newBoardsIds = await Promise.allSettled(
boards.map(async (board) => {
const { users } = board;
const { _id } = await this.createBoard(board, userId, true);
const { _id } = await this.createBoard(board, userId, true, true);

if (!isEmpty(users)) {
this.saveBoardUsers(users, _id);
Expand All @@ -78,18 +80,29 @@ export default class CreateBoardServiceImpl implements CreateBoardService {
return newBoardsIds.flatMap((result) => (result.status === 'fulfilled' ? [result.value] : []));
}

async createBoard(boardData: BoardDto, userId: string, isSubBoard = false) {
async createBoard(boardData: BoardDto, userId: string, isSubBoard = false, haveSubBoards = true) {
const { dividedBoards = [], team } = boardData;

/**
* Add in each divided board the team id (from main board)
*/
const dividedBoardsWithTeam = dividedBoards.map((dividedBoard) => ({ ...dividedBoard, team }));
if (haveSubBoards) {
/**
* Add in each divided board the team id (from main board)
*/
const dividedBoardsWithTeam = dividedBoards.map((dividedBoard) => ({
...dividedBoard,
team
}));

return this.boardModel.create({
...boardData,
createdBy: userId,
dividedBoards: await this.createDividedBoards(dividedBoardsWithTeam, userId),
isSubBoard
});
}

return this.boardModel.create({
...boardData,
createdBy: userId,
dividedBoards: await this.createDividedBoards(dividedBoardsWithTeam, userId),
isSubBoard
});
}
Expand Down Expand Up @@ -122,15 +135,21 @@ export default class CreateBoardServiceImpl implements CreateBoardService {
}

async create(boardData: BoardDto, userId: string, fromSchedule = false) {
const { team, recurrent, maxUsers, slackEnable } = boardData;
const newUsers = [];
const { team, recurrent, maxUsers, slackEnable, users, dividedBoards } = boardData;

const newBoard = await this.createBoard(boardData, userId);
const haveDividedBoards = dividedBoards.length > 0 ? true : false;
let newUsers = [];

const newBoard = await this.createBoard(boardData, userId, false, haveDividedBoards);

if (team) {
await this.saveBoardUsersFromTeam(newUsers, team, boardData.responsibles);
}

if (!haveDividedBoards && !team) {
newUsers = [...users];
}

this.saveBoardUsers(newUsers, newBoard._id);

if (newBoard && recurrent && team && maxUsers) {
Expand Down
22 changes: 14 additions & 8 deletions backend/src/modules/boards/services/delete.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import Board, { BoardDocument } from '../schemas/board.schema';
import BoardUser, { BoardUserDocument } from '../schemas/board.user.schema';
import * as Boards from 'src/modules/boards/interfaces/types';
import { GetBoardServiceInterface } from '../interfaces/services/get.board.service.interface';
import UserDto from 'src/modules/users/dto/user.dto';

@Injectable()
export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterface {
Expand Down Expand Up @@ -105,26 +106,31 @@ export default class DeleteBoardServiceImpl implements DeleteBoardServiceInterfa
return { dividedBoards: result.dividedBoards, _id: result._id };
}

async delete(boardId: string, userId: string) {
async delete(boardId: string, user: UserDto) {
const board = await this.boardModel.findById(boardId).exec();

const userId = user._id;
const isSAdmin = user.isSAdmin;

if (!board) {
throw new NotFoundException('Board not found!');
}
const { team, createdBy } = board;
const teamUser = await this.getTeamUser(userId, String(team));
const users = await this.getUsersOfTeam(String(team));

const userIsSAdmin = await this.isUserSAdmin(userId, users);
let isAdminOrStakeholder = false;

const isAdminOrStakeholder = [TeamRoles.STAKEHOLDER, TeamRoles.ADMIN].includes(
teamUser.role as TeamRoles
);
if (team) {
const teamUser = await this.getTeamUser(userId, String(team));

isAdminOrStakeholder = [TeamRoles.STAKEHOLDER, TeamRoles.ADMIN].includes(
teamUser.role as TeamRoles
);
}

// Validate if the logged user are the owner
const isOwner = String(userId) === String(createdBy);

if (isOwner || isAdminOrStakeholder || userIsSAdmin) {
if (isOwner || isAdminOrStakeholder || isSAdmin) {
try {
return this.deleteBoardBoardUsersAndSchedules(boardId, true);
} catch (error) {
Expand Down
2 changes: 1 addition & 1 deletion backend/src/modules/boards/services/get.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export default class GetBoardServiceImpl implements GetBoardServiceInterface {
private readonly logger = new Logger(GetBoardServiceImpl.name);

getAllBoardsIdsOfUser(userId: string) {
return this.boardModel.find({ user: userId }).select('board').distinct('board').lean().exec();
return this.boardModel.find({ user: userId }).select('board').lean().exec();
}

async getAllBoardIdsAndTeamIdsOfUser(userId: string) {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/api/boardService.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export const deleteBoardRequest = async ({
}: {
id: string;
socketId?: string;
teamId: string;
teamId?: string;
}): Promise<BoardType> =>
fetchData(`/boards/${id}`, { method: 'DELETE', params: { socketId, teamId } });

Expand Down
4 changes: 3 additions & 1 deletion frontend/src/components/Boards/MyBoards/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ const MyBoards = React.memo<MyBoardsProps>(({ userId, isSuperAdmin }) => {

const { data, isLoading } = fetchBoards;

const teamSocketId = data?.pages[0].boards[0] ? data?.pages[0].boards[0].team._id : undefined;
const teamSocketId = data?.pages[0].boards[0].team
? data?.pages[0].boards[0].team._id
: undefined;

// socketId
const { socket, queryClient } = useSocketBoardIO(teamSocketId);
Expand Down
19 changes: 13 additions & 6 deletions frontend/src/components/CardBoard/CardBody/CardBody.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,22 @@ const CardBody = React.memo<CardBodyProps>(
if (isSAdmin) {
return true;
}
const myUser = team.users.find((user) => String(user.user._id) === String(userId));

let myUser;

const myUserIsOwnerMainBoard = board.createdBy?._id === userId;
const myUserIsOwnerSubBoard = String(board.createdBy) === userId;
const owner = myUserIsOwnerMainBoard || myUserIsOwnerSubBoard;
if (team && (myUser?.role === 'admin' || myUser?.role === 'stakeholder' || owner)) {
return true;

if (team) {
myUser = team.users.find((user) => String(user.user._id) === String(userId));

const myUserIsOwnerSubBoard = String(board.createdBy) === userId;
const owner = myUserIsOwnerMainBoard || myUserIsOwnerSubBoard;
if (myUser?.role === 'admin' || myUser?.role === 'stakeholder' || owner) {
return true;
}
}

return false;
return myUserIsOwnerMainBoard;
}, [isSAdmin, team, userId, board.createdBy]);

const handleOpenSubBoards = (e: ClickEvent<HTMLDivElement, MouseEvent>) => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/CardBoard/CardBody/CardEnd.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ const CardEnd: React.FC<CardEndProps> = React.memo(
}}
/>

<DeleteBoard boardId={id} boardName={title} socketId={socketId} teamId={team._id} />
<DeleteBoard boardId={id} boardName={title} socketId={socketId} teamId={team?._id} />
</Flex>
)}
</Flex>
Expand Down
6 changes: 5 additions & 1 deletion frontend/src/components/CardBoard/DeleteBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ const DeleteBoard: React.FC<DeleteBoardProps> = ({ boardId, boardName, socketId,
const { deleteBoard } = useBoard({ autoFetchBoard: false });

const handleDelete = () => {
deleteBoard.mutate({ id: boardId, socketId, teamId });
if (teamId) {
deleteBoard.mutate({ id: boardId, socketId, teamId });
} else {
deleteBoard.mutate({ id: boardId });
}
};

return (
Expand Down
62 changes: 36 additions & 26 deletions frontend/src/pages/boards/newRegularBoard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import { TeamUserRoles } from '@/utils/enums/team.user.roles';
import SchemaCreateRegularBoard from '@/schema/schemaCreateRegularBoard';
import { getAllUsers } from '@/api/userService';
import { ToastStateEnum } from '@/utils/enums/toast-types';
import useBoard from '@/hooks/useBoard';

const defaultBoard = {
users: [],
Expand Down Expand Up @@ -75,8 +76,7 @@ const NewRegularBoard: NextPage = () => {
const [createBoard, setCreateBoard] = useState(false);

const setToastState = useSetRecoilState(toastState);
const setBoardState = useSetRecoilState(createBoardDataState);
// const [boardState, setBoardState] = useRecoilState(createBoardDataState);
const [boardState, setBoardState] = useRecoilState(createBoardDataState);
const [usersList, setUsersList] = useRecoilState(usersListState);
const setTeams = useSetRecoilState(teamsOfUser);
const setSelectedTeam = useSetRecoilState(createBoardTeam);
Expand Down Expand Up @@ -104,6 +104,13 @@ const NewRegularBoard: NextPage = () => {
},
});

/**
* Board Hook
*/
const {
createBoard: { status, mutate },
} = useBoard({ autoFetchBoard: false });

const addNewRegularBoard = () => {
setCreateBoard(true);
};
Expand Down Expand Up @@ -155,16 +162,18 @@ const NewRegularBoard: NextPage = () => {
* Save board

*/
// const saveBoard = (title?: string, maxVotes?: number, slackEnable?: boolean) => {
// // mutate( {
// // ...boardState.board,
// // users: boardState.users,
// // title: title || boardState.board.title,
// // maxVotes,
// // slackEnable,
// // maxUsers: boardState.count.maxUsersCount,
// // });
// };
const saveBoard = (title?: string, maxVotes?: number, slackEnable?: boolean) => {
mutate({
...boardState.board,
users: boardState.users,
title: title || boardState.board.title,
dividedBoards: [],
maxVotes,
slackEnable,
maxUsers: boardState.count.maxUsersCount,
recurrent: false,
});
};

useEffect(() => {
if (teamsData && allTeamsData && session && allUsers) {
Expand All @@ -186,18 +195,18 @@ const NewRegularBoard: NextPage = () => {
setUsersList(usersWithChecked);
}

// if (status === 'success') {
// setIsLoading(true);
// setToastState({
// open: true,
// content: 'Board created with success!',
// type: ToastStateEnum.SUCCESS,
// });
if (status === 'success') {
setIsLoading(true);
setToastState({
open: true,
content: 'Board created with success!',
type: ToastStateEnum.SUCCESS,
});

// setBoardState(defaultBoard);
// setSelectedTeam(undefined);
// router.push('/boards');
// }
setBoardState(defaultBoard);
setSelectedTeam(undefined);
router.push('/boards');
}

return () => {
setBoardState(defaultBoard);
Expand All @@ -214,6 +223,7 @@ const NewRegularBoard: NextPage = () => {
setBoardState,
allUsers,
setUsersList,
status,
]);

if (!session || !teamsData || !allTeamsData) return null;
Expand All @@ -236,9 +246,9 @@ const NewRegularBoard: NextPage = () => {
<SubContainer>
<StyledForm
direction="column"
// onSubmit={methods.handleSubmit(({ text, maxVotes, slackEnable }) => {
// saveBoard(text, maxVotes, slackEnable);
// })}
onSubmit={methods.handleSubmit(({ text, maxVotes, slackEnable }) => {
saveBoard(text, maxVotes, slackEnable);
})}
>
<InnerContent direction="column">
<FormProvider {...methods}>
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/types/board/useBoard.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export default interface UseBoardType {
deleteBoard: UseMutationResult<
BoardType,
unknown,
{ id: string; socketId?: string; teamId: string },
{ id: string; socketId?: string; teamId?: string },
unknown
>;
fetchBoard: UseQueryResult<GetBoardResponse | null, unknown>;
Expand Down