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 30, 2022
1 parent 860850b commit d99cfa5
Show file tree
Hide file tree
Showing 92 changed files with 21,214 additions and 30,082 deletions.
1,435 changes: 578 additions & 857 deletions backend/package-lock.json

Large diffs are not rendered by default.

40 changes: 20 additions & 20 deletions backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
"pre-commit": "lint-staged"
},
"dependencies": {
"@nestjs/common": "^8.2.4",
"@nestjs/common": "^8.2.6",
"@nestjs/config": "^1.1.6",
"@nestjs/core": "^8.2.4",
"@nestjs/core": "^8.2.6",
"@nestjs/jwt": "^8.0.0",
"@nestjs/passport": "^8.0.1",
"@nestjs/platform-express": "^8.2.4",
"@nestjs/platform-socket.io": "^8.2.4",
"@nestjs/websockets": "^8.2.4",
"@nestjs/passport": "^8.1.0",
"@nestjs/platform-express": "^8.2.6",
"@nestjs/platform-socket.io": "^8.2.6",
"@nestjs/websockets": "^8.2.6",
"@nestjs/mongoose": "^9.0.2",
"@types/bcrypt": "^5.0.0",
"@types/passport-jwt": "^3.0.6",
Expand All @@ -41,41 +41,41 @@
"eslint-config-airbnb": "^19.0.4",
"eslint-config-airbnb-typescript": "^16.1.0",
"express": "^4.17.2",
"mongoose": "^6.1.5",
"joi": "^17.5.0",
"lint-staged": "^12.1.6",
"mongodb": "^4.3.0",
"mongoose": "^6.1.8",
"joi": "^17.6.0",
"lint-staged": "^12.3.2",
"mongodb": "^4.3.1",
"passport": "^0.5.2",
"passport-jwt": "^4.0.0",
"passport-local": "^1.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
"rxjs": "^7.5.1",
"rxjs": "^7.5.2",
"socket.io": "^4.4.1"
},
"devDependencies": {
"@nestjs/cli": "^8.1.6",
"@nestjs/cli": "^8.2.0",
"@nestjs/schematics": "^8.0.5",
"@nestjs/testing": "^8.2.4",
"@nestjs/testing": "^8.2.6",
"@types/express": "^4.17.13",
"@types/jest": "^27.4.0",
"@types/mongodb": "^4.0.7",
"@types/node": "^17.0.8",
"@types/node": "^17.0.13",
"@types/supertest": "^2.0.11",
"@typescript-eslint/eslint-plugin": "^5.9.0",
"@typescript-eslint/parser": "^5.9.0",
"eslint": "^8.6.0",
"@typescript-eslint/eslint-plugin": "^5.10.1",
"@typescript-eslint/parser": "^5.10.1",
"eslint": "^8.8.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.4.7",
"prettier": "^2.5.1",
"source-map-support": "^0.5.21",
"supertest": "^6.1.6",
"ts-jest": "^27.1.2",
"supertest": "^6.2.2",
"ts-jest": "^27.1.3",
"ts-loader": "^9.2.6",
"ts-node": "^10.4.0",
"tsconfig-paths": "^3.12.0",
"typescript": "^4.5.4"
"typescript": "^4.5.5"
},
"lint-staged": {
"*.{js,ts,tsx}": [
Expand Down
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
2 changes: 1 addition & 1 deletion backend/src/models/boards/schemas/board.schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export default class Board {
@Prop({ nullable: true })
password?: string;

@Prop({ type: Date })
@Prop({ type: Date, default: Date.now })
creationDate!: Date;

@Prop({ nullable: false })
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 d99cfa5

Please sign in to comment.