Commit 51adc624 authored by Болатов Ален's avatar Болатов Ален

Merge branch 'master' into 'dev'

# Conflicts:
#   backend/src/controllers/comments.ts
#   backend/src/repository/mongoose.ts
#   frontend/src/containers/DetailsPost/DetailsPost.css
#   frontend/src/containers/DetailsPost/DetailsPost.tsx
parents e5f6ca58 fc57e0b5
...@@ -179,6 +179,10 @@ export class Mongo implements IDataBase { ...@@ -179,6 +179,10 @@ export class Mongo implements IDataBase {
return response return response
} }
} }
<<<<<<< backend/src/repository/mongoose.ts
=======
>>>>>>> backend/src/repository/mongoose.ts
public addComment = async (commentDto: ICommentDto): Promise<IResponse<IComment | undefined>> => { public addComment = async (commentDto: ICommentDto): Promise<IResponse<IComment | undefined>> => {
try { try {
const comment = new Comment(commentDto) const comment = new Comment(commentDto)
......
...@@ -4,6 +4,7 @@ import Layout from './components/Layout'; ...@@ -4,6 +4,7 @@ import Layout from './components/Layout';
import HomePage from './containers/HomePage'; import HomePage from './containers/HomePage';
import Login from './containers/Login'; import Login from './containers/Login';
import CreatePost from './components/CreatePost/CreatePost'; import CreatePost from './components/CreatePost/CreatePost';
import DetailsPost from './containers/DetailsPost/DetailsPost';
const App: React.FunctionComponent = (): React.ReactElement => { const App: React.FunctionComponent = (): React.ReactElement => {
return ( return (
...@@ -12,6 +13,7 @@ const App: React.FunctionComponent = (): React.ReactElement => { ...@@ -12,6 +13,7 @@ const App: React.FunctionComponent = (): React.ReactElement => {
<Route path="/" element={<Layout />}> <Route path="/" element={<Layout />}>
<Route index element={<HomePage />} /> <Route index element={<HomePage />} />
<Route path="register" element={<Login />} /> <Route path="register" element={<Login />} />
<Route path="/posts/:id" element={<DetailsPost />} />
<Route path="create" element={<CreatePost />} /> <Route path="create" element={<CreatePost />} />
</Route> </Route>
</Routes> </Routes>
......
import { FunctionComponent, ReactElement, useState, ChangeEvent, FormEvent } from 'react'; import {
import { AppDispatch, AppState } from '../../store/store'; FunctionComponent,
import { shallowEqual, useDispatch } from 'react-redux'; ReactElement,
useState,
ChangeEvent,
FormEvent,
} from 'react';
import {AppDispatch, AppState} from '../../store/store';
import {shallowEqual, useDispatch} from 'react-redux';
import IPostDto from '../../interfaces/IPostDto'; import IPostDto from '../../interfaces/IPostDto';
import { createPost } from '../../store/posts/posts.slice'; import {createPost} from '../../store/posts/posts.slice';
import { useSelector } from 'react-redux'; import {useSelector} from 'react-redux';
const CreatePost: FunctionComponent = (): ReactElement => { const CreatePost: FunctionComponent = (): ReactElement => {
const { user } = useSelector((state: AppState) => state.user,shallowEqual); const {user} = useSelector((state: AppState) => state.user, shallowEqual);
const dispatch: AppDispatch = useDispatch() const dispatch: AppDispatch = useDispatch();
const [post, setPost] = useState<IPostDto>({ const [post, setPost] = useState<IPostDto>({
title: '', title: '',
description: '', description: '',
image: undefined image: undefined,
}) });
const [fileName, setFileName] = useState<string>('') const [fileName, setFileName] = useState<string>('');
const inputHandler = (e: ChangeEvent<HTMLInputElement>): void => { const inputHandler = (e: ChangeEvent<HTMLInputElement>): void => {
setPost(prevState => { setPost((prevState) => {
return { ...prevState, [e.target.name]: e.target.value } return {...prevState, [e.target.name]: e.target.value};
}) });
} };
const inputFileHandler = (e: ChangeEvent<HTMLInputElement>): void => { const inputFileHandler = (e: ChangeEvent<HTMLInputElement>): void => {
setPost(prevState => { setPost((prevState) => {
return { return {
...prevState, ...prevState,
image: e.target.files ? e.target.files[0] : undefined image: e.target.files ? e.target.files[0] : undefined,
} };
}) });
setFileName(e.target.files && e.target.files[0] ? e.target.files[0].name : '') setFileName(
} e.target.files && e.target.files[0] ? e.target.files[0].name : ''
const submitHandler = (e: FormEvent) => { );
e.preventDefault() };
const submitHandler = (e: FormEvent) => {
e.preventDefault();
const formData = new FormData();
formData.append('title', post.title);
formData.append('description', post.description);
formData.append('user', user._id);
dispatch(createPost(formData));
setPost(post);
};
return (
<div className="bg-gray-100 p-4">
<form
onSubmit={submitHandler}
className="flex gap-4 flex-col items-center"
>
<input
name="title"
type="text"
placeholder="Title"
value={post.title}
onChange={inputHandler}
className="border border-gray-400 rounded-md px-3 py-2 focus:outline-none focus:border-blue-400 flex-grow"
/>
const formData = new FormData(); <input
Object.keys(post).forEach((key: any) => { name="description"
//@ts-ignore type="text"
formData.append(key, post[key]) placeholder="Description"
}) value={post.description}
formData.append("user", user._id) onChange={inputHandler}
className="border border-gray-400 rounded-md px-3 py-2 focus:outline-none focus:border-blue-400 flex-grow"
/>
dispatch(createPost(formData)) <label className="relative flex items-center justify-center bg-white border border-gray-400 rounded-md px-4 py-2 cursor-pointer">
setPost(post) <input
} type="file"
return ( name="image"
<div className="bg-gray-100 p-4"> onChange={inputFileHandler}
<form onSubmit={submitHandler} className="flex gap-4 flex-col items-center"> className="hidden"
<input />
<span className="text-gray-500 font-medium mr-2">CHOOSE FILE</span>
name="title" <span className="text-gray-500 font-medium">{fileName}</span>
type="text" </label>
placeholder="Title"
value={post.title}
onChange={inputHandler}
className="border border-gray-400 rounded-md px-3 py-2 focus:outline-none focus:border-blue-400 flex-grow"
/>
<input <button className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-md">
name="description" SEND
type="text" </button>
placeholder="Description" </form>
value={post.description} </div>
onChange={inputHandler} );
className="border border-gray-400 rounded-md px-3 py-2 focus:outline-none focus:border-blue-400 flex-grow" };
/>
<label className="relative flex items-center justify-center bg-white border border-gray-400 rounded-md px-4 py-2 cursor-pointer"> export default CreatePost;
<input
type="file"
name="image"
onChange={inputFileHandler}
className="hidden"
/>
<span className="text-gray-500 font-medium mr-2">CHOOSE FILE</span>
<span className="text-gray-500 font-medium">{fileName}</span>
</label>
<button className="bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded-md">
SEND
</button>
</form>
</div>
)
}
export default CreatePost
import React, { FunctionComponent, MutableRefObject, ReactElement, useRef } from "react"; import React, { FunctionComponent, MutableRefObject, ReactElement, useRef } from "react";
import IPost from "../../interfaces/IPost"; import IPost from "../../interfaces/IPost";
import defaultImage from '../../assets/default-image.jpg' import defaultImage from '../../assets/default-image.jpg'
import { useNavigate } from "react-router-dom";
interface IPostProps { interface IPostProps {
post: IPost; post: IPost;
......
...@@ -6,10 +6,17 @@ import { useSelector } from "react-redux"; ...@@ -6,10 +6,17 @@ import { useSelector } from "react-redux";
import { RootState } from "../../store/store"; import { RootState } from "../../store/store";
import { createComment, getCommentsByPost } from "../../store/comments/comments.slice"; import { createComment, getCommentsByPost } from "../../store/comments/comments.slice";
import Comment from "../../components/Comment/Comment"; import Comment from "../../components/Comment/Comment";
<<<<<<< frontend/src/containers/DetailsPost/DetailsPost.tsx
import Post from "../../components/Post/Post"; import Post from "../../components/Post/Post";
import ICommentDto from "../../interfaces/ICommentDto"; import ICommentDto from "../../interfaces/ICommentDto";
import './DetailsPost.css' import './DetailsPost.css'
=======
import ICommentDto from "../../interfaces/ICommentDto";
import Post from "../../components/Post/Post";
>>>>>>> frontend/src/containers/DetailsPost/DetailsPost.tsx
const DetailsPost: FunctionComponent = (): ReactElement => { const DetailsPost: FunctionComponent = (): ReactElement => {
const params: any = useParams(); const params: any = useParams();
const dispatch = useDispatch(); const dispatch = useDispatch();
...@@ -21,6 +28,10 @@ const DetailsPost: FunctionComponent = (): ReactElement => { ...@@ -21,6 +28,10 @@ const DetailsPost: FunctionComponent = (): ReactElement => {
post: params.id, post: params.id,
comment: '' comment: ''
}) })
<<<<<<< frontend/src/containers/DetailsPost/DetailsPost.tsx
=======
>>>>>>> frontend/src/containers/DetailsPost/DetailsPost.tsx
useEffect(() => { useEffect(() => {
//@ts-ignore //@ts-ignore
dispatch(getPostById(params.id)) dispatch(getPostById(params.id))
...@@ -52,13 +63,17 @@ const DetailsPost: FunctionComponent = (): ReactElement => { ...@@ -52,13 +63,17 @@ const DetailsPost: FunctionComponent = (): ReactElement => {
<div className="container"> <div className="container">
<Post post={post} /> <Post post={post} />
{comments ? <h1>Comments</h1> : ''} {comments ? <h1>Comments</h1> : ''}
{comments.map(c => { {comments ? comments.map(c => {
return <Comment return <Comment
key={c._id} key={c._id}
params_id={params.id} params_id={params.id}
comments={c} comments={c}
/> />
<<<<<<< frontend/src/containers/DetailsPost/DetailsPost.tsx
})} })}
=======
}): ''}
>>>>>>> frontend/src/containers/DetailsPost/DetailsPost.tsx
<h2>Add comment</h2> <h2>Add comment</h2>
<div className="addcomment_block"> <div className="addcomment_block">
<div className="addComment"> <div className="addComment">
...@@ -72,6 +87,10 @@ const DetailsPost: FunctionComponent = (): ReactElement => { ...@@ -72,6 +87,10 @@ const DetailsPost: FunctionComponent = (): ReactElement => {
<button className="addcomment_btn" onClick={sendComment}>Send</button> <button className="addcomment_btn" onClick={sendComment}>Send</button>
</div> </div>
<<<<<<< frontend/src/containers/DetailsPost/DetailsPost.tsx
=======
>>>>>>> frontend/src/containers/DetailsPost/DetailsPost.tsx
</div> </div>
</div> </div>
) )
......
import {createAsyncThunk, createSlice} from '@reduxjs/toolkit'; import {createAsyncThunk, createSlice} from '@reduxjs/toolkit';
import IPost from '../../interfaces/IPost' import IPost from '../../interfaces/IPost';
import { postApi } from '../../api/postApi' import {postApi} from '../../api/postApi';
import IResponse from '../../interfaces/IResponse' import IResponse from '../../interfaces/IResponse';
interface IPostState { interface IPostState {
posts: IPost[] posts: IPost[];
post: IPost post: IPost;
loadingPosts: boolean loadingPosts: boolean;
messagePosts: string messagePosts: string;
} }
const initialState:IPostState = { const initialState: IPostState = {
posts: [] as IPost[], posts: [] as IPost[],
post: {} as IPost, post: {} as IPost,
loadingPosts: false, loadingPosts: false,
messagePosts: '' messagePosts: '',
} };
const namespace = 'posts' const namespace = 'posts';
export const getPosts = createAsyncThunk( export const getPosts = createAsyncThunk(
`${namespace}/getPosts`, `${namespace}/getPosts`,
async (): Promise<IResponse<IPost[] | undefined>> => { async (): Promise<IResponse<IPost[] | undefined>> => {
return postApi.getPosts() return await postApi.getPosts();
} }
) );
export const getPostById = createAsyncThunk( export const getPostById = createAsyncThunk(
`${namespace}/getPostById`, `${namespace}/getPostById`,
async (id: string): Promise<IResponse<IPost | undefined>> => { async (id: string): Promise<IResponse<IPost | undefined>> => {
return postApi.getPostById(id) return await postApi.getPostById(id);
} }
) );
export const createPost = createAsyncThunk( export const createPost = createAsyncThunk(
`${namespace}/createPost`, `${namespace}/createPost`,
async (post: FormData) => { async (post: FormData) => {
return postApi.createPost(post) return await postApi.createPost(post);
} }
) );
export const deletePostById = createAsyncThunk( export const deletePostById = createAsyncThunk(
`${namespace}/deletePostById`, `${namespace}/deletePostById`,
async (id: string) => { async (id: string) => {
return postApi.deletePostById(id) return await postApi.deletePostById(id);
} }
) );
export const postsSlice = createSlice({ export const postsSlice = createSlice({
name: namespace, name: namespace,
initialState, initialState,
reducers: {}, reducers: {},
extraReducers: (builder) => { extraReducers: (builder) => {
builder builder
.addCase(getPosts.rejected, (state) => { .addCase(getPosts.rejected, (state) => {
state.loadingPosts = false state.loadingPosts = false;
}) })
.addCase(getPosts.pending, (state) => { .addCase(getPosts.pending, (state) => {
state.loadingPosts = true state.loadingPosts = true;
}) })
.addCase(getPosts.fulfilled, (state, action) => { .addCase(getPosts.fulfilled, (state, action) => {
state.loadingPosts = false state.loadingPosts = false;
state.posts = action.payload.result as IPost[] state.posts = action.payload.result as IPost[];
state.messagePosts = action.payload.message state.messagePosts = action.payload.message;
}) })
.addCase(getPostById.rejected, (state) => { .addCase(getPostById.rejected, (state) => {
state.loadingPosts = false state.loadingPosts = false;
}) })
.addCase(getPostById.pending, (state) => { .addCase(getPostById.pending, (state) => {
state.loadingPosts = true state.loadingPosts = true;
}) })
.addCase(getPostById.fulfilled, (state, action) => { .addCase(getPostById.fulfilled, (state, action) => {
state.loadingPosts = false state.loadingPosts = false;
state.post = action.payload.result as IPost state.post = action.payload.result as IPost;
state.messagePosts = action.payload.message state.messagePosts = action.payload.message;
}) })
.addCase(createPost.rejected, (state) => { .addCase(createPost.rejected, (state) => {
state.loadingPosts = false state.loadingPosts = false;
}) })
.addCase(createPost.pending, (state) => { .addCase(createPost.pending, (state) => {
state.loadingPosts = true state.loadingPosts = true;
}) })
.addCase(createPost.fulfilled, (state, action) => { .addCase(createPost.fulfilled, (state, action) => {
state.loadingPosts = false state.loadingPosts = false;
state.messagePosts = action.payload.message state.messagePosts = action.payload.message;
}) console.log(action.payload);
.addCase(deletePostById.rejected, (state) => { })
state.loadingPosts = false .addCase(deletePostById.rejected, (state) => {
}) state.loadingPosts = false;
.addCase(deletePostById.pending, (state) => { })
state.loadingPosts = true .addCase(deletePostById.pending, (state) => {
}) state.loadingPosts = true;
.addCase(deletePostById.fulfilled, (state, action) => { })
state.loadingPosts = false .addCase(deletePostById.fulfilled, (state, action) => {
state.messagePosts = action.payload.message state.loadingPosts = false;
}) state.messagePosts = action.payload.message;
} });
}) },
});
export default postsSlice.reducer; export default postsSlice.reducer;
\ No newline at end of file
...@@ -104,6 +104,7 @@ export const userSlice = createSlice({ ...@@ -104,6 +104,7 @@ export const userSlice = createSlice({
state.user.username = action.payload[0].username; state.user.username = action.payload[0].username;
state.loading = false; state.loading = false;
state.userLoggedIn = true; state.userLoggedIn = true;
state.user._id = action.payload[0]._id;
}); });
}, },
}); });
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment