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

fix: regular board votes and quick create #996

Merged
merged 11 commits into from
Feb 3, 2023
17 changes: 16 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,22 @@

All notable changes to this project will be documented in this file.

## [Unreleased](https://github.com/xgeekshq/split/compare/v0.1.9...HEAD)
## [Unreleased](https://github.com/xgeekshq/split/compare/v0.1.10...HEAD)

## [v0.1.10](https://github.com/xgeekshq/split/compare/v0.1.9...v0.1.10) - 2023-02-02

### What Changed 👀

### 🐛 Bug Fixes

- fix: new responsible validation @nunocaseiro (#1001)
- fix: isNewJoiner period and votes @CatiaAntunes96 (#1000)

### 📄 Documentation

- feat: popover primitive refactor and story @JoaoSaIvador (#993)

**Full Changelog**: https://github.com/xgeekshq/split/compare/v0.1.9...v0.1.10

## [v0.1.9](https://github.com/xgeekshq/split/compare/v0.1.8...v0.1.9) - 2023-02-02

Expand Down
2 changes: 1 addition & 1 deletion backend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "backend",
"version": "v0.1.9",
"version": "v0.1.10",
"description": "",
"author": "",
"private": true,
Expand Down
2 changes: 2 additions & 0 deletions backend/src/modules/boards/boards.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import { CommunicationModule } from 'src/modules/communication/communication.mod
import { SchedulesModule } from 'src/modules/schedules/schedules.module';
import TeamsModule from 'src/modules/teams/teams.module';
import UsersModule from 'src/modules/users/users.module';
import { VotesModule } from '../votes/votes.module';
import {
createBoardApplication,
createBoardService,
Expand All @@ -23,6 +24,7 @@ import BoardsController from './controller/boards.controller';
imports: [
UsersModule,
forwardRef(() => TeamsModule),
VotesModule,
SchedulesModule,
CommunicationModule,
mongooseBoardModule,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import {
updateBoardService
} from 'src/modules/boards/boards.providers';
import BoardsController from 'src/modules/boards/controller/boards.controller';
import { getCardService } from 'src/modules/cards/cards.providers';
import * as CommunicationsType from 'src/modules/communication/interfaces/types';
import {
createSchedulesService,
Expand All @@ -26,6 +27,7 @@ import {
teamUserRepository,
updateTeamService
} from 'src/modules/teams/providers';
import { deleteVoteService } from 'src/modules/votes/votes.providers';

describe('BoardsController', () => {
let controller: BoardsController;
Expand All @@ -52,6 +54,8 @@ describe('BoardsController', () => {
teamRepository,
teamUserRepository,
updateTeamService,
deleteVoteService,
getCardService,
{
provide: getModelToken('User'),
useValue: {}
Expand Down
12 changes: 2 additions & 10 deletions backend/src/modules/boards/controller/boards.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,16 +235,8 @@ export default class BoardsController {
@BoardUser([BoardRoles.RESPONSIBLE, TeamRoles.ADMIN, TeamRoles.STAKEHOLDER])
@UseGuards(BoardUserGuard)
@Put(':boardId')
async updateBoard(@Param() { boardId }: BaseParam, @Body() boardData: UpdateBoardDto) {
const board = await this.updateBoardApp.update(boardId, boardData);

if (!board) throw new BadRequestException(UPDATE_FAILED);

if (boardData.socketId) {
this.socketService.sendUpdatedBoard(boardId, boardData.socketId);
}

return board;
updateBoard(@Param() { boardId }: BaseParam, @Body() boardData: UpdateBoardDto) {
return this.updateBoardApp.update(boardId, boardData);
}

@ApiOperation({ summary: 'Delete a specific board' })
Expand Down
8 changes: 7 additions & 1 deletion backend/src/modules/boards/dto/update-board.dto.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PartialType } from '@nestjs/mapped-types';
import { ApiPropertyOptional } from '@nestjs/swagger';
import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger';
import { Type } from 'class-transformer';
import { IsOptional } from 'class-validator';
import BoardUser from '../schemas/board.user.schema';
import BoardDto from './board.dto';
Expand All @@ -8,4 +9,9 @@ export class UpdateBoardDto extends PartialType(BoardDto) {
@ApiPropertyOptional({ type: BoardUser, isArray: true })
@IsOptional()
responsible?: BoardUser;

@ApiProperty({ type: String, isArray: true })
@IsOptional()
@Type(() => String)
deletedColumns?: string[];
}
9 changes: 7 additions & 2 deletions backend/src/modules/boards/services/create.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ export default class CreateBoardServiceImpl implements CreateBoardService {
const { team, recurrent, maxUsers, slackEnable, users, dividedBoards } = boardData;

const haveDividedBoards = dividedBoards.length > 0 ? true : false;
let newUsers = [];
const newUsers = [];

const newBoard = await this.createBoard(boardData, userId, false, haveDividedBoards);
let teamData;
Expand All @@ -155,7 +155,12 @@ export default class CreateBoardServiceImpl implements CreateBoardService {
}

if (!haveDividedBoards && !team) {
newUsers = [...users];
users.forEach((user) =>
newUsers.push({
...user,
votesCount: 0
})
);
}

await this.saveBoardUsers(newUsers, newBoard._id);
Expand Down
76 changes: 73 additions & 3 deletions backend/src/modules/boards/services/update.board.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { CommunicationServiceInterface } from 'src/modules/communication/interfa
import * as CommunicationsType from 'src/modules/communication/interfaces/types';
import { GetTeamServiceInterface } from 'src/modules/teams/interfaces/services/get.team.service.interface';
import * as Teams from 'src/modules/teams/interfaces/types';
import * as Votes from 'src/modules/votes/interfaces/types';
import User, { UserDocument } from 'src/modules/users/entities/user.schema';
import { UpdateBoardDto } from '../dto/update-board.dto';
import { ResponsibleType } from '../interfaces/responsible.interface';
Expand All @@ -25,6 +26,9 @@ import { BoardDataPopulate } from '../utils/populate-board';
import { UpdateColumnDto } from '../dto/column/update-column.dto';
import { UPDATE_FAILED } from 'src/libs/exceptions/messages';
import SocketGateway from 'src/modules/socket/gateway/socket.gateway';
import { DeleteVoteServiceInterface } from 'src/modules/votes/interfaces/services/delete.vote.service.interface';
import Column from '../schemas/column.schema';
import ColumnDto from '../dto/column/column.dto';

@Injectable()
export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterface {
Expand All @@ -36,7 +40,9 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
private slackCommunicationService: CommunicationServiceInterface,
@InjectModel(BoardUser.name)
private boardUserModel: Model<BoardUserDocument>,
private socketService: SocketGateway
private socketService: SocketGateway,
@Inject(Votes.TYPES.services.DeleteVoteService)
private deleteVoteService: DeleteVoteServiceInterface
) {}

/**
Expand Down Expand Up @@ -98,11 +104,11 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
* - is a sub-board
* - and the logged user isn't the current responsible
*/
if (boardData.users && currentResponsible.id !== newResponsible.id) {
if (boardData.users && String(currentResponsible.id) !== String(newResponsible.id)) {
if (isSubBoard) {
const promises = boardData.users
.filter((boardUser) =>
[getIdFromObjectId(String(currentResponsible?.id)), newResponsible.id].includes(
[getIdFromObjectId(String(currentResponsible?.id)), String(newResponsible.id)].includes(
(boardUser.user as unknown as User)._id
)
)
Expand Down Expand Up @@ -164,6 +170,64 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
board.addCards = boardData.addCards;
board.hideVotes = boardData.hideVotes;

/**
* Validate if:
* - have columns to delete
* Returns the votes to the user
*/
if (boardData.deletedColumns && !isEmpty(boardData.deletedColumns)) {
const cardsToDelete = boardData.deletedColumns.flatMap((deletedColumnId: string) => {
return board.columns.find((column) => column._id.toString() === deletedColumnId)?.cards;
});

cardsToDelete.forEach((cards) => {
cards.items.forEach(async (card) => {
const votesByUser = new Map<string, number>();

card.votes.forEach((userId) => {
if (!votesByUser.has(userId.toString())) {
votesByUser.set(userId.toString(), 1);
} else {
const count = votesByUser.get(userId.toString());

votesByUser.set(userId.toString(), count + 1);
}
});

votesByUser.forEach(async (votesCount, userId) => {
await this.deleteVoteService.decrementVoteUser(board.id, userId, -votesCount);
});
});
});
}

/**
* Only the regular boards will have their columns updated
*
* */

if (!isSubBoard && isEmpty(board.dividedBoards)) {
board.columns = boardData.columns.flatMap((col: Column | ColumnDto) => {
if (col._id) {
const columnBoard = board.columns.find((colBoard) => colBoard._id === col._id.toString());

if (columnBoard) {
return [{ ...columnBoard, title: col.title }];
}

const columnToDelete = boardData.deletedColumns.some(
(colId) => colId === col._id.toString()
);

if (columnToDelete) {
return [];
}
}

return [{ ...col }];
}) as Column[];
}

/**
* Only can change the maxVotes if:
* - new maxVotes not empty
Expand Down Expand Up @@ -197,6 +261,12 @@ export default class UpdateBoardServiceImpl implements UpdateBoardServiceInterfa
.lean()
.exec();

if (!updatedBoard) throw new BadRequestException(UPDATE_FAILED);

if (boardData.socketId) {
this.socketService.sendUpdatedBoard(boardId, boardData.socketId);
}

if (
updatedBoard &&
newResponsible &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ export class SlackCommunicationApplication implements CommunicationApplicationIn
const generalText = {
member: (
boardId: string
) => `<!channel> In order to proceed with the retro of this month, here is the board link: \n\n
) => `<!here> In order to proceed with the retro of this month, here is the board link: \n\n
${this.config.frontendUrl}/boards/${boardId}
`,
responsible: (
boardId: string
) => `<!channel> In order to proceed with the retro of this month, here is the main board link: \n\n
) => `<!here> In order to proceed with the retro of this month, here is the main board link: \n\n
${this.config.frontendUrl}/boards/${boardId}
`
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export class SlackMergeBoardApplication implements MergeBoardApplicationInterfac

async execute(data: MergeBoardType): Promise<MergeBoardType | null> {
const { responsiblesChannelId, teamNumber, isLastSubBoard } = data;
const message = `<!channel>, The board of team ${teamNumber} is ready`;
const message = `<!here>, The board of team ${teamNumber} is ready`;
this.chatHandler.postMessage(responsiblesChannelId, message);

if (isLastSubBoard) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class SlackResponsibleApplication implements ResponsibleApplicationInterf

const newResponsibleId = await this.usersHandler.getSlackUserIdByEmail(newResponsibleEmail);

const message = `<!channel>, <@${newResponsibleId}> is the new responsible for the team ${teamNumber}`;
const message = `<!here>, <@${newResponsibleId}> is the new responsible for the team ${teamNumber}`;

if (mainChannelId) {
await this.chatHandler.postMessage(mainChannelId, message);
Expand Down
2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "frontend",
"version": "v0.1.9",
"version": "v0.1.10",
"private": true,
"scripts": {
"dev": "next dev",
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 @@ -22,7 +22,7 @@ export const createBoardRequest = (newBoard: CreateBoardDto): Promise<BoardType>
fetchData(`/boards`, { method: 'POST', data: newBoard });

export const updateBoardRequest = (
board: UpdateBoardType & { socketId: string },
board: UpdateBoardType & { socketId: string; deletedColumns?: string[] },
): Promise<BoardType> => fetchData(`/boards/${board._id}`, { method: 'PUT', data: board });

export const getBoardRequest = (
Expand Down
14 changes: 11 additions & 3 deletions frontend/src/components/Board/RegularBoard/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { Container } from '@/styles/pages/boards/board.styles';
import DragDropArea from '@/components/Board/DragDropArea';
import LoadingPage from '@/components/loadings/LoadingPage';
import Flex from '@/components/Primitives/Flex';
import { boardInfoState, editColumnsState } from '@/store/board/atoms/board.atom';
import {
boardInfoState,
deletedColumnsState,
editColumnsState,
} from '@/store/board/atoms/board.atom';
import { BoardUserRoles } from '@/utils/enums/board.user.roles';
import Button from '@/components/Primitives/Button';
import Icon from '@/components/icons/Icon';
Expand All @@ -26,10 +30,14 @@ const RegularBoard = ({ socketId }: RegularBoardProps) => {
// Recoil States
const { board } = useRecoilValue(boardInfoState);
const setEditColumns = useSetRecoilState(editColumnsState);
const setDeletedColumns = useSetRecoilState(deletedColumnsState);

useMemo(() => {
if (!isOpen) setEditColumns(board.columns);
}, [board.columns, isOpen, setEditColumns]);
if (!isOpen) {
setEditColumns(board.columns);
setDeletedColumns([]);
}
}, [board.columns, isOpen, setDeletedColumns, setEditColumns]);

// Session Details
const { data: session } = useSession({ required: true });
Expand Down
8 changes: 7 additions & 1 deletion frontend/src/components/Board/Settings/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,11 @@ import Separator from '@/components/Primitives/Separator';
import Text from '@/components/Primitives/Text';
import useBoard from '@/hooks/useBoard';
import SchemaUpdateBoard from '@/schema/schemaUpdateBoardForm';
import { boardInfoState, editColumnsState } from '@/store/board/atoms/board.atom';
import {
boardInfoState,
deletedColumnsState,
editColumnsState,
} from '@/store/board/atoms/board.atom';
import { UpdateBoardType } from '@/types/board/board';
import { BoardUserToAdd } from '@/types/board/board.user';
import { BoardUserRoles } from '@/utils/enums/board.user.roles';
Expand Down Expand Up @@ -73,6 +77,7 @@ const BoardSettings = ({
} = useRecoilValue(boardInfoState);

const [editColumns, setEditColumns] = useRecoilState(editColumnsState);
const deletedColumns = useRecoilValue(deletedColumnsState);

// State used to change values
const initialData: UpdateBoardType = {
Expand Down Expand Up @@ -265,6 +270,7 @@ const BoardSettings = ({
title,
maxVotes,
columns: isRegularBoard ? updatedColumns : data.columns,
deletedColumns,
socketId,
responsible: data.users?.find((user) => user.role === BoardUserRoles.RESPONSIBLE),
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import Text from '@/components/Primitives/Text';
// import { AlertDialogTrigger } from '@radix-ui/react-alert-dialog';
import Tooltip from '@/components/Primitives/Tooltip';
import Icon from '@/components/icons/Icon';
import { editColumnsState } from '@/store/board/atoms/board.atom';
import { deletedColumnsState, editColumnsState } from '@/store/board/atoms/board.atom';
import { useRecoilState } from 'recoil';
import {
AlertDialog,
Expand All @@ -13,6 +13,7 @@ import {
AlertDialogTrigger,
} from '@/components/Primitives/AlertDialog';
import Button from '@/components/Primitives/Button';
import ColumnType from '@/types/column';

interface Props {
columnTitle: string;
Expand All @@ -22,10 +23,15 @@ interface Props {

const DeleteColumnButton = ({ columnTitle, columnIndex, disableDeleteColumn }: Props) => {
const [editColumns, setEditColumns] = useRecoilState(editColumnsState);
const [deletedColumns, setDeletedColumns] = useRecoilState(deletedColumnsState);

const handleDeleteColumn = () => {
const arrayWithoutColumn = [...editColumns];
arrayWithoutColumn.splice(columnIndex, 1);

const column = arrayWithoutColumn.splice(columnIndex, 1)[0] as ColumnType;

setEditColumns(arrayWithoutColumn);
if (column._id) setDeletedColumns([...deletedColumns, column._id]);
};

return (
Expand Down
Loading