diff --git a/api/src/controllers/comments.ts b/api/src/controllers/comments.ts new file mode 100644 index 0000000..3b4087c --- /dev/null +++ b/api/src/controllers/comments.ts @@ -0,0 +1,33 @@ +import express from 'express'; + +import { Comment } from 'entities'; +import { catchErrors } from 'errors'; +import { updateEntity, deleteEntity, createEntity } from 'utils/typeorm'; + +const router = express.Router(); + +router.post( + '/comments', + catchErrors(async (req, res) => { + const comment = await createEntity(Comment, req.body); + res.respond({ comment }); + }), +); + +router.put( + '/comments/:commentId', + catchErrors(async (req, res) => { + const comment = await updateEntity(Comment, req.params.commentId, req.body); + res.respond({ comment }); + }), +); + +router.delete( + '/comments/:commentId', + catchErrors(async (req, res) => { + const comment = await deleteEntity(Comment, req.params.commentId); + res.respond({ comment }); + }), +); + +export default router; diff --git a/api/src/controllers/issues.ts b/api/src/controllers/issues.ts index bab172f..cd7d12b 100644 --- a/api/src/controllers/issues.ts +++ b/api/src/controllers/issues.ts @@ -10,7 +10,7 @@ router.get( '/issues/:issueId', catchErrors(async (req, res) => { const issue = await findEntityOrThrow(Issue, req.params.issueId, { - relations: ['users', 'comments'], + relations: ['users', 'comments', 'comments.user'], }); res.respond({ issue }); }), diff --git a/api/src/entities/Comment.ts b/api/src/entities/Comment.ts index a96d193..f77f4a0 100644 --- a/api/src/entities/Comment.ts +++ b/api/src/entities/Comment.ts @@ -6,7 +6,6 @@ import { CreateDateColumn, UpdateDateColumn, ManyToOne, - RelationId, } from 'typeorm'; import is from 'utils/validation'; @@ -36,7 +35,7 @@ class Comment extends BaseEntity { ) user: User; - @RelationId((comment: Comment) => comment.user) + @Column('integer') userId: number; @ManyToOne( @@ -45,6 +44,9 @@ class Comment extends BaseEntity { { onDelete: 'CASCADE' }, ) issue: Issue; + + @Column('integer') + issueId: number; } export default Comment; diff --git a/api/src/index.ts b/api/src/index.ts index a38bb9a..4184769 100644 --- a/api/src/index.ts +++ b/api/src/index.ts @@ -7,8 +7,9 @@ import cors from 'cors'; import createDatabaseConnection from 'database/connection'; import { authenticateUser } from 'middleware/authentication'; import authenticationRoutes from 'controllers/authentication'; -import projectsRoutes from 'controllers/projects'; +import commentsRoutes from 'controllers/comments'; import issuesRoutes from 'controllers/issues'; +import projectsRoutes from 'controllers/projects'; import usersRoutes from 'controllers/users'; import { RouteNotFoundError } from 'errors'; import { errorHandler } from 'errors/errorHandler'; @@ -40,8 +41,9 @@ const initializeExpress = (): void => { app.use('/', authenticateUser); - app.use('/', projectsRoutes); + app.use('/', commentsRoutes); app.use('/', issuesRoutes); + app.use('/', projectsRoutes); app.use('/', usersRoutes); app.use((req, _res, next) => next(new RouteNotFoundError(req.originalUrl))); diff --git a/client/src/components/Project/Board/IssueDetails/Comments/BodyForm/Styles.js b/client/src/components/Project/Board/IssueDetails/Comments/BodyForm/Styles.js new file mode 100644 index 0000000..c25a3d6 --- /dev/null +++ b/client/src/components/Project/Board/IssueDetails/Comments/BodyForm/Styles.js @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +import { Button } from 'shared/components'; + +export const Actions = styled.div` + display: flex; + padding-top: 10px; +`; + +export const FormButton = styled(Button)` + margin-right: 6px; +`; diff --git a/client/src/components/Project/Board/IssueDetails/Comments/BodyForm/index.jsx b/client/src/components/Project/Board/IssueDetails/Comments/BodyForm/index.jsx new file mode 100644 index 0000000..d96a728 --- /dev/null +++ b/client/src/components/Project/Board/IssueDetails/Comments/BodyForm/index.jsx @@ -0,0 +1,54 @@ +import React, { useRef } from 'react'; +import PropTypes from 'prop-types'; + +import { Textarea } from 'shared/components'; +import { Actions, FormButton } from './Styles'; + +const propTypes = { + value: PropTypes.string.isRequired, + onChange: PropTypes.func.isRequired, + isWorking: PropTypes.bool.isRequired, + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +}; + +const ProjectBoardIssueDetailsCommentsBodyForm = ({ + value, + onChange, + isWorking, + onSubmit, + onCancel, +}) => { + const $textareaRef = useRef(); + return ( + <> +