Skip to content

Commit

Permalink
feat: add, edit, delete cards
Browse files Browse the repository at this point in the history
  • Loading branch information
nunocaseiro committed Jan 29, 2022
1 parent 860850b commit d9a024a
Show file tree
Hide file tree
Showing 86 changed files with 18,915 additions and 11,053 deletions.
12 changes: 5 additions & 7 deletions backend/src/auth/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,18 +41,16 @@ export default class AuthController {
} = request;

if (id) {
const accessToken = this.authService.getJwtAccessToken(id);

const refreshToken = this.authService.getJwtRefreshToken(id);

await this.usersService.setCurrentRefreshToken(refreshToken.token, id);
const { accessToken, refreshToken } = await this.authService.getJwt(id);

const userWToken: LoggedUserDto = {
id,
name,
email,
accessToken,
refreshToken,
accessToken: accessToken.token,
accessTokenExpiresIn: accessToken.expiresIn,
refreshToken: refreshToken.token,
refreshTokenExpiresIn: refreshToken.expiresIn,
};
return response.send(userWToken);
}
Expand Down
11 changes: 11 additions & 0 deletions backend/src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,17 @@ export default class AuthService {
}
}

async getJwt(userId: string) {
const accessToken = this.getJwtAccessToken(userId);
const refreshToken = this.getJwtRefreshToken(userId);
await this.usersService.setCurrentRefreshToken(refreshToken.token, userId);

return {
accessToken,
refreshToken,
};
}

public getJwtAccessToken(userId: string) {
const payload: TokenPayload = { userId };
const token = this.jwtService.sign(payload, {
Expand Down
18 changes: 11 additions & 7 deletions backend/src/models/boards/boards.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,10 @@ export default class BoardsService {
},
{
$push: {
'columns.$.cards': { ...card },
'columns.$.cards': {
$each: [{ ...card }],
$position: 0,
},
},
},
{ new: true, rawResult: true },
Expand All @@ -222,16 +225,18 @@ export default class BoardsService {
{
_id: boardId,
'columns.cards._id': cardId,
'columns.cards.createdBy': userId,
},
{
$set: {
'columns.$.cards.$[c].text': text,
'columns.$.cards.$[c].items.$[item].text': text,
'columns.$.cards.$[card].text': text,
'columns.$.cards.$[card].items.$[item].text': text,
},
},
{
arrayFilters: [{ 'c._id': cardId }, { 'item._id': cardItemId }],
arrayFilters: [
{ 'card._id': cardId },
{ 'item._id': cardItemId, 'item.createdBy': userId },
],
new: true,
rawResult: true,
},
Expand All @@ -248,11 +253,10 @@ export default class BoardsService {
{
_id: boardId,
'columns.cards._id': cardId,
'columns.cards.createdBy': userId,
},
{
$pull: {
'columns.$[].cards': { _id: cardId },
'columns.$[].cards': { _id: cardId, createdBy: userId },
},
},
{ new: true, rawResult: true },
Expand Down
14 changes: 12 additions & 2 deletions backend/src/models/users/dto/loggedUser.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,18 @@ export default class LoggedUserDto {
email!: string;

@IsNotEmpty()
accessToken!: { expiresIn: string; token: string };
@IsString()
accessToken!: string;

@IsNotEmpty()
@IsString()
accessTokenExpiresIn!: string;

@IsNotEmpty()
refreshToken!: { expiresIn: string; token: string };
@IsString()
refreshToken!: string;

@IsNotEmpty()
@IsString()
refreshTokenExpiresIn!: string;
}
10 changes: 8 additions & 2 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,20 @@
},
"ecmaVersion": 12,
"sourceType": "module",
"extraFileExtensions": [".css"]
"extraFileExtensions": [".css"],
"project": "./tsconfig.json"
},
"plugins": ["react", "@typescript-eslint"],
"rules": {
"react/react-in-jsx-scope": "off",
"react/jsx-filename-extension": [1, { "extensions": [".ts", ".tsx"] }],
"react/jsx-props-no-spreading": "off",
"no-underscore-dangle": "off",
"no-param-reassign": "off"
"no-param-reassign": "off",
"react/function-component-definition": "off",
"@typescript-eslint/no-empty-interface": "off",
"import/prefer-default-export": "off",
"import/no-cycle": "off",
"no-plusplus" : "off"
}
}
63 changes: 51 additions & 12 deletions frontend/api/boardService.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,66 @@
import fetchData from "../utils/fetchData";
import { BoardType, UpdateTitleWithToken } from "../types/board";
import { Nullable } from "../types/common";
import BoardType, { BoardToAdd } from "../types/board/board";
import UpdateCardPositionDto from "../types/card/updateCardPosition.dto";
import UpdateBoardDto from "../types/board/updateBoard";
import AddCardDto from "../types/card/addCard.dto";
import DeleteCardDto from "../types/card/deleteCard.dto";
import UpdateCardDto from "../types/card/updateCard.dto";
// #region BOARD

export const postBoard = (newBoard: BoardType): Promise<BoardType> => {
export const createBoardRequest = (newBoard: BoardToAdd): Promise<BoardType> => {
return fetchData(`/boards`, { method: "POST", data: newBoard });
};

export const updateBoardTitle = ({ id, title }: UpdateTitleWithToken): Promise<BoardType> => {
return fetchData(`/boards/${id}/updateTitle`, { method: "PATCH", data: { title } });
export const updateBoardRequest = ({ board }: UpdateBoardDto): Promise<BoardType> => {
return fetchData(`/boards/${board._id}`, { method: "PUT", data: board });
};

export const getBoard = (id: Nullable<string>): Promise<BoardType> => {
return fetchData<BoardType>(`/boards/${id}`, { method: "POST" });
};

export const getBoardWithAuth = (id: string): Promise<BoardType> => {
export const getBoardRequest = (id: string): Promise<BoardType> => {
return fetchData<BoardType>(`/boards/${id}`);
};

export const getBoards = (): Promise<BoardType[]> => {
export const getBoardsRequest = (): Promise<BoardType[]> => {
return fetchData<BoardType[]>(`/boards`);
};

export const deleteBoard = async (id: string): Promise<BoardType> => {
export const deleteBoardRequest = async (id: string): Promise<BoardType> => {
return fetchData(`/boards/${id}`, { method: "DELETE" });
};

// #endregion

// #region CARD

export const addCardRequest = (addCardDto: AddCardDto): Promise<BoardType> => {
return fetchData<BoardType>(`/boards/${addCardDto.boardId}/card`, {
method: "POST",
data: addCardDto,
});
};

export const updateCardRequest = (updateCard: UpdateCardDto): Promise<BoardType> => {
return fetchData<BoardType>(
updateCard.isCardGroup
? `/boards/${updateCard.boardId}/card/${updateCard.cardId}`
: `/boards/${updateCard.boardId}/card/${updateCard.cardId}/items/${updateCard.cardItemId}`,
{ method: "PUT", data: updateCard }
);
};

export const updateCardPositionRequest = (
updateCardPosition: UpdateCardPositionDto
): Promise<BoardType> => {
return fetchData<BoardType>(
`/boards/${updateCardPosition.boardId}/card/${updateCardPosition.cardId}/updateCardPosition`,
{ method: "PUT", data: updateCardPosition }
);
};

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

// #endregion
90 changes: 90 additions & 0 deletions frontend/components/Board/BoardAlertDialog.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { Cross1Icon } from "@modulz/radix-icons";
import { styled } from "../../stitches.config";
import {
AlertDialog,
AlertDialogAction,
AlertDialogCancel,
AlertDialogContent,
AlertDialogOverlay,
AlertDialogPortal,
AlertDialogTrigger,
} from "../Primitives/AlertDialog";
import Button from "../Primitives/Button";
import Text from "../Primitives/Text";
import Flex from "../Primitives/Flex";
import ClickEvent from "../../types/events/clickEvent";

const CloseButton = styled(AlertDialogCancel, Button, {
position: "relative",
top: "0",
left: "0",
});

const ActionButton = styled(AlertDialogAction, Button, {
position: "relative",
top: "0",
left: "0",
});

const StyledCrossIcon = styled(Cross1Icon, { size: "$15" });

interface BoardAlertDialogProps {
text: string;
defaultOpen: boolean;
handleConfirm: (event: ClickEvent<HTMLButtonElement, MouseEvent>) => void;
handleClose: (event: ClickEvent<HTMLButtonElement, MouseEvent>) => void;
}

const BoardAlertDialog: React.FC<BoardAlertDialogProps> = ({
text,
handleConfirm,
handleClose,
defaultOpen,
}) => {
const handleStopPropagation = <T,>(event: ClickEvent<T, MouseEvent>) => {
event.stopPropagation();
};

return (
<AlertDialog defaultOpen={defaultOpen}>
{!defaultOpen && (
<AlertDialogTrigger align="center" asChild>
<Button
id="delete-item"
size="20"
onClick={handleStopPropagation}
variant="ghost"
css={{ cursor: "pointer" }}
>
<StyledCrossIcon />
</Button>
</AlertDialogTrigger>
)}
<AlertDialogPortal>
<AlertDialogOverlay />
<AlertDialogContent
direction="column"
onClick={handleStopPropagation}
css={{ width: "$400" }}
>
<Text>{text}</Text>
<Flex justify="end" css={{ mt: "$16" }} gap="16">
<ActionButton size="2" color="red" css={{ width: "10%" }} onClick={handleConfirm}>
Yes
</ActionButton>
<CloseButton
color="blue"
size="2"
css={{ position: "relative", width: "10%" }}
onClick={handleClose}
>
No
</CloseButton>
</Flex>
</AlertDialogContent>
</AlertDialogPortal>
</AlertDialog>
);
};

export default BoardAlertDialog;
Loading

0 comments on commit d9a024a

Please sign in to comment.