now everything works with React ToolKit

This commit is contained in:
QkoSad
2023-06-06 22:28:48 +03:00
parent 35b69eae3f
commit 703784307a
52 changed files with 36817 additions and 7677 deletions
+23
View File
@@ -0,0 +1,23 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.js
# testing
/coverage
# production
/build
# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local
npm-debug.log*
yarn-debug.log*
yarn-error.log*
+6734 -5503
View File
File diff suppressed because it is too large Load Diff
+6 -10
View File
@@ -1,26 +1,22 @@
{ {
"name": "client", "name": "my-app",
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"dependencies": { "dependencies": {
"@reduxjs/toolkit": "^1.9.5",
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"axios": "^1.1.3", "axios": "^1.4.0",
"moment": "^2.29.4",
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-moment": "^1.1.2", "react-redux": "^8.0.5",
"react-redux": "^8.0.4", "react-router-dom": "^6.11.2",
"react-router-dom": "^6.4.2",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"redux": "^4.2.0",
"redux-devtools-extension": "^2.13.9",
"redux-thunk": "^2.4.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {
"start": "react-scripts start --openssl-legacy-provider start", "start": "react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"
+30 -2
View File
@@ -2,7 +2,7 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" /> <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" /> <meta name="theme-color" content="#000000" />
<link <link
@@ -11,11 +11,39 @@
integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
crossorigin="anonymous" crossorigin="anonymous"
/> />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Welcome To DevConnector</title> <!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head> </head>
<body> <body>
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body> </body>
</html> </html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

+10
View File
@@ -6,6 +6,16 @@
"src": "favicon.ico", "src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16", "sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon" "type": "image/x-icon"
},
{
"src": "logo192.png",
"type": "image/png",
"sizes": "192x192"
},
{
"src": "logo512.png",
"type": "image/png",
"sizes": "512x512"
} }
], ],
"start_url": ".", "start_url": ".",
+3
View File
@@ -0,0 +1,3 @@
# https://www.robotstxt.org/robotstxt.html
User-agent: *
Disallow:
+1
View File
@@ -1,3 +1,4 @@
/* Global Styles */ /* Global Styles */
:root { :root {
--primary-color: #17a2b8; --primary-color: #17a2b8;
+12 -3
View File
@@ -16,7 +16,6 @@ import Posts from './components/posts/Posts';
import Post from './components/post/Post'; import Post from './components/post/Post';
import NotFound from './components/layout/NotFound'; import NotFound from './components/layout/NotFound';
import PrivateRoute from './components/routing/PrivateRoute'; import PrivateRoute from './components/routing/PrivateRoute';
import { LOGOUT } from './actions/types';
// Redux // Redux
import { Provider } from 'react-redux'; import { Provider } from 'react-redux';
@@ -25,6 +24,7 @@ import { loadUser } from './actions/auth';
import setAuthToken from './utils/setAuthToken'; import setAuthToken from './utils/setAuthToken';
import './App.css'; import './App.css';
import { logOut } from './reducers/auth';
// Level - 1 // Level - 1
// //
@@ -42,13 +42,12 @@ const App = () => {
// log user out from all tabs if they log out in one tab // log user out from all tabs if they log out in one tab
window.addEventListener("storage", () => { window.addEventListener("storage", () => {
if (!localStorage.token) store.dispatch({ type: LOGOUT }); if (!localStorage.token) store.dispatch(logOut);
}); });
}, []); }, []);
return ( return (
<Provider store={store}> <Provider store={store}>
// Seting Up redux store
<Router> <Router>
<Navbar /> <Navbar />
<Alert /> <Alert />
@@ -85,6 +84,16 @@ const App = () => {
</Router> </Router>
</Provider> </Provider>
); );
/*
return (
<Provider store={store}>
<Router>
<Routes>
<Route path="login" element={<Login />} />
</Routes>
</Router>
</Provider>
);*/
}; };
export default App; export default App;
+4 -7
View File
@@ -1,12 +1,9 @@
import { v4 as uuidv4 } from 'uuid'; import { v4 as uuidv4 } from 'uuid';
import { SET_ALERT, REMOVE_ALERT } from './types'; import {removeAlert, setAlert } from '../reducers/alert';
export const setAlert = (msg, alertType, timeout = 5000) => dispatch => { export const createAlert = (msg, alertType, timeout = 5000) => dispatch => {
const id = uuidv4(); const id = uuidv4();
dispatch({ dispatch(setAlert({ msg, alertType, id }));
type: SET_ALERT,
payload: { msg, alertType, id }
});
setTimeout(() => dispatch({ type: REMOVE_ALERT, payload: id }), timeout); setTimeout(() => dispatch(removeAlert(id)), timeout);
}; };
+33 -51
View File
@@ -1,15 +1,12 @@
import api from "../utils/api"; import api from "../utils/api";
import { setAlert } from "./alert"; import { createAlert } from "./alert";
import { import {
REGISTER_SUCCESS, loginSucces,
REGISTER_FAIL, authError,
USER_LOADED, registerSuccess,
AUTH_ERROR, logOut,
LOGIN_SUCCESS, userLoaded,
LOGIN_FAIL, } from "../reducers/auth";
LOGOUT,
} from "./types";
/* /*
NOTE: we don't need a config object for axios as the NOTE: we don't need a config object for axios as the
default headers in axios are already Content-Type: application/json default headers in axios are already Content-Type: application/json
@@ -17,19 +14,32 @@ import {
JSON.stringify or JSON.parse JSON.stringify or JSON.parse
*/ */
// Load User export const login = (email, password) => async (dispatch) => {
const body = { email, password };
try {
const res = await api.post("/auth", body);
dispatch(loginSucces(res.data));
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(createAlert(error.msg, "danger")));
}
dispatch({
type: "auth/loginFail",
});
}
};
export const loadUser = () => async (dispatch) => { export const loadUser = () => async (dispatch) => {
try { try {
const res = await api.get("/auth"); const res = await api.get("/auth");
dispatch({
type: USER_LOADED, dispatch(userLoaded(res.data));
payload: res.data,
});
} catch (err) { } catch (err) {
dispatch({ dispatch(authError());
type: AUTH_ERROR,
});
} }
}; };
@@ -38,49 +48,21 @@ export const register = (formData) => async (dispatch) => {
try { try {
const res = await api.post("/users", formData); const res = await api.post("/users", formData);
dispatch({ dispatch(registerSuccess(res.data));
type: REGISTER_SUCCESS,
payload: res.data,
});
dispatch(loadUser()); dispatch(loadUser());
} catch (err) { } catch (err) {
const errors = err.response.data.errors; const errors = err.response.data.errors;
if (errors) { if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger"))); errors.forEach((error) => dispatch(createAlert(error.msg, "danger")));
} }
dispatch({ dispatch({
type: REGISTER_FAIL, type: "auth/registerFail",
}); });
} }
}; };
// Login User export const logout = () => async (dispatch) => {
export const login = (email, password) => async (dispatch) => { dispatch(logOut);
const body = { email, password };
try {
const res = await api.post("/auth", body);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: LOGIN_FAIL,
});
}
}; };
// Logout
export const logout = () => ({ type: LOGOUT });
+49 -83
View File
@@ -1,32 +1,26 @@
import api from '../utils/api';
import { setAlert } from './alert';
import { import {
GET_POSTS, removeComment,
POST_ERROR, addCommentAction,
UPDATE_LIKES, updateLikes,
DELETE_POST, postError,
ADD_POST, deletePostAction,
GET_POST, getPostAction,
ADD_COMMENT, getPostsAction,
REMOVE_COMMENT addPostAction,
} from './types'; } from "../reducers/post";
import api from "../utils/api";
import { createAlert } from "./alert";
// Get posts // Get posts
export const getPosts = () => async (dispatch) => { export const getPosts = () => async (dispatch) => {
try { try {
const res = await api.get('/posts'); const res = await api.get("/posts");
dispatch({ dispatch(getPostsAction(res.data));
type: GET_POSTS,
payload: res.data
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
@@ -35,15 +29,11 @@ export const addLike = (id) => async (dispatch) => {
try { try {
const res = await api.put(`/posts/like/${id}`); const res = await api.put(`/posts/like/${id}`);
dispatch({ dispatch(updateLikes({ id, likes: res.data }));
type: UPDATE_LIKES,
payload: { id, likes: res.data }
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
@@ -52,15 +42,11 @@ export const removeLike = (id) => async (dispatch) => {
try { try {
const res = await api.put(`/posts/unlike/${id}`); const res = await api.put(`/posts/unlike/${id}`);
dispatch({ dispatch(updateLikes({ id, likes: res.data }));
type: UPDATE_LIKES,
payload: { id, likes: res.data }
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
@@ -69,36 +55,28 @@ export const deletePost = (id) => async (dispatch) => {
try { try {
await api.delete(`/posts/${id}`); await api.delete(`/posts/${id}`);
dispatch({ dispatch(deletePostAction(id));
type: DELETE_POST,
payload: id
});
dispatch(setAlert('Post Removed', 'success')); dispatch(createAlert("Post Removed", "success"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
// Add post // Add post
export const addPost = (formData) => async (dispatch) => { export const addPost = (formData) => async (dispatch) => {
try { try {
const res = await api.post('/posts', formData); const res = await api.post("/posts", formData);
dispatch({ dispatch(addPostAction(res.data));
type: ADD_POST,
payload: res.data
});
dispatch(setAlert('Post Created', 'success')); dispatch(createAlert("Post Created", "success"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
@@ -107,15 +85,11 @@ export const getPost = (id) => async (dispatch) => {
try { try {
const res = await api.get(`/posts/${id}`); const res = await api.get(`/posts/${id}`);
dispatch({ dispatch(getPostAction(res.data));
type: GET_POST,
payload: res.data
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
@@ -124,17 +98,13 @@ export const addComment = (postId, formData) => async (dispatch) => {
try { try {
const res = await api.post(`/posts/comment/${postId}`, formData); const res = await api.post(`/posts/comment/${postId}`, formData);
dispatch({ dispatch(addCommentAction(res.data));
type: ADD_COMMENT,
payload: res.data
});
dispatch(setAlert('Comment Added', 'success')); dispatch(createAlert("Comment Added", "success"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
@@ -143,16 +113,12 @@ export const deleteComment = (postId, commentId) => async (dispatch) => {
try { try {
await api.delete(`/posts/comment/${postId}/${commentId}`); await api.delete(`/posts/comment/${postId}/${commentId}`);
dispatch({ dispatch(removeComment(commentId));
type: REMOVE_COMMENT,
payload: commentId
});
dispatch(setAlert('Comment Removed', 'success')); dispatch(createAlert("Comment Removed", "success"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: POST_ERROR, postError({ msg: err.response.statusText, status: err.response.status })
payload: { msg: err.response.statusText, status: err.response.status } );
});
} }
}; };
+97 -120
View File
@@ -1,57 +1,48 @@
import api from '../utils/api'; import api from "../utils/api";
import { setAlert } from './alert'; import { createAlert } from "./alert";
import { import {
GET_PROFILE, noRepos,
GET_PROFILES, getRepos,
PROFILE_ERROR, clearProfile,
UPDATE_PROFILE, profileError,
CLEAR_PROFILE, getProfilesType,
ACCOUNT_DELETED, updateProfile,
GET_REPOS, getProfile,
NO_REPOS } from "../reducers/profile";
} from './types'; import { accountDeleted } from "../reducers/auth";
/*
NOTE: we don't need a config object for axios as the
default headers in axios are already Content-Type: application/json
also axios stringifies and parses JSON for you, so no need for
JSON.stringify or JSON.parse
*/
// Get current users profile // Get current users profile
export const getCurrentProfile = () => async (dispatch) => { export const getCurrentProfile = () => async (dispatch) => {
try { try {
const res = await api.get('/profile/me'); const res = await api.get("/profile/me");
dispatch({ dispatch(getProfile(res.data));
type: GET_PROFILE,
payload: res.data
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
// Get all profiles // Get all profiles
export const getProfiles = () => async (dispatch) => { export const getProfiles = () => async (dispatch) => {
dispatch({ type: CLEAR_PROFILE }); dispatch(clearProfile());
try { try {
const res = await api.get('/profile'); const res = await api.get("/profile");
dispatch({ dispatch(getProfilesType(res.data));
type: GET_PROFILES,
payload: res.data
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
@@ -60,15 +51,14 @@ export const getProfileById = (userId) => async (dispatch) => {
try { try {
const res = await api.get(`/profile/user/${userId}`); const res = await api.get(`/profile/user/${userId}`);
dispatch({ dispatch(getProfile(res.data));
type: GET_PROFILE,
payload: res.data
});
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
@@ -77,101 +67,88 @@ export const getGithubRepos = (username) => async (dispatch) => {
try { try {
const res = await api.get(`/profile/github/${username}`); const res = await api.get(`/profile/github/${username}`);
dispatch({ dispatch(getRepos(res.data));
type: GET_REPOS,
payload: res.data
});
} catch (err) { } catch (err) {
dispatch({ dispatch(noRepos());
type: NO_REPOS
});
} }
}; };
// Create or update profile // Create or update profile
export const createProfile = export const createProfile =
(formData, navigate, edit = false) => (formData, edit = false) =>
async (dispatch) => { async (dispatch) => {
try { try {
const res = await api.post('/profile', formData); const res = await api.post("/profile", formData);
dispatch({ dispatch(getProfile(res.data));
type: GET_PROFILE,
payload: res.data
});
dispatch( dispatch(
setAlert(edit ? 'Profile Updated' : 'Profile Created', 'success') createAlert(edit ? "Profile Updated" : "Profile Created", "success")
); );
if (!edit) {
navigate('/dashboard');
}
} catch (err) { } catch (err) {
const errors = err.response.data.errors; const errors = err.response.data.errors;
if (errors) { if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger'))); errors.forEach((error) => dispatch(createAlert(error.msg, "danger")));
} }
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
// Add Experience // Add Experience
export const addExperience = (formData, navigate) => async (dispatch) => { export const addExperience = (formData) => async (dispatch) => {
try { try {
const res = await api.put('/profile/experience', formData); const res = await api.put("/profile/experience", formData);
dispatch({ dispatch(updateProfile(res.data));
type: UPDATE_PROFILE,
payload: res.data
});
dispatch(setAlert('Experience Added', 'success')); dispatch(createAlert("Experience Added", "success"));
navigate('/dashboard');
} catch (err) { } catch (err) {
const errors = err.response.data.errors; const errors = err.response.data.errors;
if (errors) { if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger'))); errors.forEach((error) => dispatch(createAlert(error.msg, "danger")));
} }
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
// Add Education // Add Education
export const addEducation = (formData, navigate) => async (dispatch) => { export const addEducation = (formData) => async (dispatch) => {
try { try {
const res = await api.put('/profile/education', formData); const res = await api.put("/profile/education", formData);
dispatch({ dispatch(updateProfile(res.data));
type: UPDATE_PROFILE,
payload: res.data
});
dispatch(setAlert('Education Added', 'success')); dispatch(createAlert("Education Added", "success"));
navigate('/dashboard');
} catch (err) { } catch (err) {
const errors = err.response.data.errors; const errors = err.response.data.errors;
if (errors) { if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, 'danger'))); errors.forEach((error) => dispatch(createAlert(error.msg, "danger")));
} }
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
@@ -180,17 +157,16 @@ export const deleteExperience = (id) => async (dispatch) => {
try { try {
const res = await api.delete(`/profile/experience/${id}`); const res = await api.delete(`/profile/experience/${id}`);
dispatch({ dispatch(updateProfile(res.data));
type: UPDATE_PROFILE,
payload: res.data
});
dispatch(setAlert('Experience Removed', 'success')); dispatch(createAlert("Experience Removed", "success"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
@@ -199,35 +175,36 @@ export const deleteEducation = (id) => async (dispatch) => {
try { try {
const res = await api.delete(`/profile/education/${id}`); const res = await api.delete(`/profile/education/${id}`);
dispatch({ dispatch(updateProfile(res.data));
type: UPDATE_PROFILE,
payload: res.data
});
dispatch(setAlert('Education Removed', 'success')); dispatch(createAlert("Education Removed", "success"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
}; };
// Delete account & profile // Delete account & profile
export const deleteAccount = () => async (dispatch) => { export const deleteAccount = () => async (dispatch) => {
if (window.confirm('Are you sure? This can NOT be undone!')) { if (window.confirm("Are you sure? This can NOT be undone!")) {
try { try {
await api.delete('/profile'); await api.delete("/profile");
dispatch({ type: CLEAR_PROFILE }); dispatch(clearProfile());
dispatch({ type: ACCOUNT_DELETED }); dispatch(accountDeleted());
dispatch(setAlert('Your account has been permanently deleted')); dispatch(createAlert("Your account has been permanently deleted"));
} catch (err) { } catch (err) {
dispatch({ dispatch(
type: PROFILE_ERROR, profileError({
payload: { msg: err.response.statusText, status: err.response.status } msg: err.response.statusText,
}); status: err.response.status,
})
);
} }
} }
}; };
-25
View File
@@ -1,25 +0,0 @@
export const SET_ALERT = 'SET_ALERT';
export const REMOVE_ALERT = 'REMOVE_ALERT';
export const REGISTER_SUCCESS = 'REGISTER_SUCCESS';
export const REGISTER_FAIL = 'REGISTER_FAIL';
export const USER_LOADED = 'USER_LOADED';
export const AUTH_ERROR = 'AUTH_ERROR';
export const LOGIN_SUCCESS = 'LOGIN_SUCCESS';
export const LOGIN_FAIL = 'LOGIN_FAIL';
export const LOGOUT = 'LOGOUT';
export const GET_PROFILE = 'GET_PROFILE';
export const GET_PROFILES = 'GET_PROFILES';
export const GET_REPOS = 'GET_REPOS';
export const NO_REPOS = 'NO_REPOS';
export const UPDATE_PROFILE = 'UPDATE_PROFILE';
export const CLEAR_PROFILE = 'CLEAR_PROFILE';
export const PROFILE_ERROR = 'PROFILE_ERROR';
export const ACCOUNT_DELETED = 'ACCOUNT_DELETED';
export const GET_POSTS = 'GET_POSTS';
export const GET_POST = 'GET_POST';
export const POST_ERROR = 'POST_ERROR';
export const UPDATE_LIKES = 'UPDATE_LIKES';
export const DELETE_POST = 'DELETE_POST';
export const ADD_POST = 'ADD_POST';
export const ADD_COMMENT = 'ADD_COMMENT';
export const REMOVE_COMMENT = 'REMOVE_COMMENT';
View File
+12 -22
View File
@@ -1,23 +1,22 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { Link, Navigate } from 'react-router-dom'; import { Link, Navigate } from "react-router-dom";
import { connect } from 'react-redux'; import { useDispatch, useSelector } from "react-redux";
import PropTypes from 'prop-types'; import { login } from "../../actions/auth";
import { login } from '../../actions/auth';
const Login = ({ login, isAuthenticated }) => { const Login = () => {
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
email: '', email: "",
password: '' password: "",
}); });
const dispatch = useDispatch();
const { email, password } = formData; const { email, password } = formData;
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
const onChange = (e) => const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value }); setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = (e) => { const onSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
login(email, password); await dispatch(login(email, password));
}; };
if (isAuthenticated) { if (isAuthenticated) {
@@ -59,13 +58,4 @@ const Login = ({ login, isAuthenticated }) => {
); );
}; };
Login.propTypes = { export default Login;
login: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
};
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps, { login })(Login);
+9 -18
View File
@@ -1,18 +1,19 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { connect } from 'react-redux'; import { useDispatch,useSelector } from 'react-redux';
import { Link, Navigate } from 'react-router-dom'; import { Link, Navigate } from 'react-router-dom';
import { setAlert } from '../../actions/alert'; import { createAlert } from '../../actions/alert';
import { register } from '../../actions/auth'; import { register } from '../../actions/auth';
import PropTypes from 'prop-types';
const Register = ({ setAlert, register, isAuthenticated }) => {
const Register = () => {
const dispatch = useDispatch();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
name: '', name: '',
email: '', email: '',
password: '', password: '',
password2: '' password2: ''
}); });
const isAuthenticated = useSelector(state=>state.auth.isAuthenticated)
const { name, email, password, password2 } = formData; const { name, email, password, password2 } = formData;
const onChange = (e) => const onChange = (e) =>
@@ -21,9 +22,9 @@ const Register = ({ setAlert, register, isAuthenticated }) => {
const onSubmit = async (e) => { const onSubmit = async (e) => {
e.preventDefault(); e.preventDefault();
if (password !== password2) { if (password !== password2) {
setAlert('Passwords do not match', 'danger'); await dispatch(createAlert('Passwords do not match', 'danger'));
} else { } else {
register({ name, email, password }); await dispatch(register({ name, email, password }));
} }
}; };
@@ -87,14 +88,4 @@ const Register = ({ setAlert, register, isAuthenticated }) => {
); );
}; };
Register.propTypes = { export default Register;
setAlert: PropTypes.func.isRequired,
register: PropTypes.func.isRequired,
isAuthenticated: PropTypes.bool
};
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps, { setAlert, register })(Register);
+21 -34
View File
@@ -1,23 +1,21 @@
import React, { useEffect } from 'react'; import React, { useEffect } from "react";
import { Link } from 'react-router-dom'; import { Link } from "react-router-dom";
import PropTypes from 'prop-types'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import DashboardActions from "./DashboardActions";
import DashboardActions from './DashboardActions'; import Experience from "./Experience";
import Experience from './Experience'; import Education from "./Education";
import Education from './Education'; import { getCurrentProfile, deleteAccount } from "../../actions/profile";
import { getCurrentProfile, deleteAccount } from '../../actions/profile';
const Dashboard = () => {
const Dashboard = ({ const dispatch = useDispatch();
getCurrentProfile,
deleteAccount,
auth: { user },
profile: { profile }
}) => {
useEffect(() => { useEffect(() => {
getCurrentProfile(); function fetchData() {
}, [getCurrentProfile]); dispatch(getCurrentProfile());
}
fetchData();
}, [dispatch]);
const user = useSelector((state) => state.auth.user);
const profile = useSelector((state) => state.profile.profile);
return ( return (
<section className="container"> <section className="container">
<h1 className="large text-primary">Dashboard</h1> <h1 className="large text-primary">Dashboard</h1>
@@ -31,7 +29,10 @@ const Dashboard = ({
<Education education={profile.education} /> <Education education={profile.education} />
<div className="my-2"> <div className="my-2">
<button className="btn btn-danger" onClick={() => deleteAccount()}> <button
className="btn btn-danger"
onClick={async () => await dispatch(deleteAccount())}
>
<i className="fas fa-user" /> Delete My Account <i className="fas fa-user" /> Delete My Account
</button> </button>
</div> </div>
@@ -48,18 +49,4 @@ const Dashboard = ({
); );
}; };
Dashboard.propTypes = { export default Dashboard;
getCurrentProfile: PropTypes.func.isRequired,
deleteAccount: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired,
profile: PropTypes.object.isRequired,
};
const mapStateToProps = (state) => ({
auth: state.auth,
profile: state.profile,
});
export default connect(mapStateToProps, { getCurrentProfile, deleteAccount })(
Dashboard
);
+9 -14
View File
@@ -1,20 +1,20 @@
import React, { Fragment } from 'react'; import React, { Fragment } from "react";
import PropTypes from 'prop-types'; import {useDispatch } from "react-redux";
import { connect } from 'react-redux'; import { deleteEducation } from "../../actions/profile";
import { deleteEducation } from '../../actions/profile'; import formatDate from "../../utils/formatDate";
import formatDate from '../../utils/formatDate';
const Education = ({ education, deleteEducation }) => { const Education = ({education}) => {
const dispatch = useDispatch();
const educations = education.map((edu) => ( const educations = education.map((edu) => (
<tr key={edu._id}> <tr key={edu._id}>
<td>{edu.school}</td> <td>{edu.school}</td>
<td className="hide-sm">{edu.degree}</td> <td className="hide-sm">{edu.degree}</td>
<td> <td>
{formatDate(edu.from)} - {edu.to ? formatDate(edu.to) : 'Now'} {formatDate(edu.from)} - {edu.to ? formatDate(edu.to) : "Now"}
</td> </td>
<td> <td>
<button <button
onClick={() => deleteEducation(edu._id)} onClick={async () => await dispatch(deleteEducation(edu._id))}
className="btn btn-danger" className="btn btn-danger"
> >
Delete Delete
@@ -41,9 +41,4 @@ const Education = ({ education, deleteEducation }) => {
); );
}; };
Education.propTypes = { export default Education;
education: PropTypes.array.isRequired,
deleteEducation: PropTypes.func.isRequired
};
export default connect(null, { deleteEducation })(Education);
@@ -1,10 +1,10 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types'; import {useDispatch } from 'react-redux';
import { connect } from 'react-redux';
import { deleteExperience } from '../../actions/profile'; import { deleteExperience } from '../../actions/profile';
import formatDate from '../../utils/formatDate'; import formatDate from '../../utils/formatDate';
const Experience = ({ experience, deleteExperience }) => { const Experience = ({experience}) => {
const dispatch = useDispatch();
const experiences = experience.map((exp) => ( const experiences = experience.map((exp) => (
<tr key={exp._id}> <tr key={exp._id}>
<td>{exp.company}</td> <td>{exp.company}</td>
@@ -14,7 +14,7 @@ const Experience = ({ experience, deleteExperience }) => {
</td> </td>
<td> <td>
<button <button
onClick={() => deleteExperience(exp._id)} onClick={async () => dispatch(deleteExperience(exp._id))}
className="btn btn-danger" className="btn btn-danger"
> >
Delete Delete
@@ -41,9 +41,6 @@ const Experience = ({ experience, deleteExperience }) => {
); );
}; };
Experience.propTypes = {
experience: PropTypes.array.isRequired,
deleteExperience: PropTypes.func.isRequired
};
export default connect(null, { deleteExperience })(Experience);
export default Experience;
+14 -21
View File
@@ -1,23 +1,16 @@
import React from 'react'; import React from "react";
import PropTypes from 'prop-types'; import { useSelector } from "react-redux";
import { connect } from 'react-redux'; const Alert = () => {
const alerts = useSelector((state) => state.alert);
const Alert = ({ alerts }) => ( return (
<div className="alert-wrapper"> <div className="alert-wrapper">
{alerts.map((alert) => ( {alerts.map((alert) => (
<div key={alert.id} className={`alert alert-${alert.alertType}`}> <div key={alert.id} className={`alert alert-${alert.alertType}`}>
{alert.msg} {alert.msg}
</div> </div>
))} ))}
</div> </div>
); );
Alert.propTypes = {
alerts: PropTypes.array.isRequired
}; };
const mapStateToProps = (state) => ({ export default Alert;
alerts: state.alert
});
export default connect(mapStateToProps)(Alert);
+6 -15
View File
@@ -1,9 +1,9 @@
import React from 'react'; import React from "react";
import { Link, Navigate } from 'react-router-dom'; import { Link, Navigate } from "react-router-dom";
import { connect } from 'react-redux'; import { useSelector } from "react-redux";
import PropTypes from 'prop-types';
const Landing = ({ isAuthenticated }) => { const Landing = () => {
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
if (isAuthenticated) { if (isAuthenticated) {
return <Navigate to="/dashboard" />; return <Navigate to="/dashboard" />;
} }
@@ -31,13 +31,4 @@ const Landing = ({ isAuthenticated }) => {
); );
}; };
Landing.propTypes = { export default Landing;
isAuthenticated: PropTypes.bool
};
const mapStateToProps = (state) => ({
isAuthenticated: state.auth.isAuthenticated
});
export default connect(mapStateToProps)(Landing);
+10 -19
View File
@@ -1,10 +1,11 @@
import React, { Fragment } from 'react'; import React, { Fragment } from "react";
import { Link } from 'react-router-dom'; import { Link } from "react-router-dom";
import { connect } from 'react-redux'; import { useDispatch, useSelector } from "react-redux";
import PropTypes from 'prop-types'; import { logOut } from "../../reducers/auth";
import { logout } from '../../actions/auth';
const Navbar = ({ auth: { isAuthenticated }, logout }) => { const Navbar = () => {
const isAuthenticated = useSelector((state) => state.auth.isAuthenticated);
const dispatch = useDispatch();
const authLinks = ( const authLinks = (
<ul> <ul>
<li> <li>
@@ -15,12 +16,11 @@ const Navbar = ({ auth: { isAuthenticated }, logout }) => {
</li> </li>
<li> <li>
<Link to="/dashboard"> <Link to="/dashboard">
<i className="fas fa-user" />{' '} <i className="fas fa-user" /> <span className="hide-sm">Profile</span>
<span className="hide-sm">Profile</span>
</Link> </Link>
</li> </li>
<li> <li>
<a onClick={logout} href="#!"> <a onClick={()=>dispatch(logOut())} href="#!">
<i className="fas fa-sign-out-alt" />{' '} <i className="fas fa-sign-out-alt" />{' '}
<span className="hide-sm">Logout</span> <span className="hide-sm">Logout</span>
</a> </a>
@@ -54,13 +54,4 @@ const Navbar = ({ auth: { isAuthenticated }, logout }) => {
); );
}; };
Navbar.propTypes = { export default Navbar;
logout: PropTypes.func.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
auth: state.auth
});
export default connect(mapStateToProps, { logout })(Navbar);
+6 -12
View File
@@ -1,10 +1,10 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import PropTypes from 'prop-types'; import { useDispatch } from 'react-redux';
import { connect } from 'react-redux';
import { addComment } from '../../actions/post'; import { addComment } from '../../actions/post';
const CommentForm = ({ postId, addComment }) => { const CommentForm = ({ postId }) => {
const [text, setText] = useState(''); const [text, setText] = useState('');
const dispatch = useDispatch();
return ( return (
<div className='post-form'> <div className='post-form'>
@@ -13,9 +13,9 @@ const CommentForm = ({ postId, addComment }) => {
</div> </div>
<form <form
className='form my-1' className='form my-1'
onSubmit={e => { onSubmit={async(e) => {
e.preventDefault(); e.preventDefault();
addComment(postId, { text }); await dispatch(addComment(postId, { text }));
setText(''); setText('');
}} }}
> >
@@ -34,12 +34,6 @@ const CommentForm = ({ postId, addComment }) => {
); );
}; };
CommentForm.propTypes = {
addComment: PropTypes.func.isRequired
};
export default connect( export default CommentForm
null,
{ addComment }
)(CommentForm);
+31 -42
View File
@@ -1,49 +1,38 @@
import React from 'react'; import React from "react";
import { Link } from 'react-router-dom'; import { Link } from "react-router-dom";
import PropTypes from 'prop-types'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import formatDate from "../../utils/formatDate";
import formatDate from '../../utils/formatDate'; import { deleteComment } from "../../actions/post";
import { deleteComment } from '../../actions/post';
const CommentItem = ({ const CommentItem = ({
postId, postId,
comment: { _id, text, name, avatar, user, date }, comment: { _id, text, name, avatar, user, date },
auth, }) => {
deleteComment const dispatch = useDispatch();
}) => ( const auth = useSelector((state) => state.auth);
<div className="post bg-white p-1 my-1"> return (
<div> <div className="post bg-white p-1 my-1">
<Link to={`/profile/${user}`}> <div>
<img className="round-img" src={avatar} alt="" /> <Link to={`/profile/${user}`}>
<h4>{name}</h4> <img className="round-img" src={avatar} alt="" />
</Link> <h4>{name}</h4>
</Link>
</div>
<div>
<p className="my-1">{text}</p>
<p className="post-date">Posted on {formatDate(date)}</p>
{!auth.loading && user === auth.user._id && (
<button
onClick={async () => await dispatch(deleteComment(postId, _id))}
type="button"
className="btn btn-danger"
>
<i className="fas fa-times" />
</button>
)}
</div>
</div> </div>
<div> );
<p className="my-1">{text}</p>
<p className="post-date">Posted on {formatDate(date)}</p>
{!auth.loading && user === auth.user._id && (
<button
onClick={() => deleteComment(postId, _id)}
type="button"
className="btn btn-danger"
>
<i className="fas fa-times" />
</button>
)}
</div>
</div>
);
CommentItem.propTypes = {
postId: PropTypes.string.isRequired,
comment: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
deleteComment: PropTypes.func.isRequired
}; };
const mapStateToProps = (state) => ({ export default CommentItem;
auth: state.auth
});
export default connect(mapStateToProps, { deleteComment })(CommentItem);
+17 -23
View File
@@ -1,18 +1,22 @@
import React, { useEffect } from 'react'; import React, { useEffect } from "react";
import PropTypes from 'prop-types'; import { Link, useParams } from "react-router-dom";
import { Link, useParams } from 'react-router-dom'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import Spinner from "../layout/Spinner";
import Spinner from '../layout/Spinner'; import PostItem from "../posts/PostItem";
import PostItem from '../posts/PostItem'; import CommentForm from "../post/CommentForm";
import CommentForm from '../post/CommentForm'; import CommentItem from "../post/CommentItem";
import CommentItem from '../post/CommentItem'; import { getPost } from "../../actions/post";
import { getPost } from '../../actions/post';
const Post = ({ getPost, post: { post, loading } }) => { const Post = () => {
const dispatch = useDispatch();
const { post, loading } = useSelector((state) => state.post);
const { id } = useParams(); const { id } = useParams();
useEffect(() => { useEffect(() => {
getPost(id); async function fetchData() {
}, [getPost, id]); await dispatch(getPost(id));
}
fetchData();
}, [dispatch, id]);
return loading || post === null ? ( return loading || post === null ? (
<Spinner /> <Spinner />
@@ -32,14 +36,4 @@ const Post = ({ getPost, post: { post, loading } }) => {
); );
}; };
Post.propTypes = { export default Post;
getPost: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
post: state.post
});
export default connect(mapStateToProps, { getPost })(Post);
+6 -25
View File
@@ -1,14 +1,10 @@
import React, { useState } from "react"; import React, { useState } from "react";
import PropTypes from "prop-types"; import { useDispatch } from "react-redux";
import { connect } from "react-redux";
import { addPost } from "../../actions/post"; import { addPost } from "../../actions/post";
const PostForm = ({ addPost }) => { const PostForm = () => {
const [text, setText] = useState(""); const [text, setText] = useState("");
const [category, setCategory] = useState(""); const dispatch = useDispatch();
//const onChange = (e) =>
//setFormData({ ...formData, [e.target.name]: e.target.value });
return ( return (
<div className="post-form"> <div className="post-form">
<div className="bg-primary p"> <div className="bg-primary p">
@@ -16,9 +12,9 @@ const PostForm = ({ addPost }) => {
</div> </div>
<form <form
className="form my-1" className="form my-1"
onSubmit={(e) => { onSubmit={async (e) => {
e.preventDefault(); e.preventDefault();
addPost({ text, category }); await dispatch(addPost({ text }));
setText(""); setText("");
}} }}
> >
@@ -31,25 +27,10 @@ const PostForm = ({ addPost }) => {
onChange={(e) => setText(e.target.value)} onChange={(e) => setText(e.target.value)}
required required
/> />
<p className="lead">Choose a category:</p>
<select name="category" value={category} onChange={setCategory}>
<option>* Select Category</option>
<option value="opinion">Opinion</option>
<option value="question">Question</option>
<option value="asssitance">Asking for asssitance</option>
<option value="news">News</option>
<option value="other">Other</option>
</select>
<input type="submit" className="btn btn-dark my-1" value="Submit" /> <input type="submit" className="btn btn-dark my-1" value="Submit" />
</form> </form>
</div> </div>
); );
}; };
PostForm.propTypes = { export default PostForm;
addPost: PropTypes.func.isRequired,
};
export default connect(null, { addPost })(PostForm);
+49 -66
View File
@@ -1,74 +1,57 @@
import React, { Fragment } from 'react'; import React from "react";
import PropTypes from 'prop-types'; import { Link } from "react-router-dom";
import { Link } from 'react-router-dom'; import formatDate from "../../utils/formatDate";
import formatDate from '../../utils/formatDate'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import { addLike, removeLike, deletePost } from "../../actions/post";
import { addLike, removeLike, deletePost } from '../../actions/post';
const PostItem = ({ const PostItem = ({
addLike, post: { _id, text, name, avatar, user, likes, comments, date },
removeLike, }) => {
deletePost, const dispatch = useDispatch();
auth, const auth = useSelector((state) => state.auth);
post: { _id, text, name, avatar, user, likes, comments, date } return (
}) => ( <div className="post bg-white p-1 my-1">
<div className="post bg-white p-1 my-1"> <div>
<div> <Link to={`/profile/${user}`}>
<Link to={`/profile/${user}`}> <img className="round-img" src={avatar} alt="" />
<img className="round-img" src={avatar} alt="" /> <h4>{name}</h4>
<h4>{name}</h4> </Link>
</Link> </div>
</div> <div>
<div> <p className="my-1">{text}</p>
<p className="my-1">{text}</p> <p className="post-date">Posted on {formatDate(date)}</p>
<p className="post-date">Posted on {formatDate(date)}</p>
<button
onClick={() => addLike(_id)}
type="button"
className="btn btn-light"
>
<i className="fas fa-thumbs-up" />{' '}
<span>{likes.length > 0 && <span>{likes.length}</span>}</span>
</button>
<button
onClick={() => removeLike(_id)}
type="button"
className="btn btn-light"
>
<i className="fas fa-thumbs-down" />
</button>
<Link to={`/posts/${_id}`} className="btn btn-primary">
Discussion{' '}
{comments.length > 0 && (
<span className="comment-count">{comments.length}</span>
)}
</Link>
{!auth.loading && user === auth.user._id && (
<button <button
onClick={() => deletePost(_id)} onClick={async () => await dispatch(addLike(_id))}
type="button" type="button"
className="btn btn-danger" className="btn btn-light"
> >
<i className="fas fa-times" /> <i className="fas fa-thumbs-up" />{" "}
<span>{likes.length > 0 && <span>{likes.length}</span>}</span>
</button> </button>
)} <button
onClick={async () => await dispatch(removeLike(_id))}
type="button"
className="btn btn-light"
>
<i className="fas fa-thumbs-down" />
</button>
<Link to={`/posts/${_id}`} className="btn btn-primary">
Discussion{" "}
{comments.length > 0 && (
<span className="comment-count">{comments.length}</span>
)}
</Link>
{!auth.loading && user === auth.user._id && (
<button
onClick={async () => await dispatch(deletePost(_id))}
type="button"
className="btn btn-danger"
>
<i className="fas fa-times" />
</button>
)}
</div>
</div> </div>
</div> );
);
PostItem.propTypes = {
post: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired,
addLike: PropTypes.func.isRequired,
removeLike: PropTypes.func.isRequired,
deletePost: PropTypes.func.isRequired
}; };
export default PostItem;
const mapStateToProps = (state) => ({
auth: state.auth
});
export default connect(mapStateToProps, { addLike, removeLike, deletePost })(
PostItem
);
+15 -19
View File
@@ -1,14 +1,19 @@
import React, { useEffect } from 'react'; import React, { useEffect } from "react";
import PropTypes from 'prop-types'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import PostItem from "./PostItem";
import PostItem from './PostItem'; import PostForm from "./PostForm";
import PostForm from './PostForm'; import { getPosts } from "../../actions/post";
import { getPosts } from '../../actions/post';
const Posts = ({ getPosts, post: { posts } }) => { const Posts = () => {
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
getPosts(); async function fetchData() {
}, [getPosts]); await dispatch(getPosts());
}
fetchData();
}, [dispatch]);
const posts = useSelector((state) => state.post.posts);
return ( return (
<section className="container"> <section className="container">
@@ -26,13 +31,4 @@ const Posts = ({ getPosts, post: { posts } }) => {
); );
}; };
Posts.propTypes = { export default Posts;
getPosts: PropTypes.func.isRequired,
post: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
post: state.post
});
export default connect(mapStateToProps, { getPosts })(Posts);
@@ -1,19 +1,19 @@
import React, { useState } from 'react'; import React, { useState } from "react";
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from "react-router-dom";
import PropTypes from 'prop-types'; import { useDispatch } from "react-redux";
import { connect } from 'react-redux'; import { addEducation } from "../../actions/profile";
import { addEducation } from '../../actions/profile';
const AddEducation = ({ addEducation }) => { const AddEducation = () => {
const navigate = useNavigate(); const navigate = useNavigate();
const dispatch = useDispatch();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
school: '', school: "",
degree: '', degree: "",
fieldofstudy: '', fieldofstudy: "",
from: '', from: "",
to: '', to: "",
current: false, current: false,
description: '' description: "",
}); });
const { school, degree, fieldofstudy, from, to, description, current } = const { school, degree, fieldofstudy, from, to, description, current } =
@@ -32,9 +32,10 @@ const AddEducation = ({ addEducation }) => {
<small>* = required field</small> <small>* = required field</small>
<form <form
className="form" className="form"
onSubmit={(e) => { onSubmit={async (e) => {
e.preventDefault(); e.preventDefault();
addEducation(formData).then(() => navigate('/dashboard')); await dispatch(addEducation(formData)).then(() => navigate("/dashboard"));
// i have no idea how this works used to work, i removed the navigate function from the addEducation and it does now
}} }}
> >
<div className="form-group"> <div className="form-group">
@@ -78,7 +79,7 @@ const AddEducation = ({ addEducation }) => {
checked={current} checked={current}
value={current} value={current}
onChange={() => setFormData({ ...formData, current: !current })} onChange={() => setFormData({ ...formData, current: !current })}
/>{' '} />{" "}
Current School Current School
</p> </p>
</div> </div>
@@ -111,8 +112,5 @@ const AddEducation = ({ addEducation }) => {
); );
}; };
AddEducation.propTypes = {
addEducation: PropTypes.func.isRequired
};
export default connect(null, { addEducation })(AddEducation); export default AddEducation;
@@ -1,10 +1,10 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { Link, useNavigate } from 'react-router-dom'; import { Link, useNavigate } from 'react-router-dom';
import PropTypes from 'prop-types'; import { useDispatch } from 'react-redux';
import { connect } from 'react-redux';
import { addExperience } from '../../actions/profile'; import { addExperience } from '../../actions/profile';
const AddExperience = ({ addExperience }) => { const AddExperience = () => {
const dispatch = useDispatch();
const navigate = useNavigate(); const navigate = useNavigate();
const [formData, setFormData] = useState({ const [formData, setFormData] = useState({
company: '', company: '',
@@ -31,9 +31,9 @@ const AddExperience = ({ addExperience }) => {
<small>* = required field</small> <small>* = required field</small>
<form <form
className="form" className="form"
onSubmit={(e) => { onSubmit={async(e) => {
e.preventDefault(); e.preventDefault();
addExperience(formData).then(() => navigate('/dashboard')); await dispatch(addExperience(formData)).then(() => navigate('/dashboard'));
}} }}
> >
<div className="form-group"> <div className="form-group">
@@ -112,8 +112,4 @@ const AddExperience = ({ addExperience }) => {
); );
}; };
AddExperience.propTypes = { export default AddExperience;
addExperience: PropTypes.func.isRequired
};
export default connect(null, { addExperience })(AddExperience);
@@ -1,32 +1,32 @@
import React, { Fragment, useState, useEffect } from 'react'; import React, { Fragment, useState, useEffect } from "react";
import { Link, useMatch, useNavigate } from 'react-router-dom'; import { Link, useMatch, useNavigate } from "react-router-dom";
import PropTypes from 'prop-types'; import { createProfile, getCurrentProfile } from "../../actions/profile";
import { connect } from 'react-redux'; import { useDispatch, useSelector } from "react-redux";
import { createProfile, getCurrentProfile } from '../../actions/profile';
const initialState = { const initialState = {
company: '', company: "",
website: '', website: "",
location: '', location: "",
status: '', status: "",
skills: '', skills: "",
githubusername: '', githubusername: "",
bio: '', bio: "",
twitter: '', twitter: "",
facebook: '', facebook: "",
linkedin: '', linkedin: "",
youtube: '', youtube: "",
instagram: '' instagram: "",
}; };
const ProfileForm = ({ const ProfileForm = () => {
profile: { profile, loading },
createProfile, const dispatch = useDispatch();
getCurrentProfile
}) => { const { profile, loading } = useSelector((state) => state.profile);
const [formData, setFormData] = useState(initialState); const [formData, setFormData] = useState(initialState);
const creatingProfile = useMatch('/create-profile'); const creatingProfile = useMatch("/create-profile");
const [displaySocialInputs, toggleSocialInputs] = useState(false); const [displaySocialInputs, toggleSocialInputs] = useState(false);
@@ -34,7 +34,10 @@ const ProfileForm = ({
useEffect(() => { useEffect(() => {
// if there is no profile, attempt to fetch one // if there is no profile, attempt to fetch one
if (!profile) getCurrentProfile(); async function fetchData(){
await dispatch(getCurrentProfile())
}
if (!profile) fetchData();
// if we finished loading and we do have a profile // if we finished loading and we do have a profile
// then build our profileData // then build our profileData
@@ -48,11 +51,11 @@ const ProfileForm = ({
} }
// the skills may be an array from our API response // the skills may be an array from our API response
if (Array.isArray(profileData.skills)) if (Array.isArray(profileData.skills))
profileData.skills = profileData.skills.join(', '); profileData.skills = profileData.skills.join(", ");
// set local state with the profileData // set local state with the profileData
setFormData(profileData); setFormData(profileData);
} }
}, [loading, getCurrentProfile, profile]); }, [loading, dispatch, profile]);
const { const {
company, company,
@@ -66,30 +69,31 @@ const ProfileForm = ({
facebook, facebook,
linkedin, linkedin,
youtube, youtube,
instagram instagram,
} = formData; } = formData;
const onChange = (e) => const onChange = (e) =>
setFormData({ ...formData, [e.target.name]: e.target.value }); setFormData({ ...formData, [e.target.name]: e.target.value });
const onSubmit = (e) => { const onSubmit = async (e) => {
const editing = profile ? true : false; const editing = profile ? true : false;
e.preventDefault(); e.preventDefault();
createProfile(formData, editing).then(() => { await dispatch(createProfile(formData, editing)).then(() => {
if (!editing) navigate('/dashboard'); console.log(editing)
if (!editing) navigate("/dashboard");
}); });
}; };
return ( return (
<section className="container"> <section className="container">
<h1 className="large text-primary"> <h1 className="large text-primary">
{creatingProfile ? 'Create Your Profile' : 'Edit Your Profile'} {creatingProfile ? "Create Your Profile" : "Edit Your Profile"}
</h1> </h1>
<p className="lead"> <p className="lead">
<i className="fas fa-user" /> <i className="fas fa-user" />
{creatingProfile {creatingProfile
? ` Let's get some information to make your` ? ` Let's get some information to make your`
: ' Add some changes to your profile'} : " Add some changes to your profile"}
</p> </p>
<small>* = required field</small> <small>* = required field</small>
<form className="form" onSubmit={onSubmit}> <form className="form" onSubmit={onSubmit}>
@@ -259,17 +263,4 @@ const ProfileForm = ({
); );
}; };
ProfileForm.propTypes = { export default ProfileForm;
createProfile: PropTypes.func.isRequired,
getCurrentProfile: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
profile: state.profile
});
export default connect(mapStateToProps, { createProfile, getCurrentProfile })(
ProfileForm
);
+21 -25
View File
@@ -1,20 +1,26 @@
import React, { Fragment, useEffect } from 'react'; import React, { Fragment, useEffect } from "react";
import PropTypes from 'prop-types'; import { Link, useParams } from "react-router-dom";
import { Link, useParams } from 'react-router-dom'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import Spinner from "../layout/Spinner";
import Spinner from '../layout/Spinner'; import ProfileTop from "./ProfileTop";
import ProfileTop from './ProfileTop'; import ProfileAbout from "./ProfileAbout";
import ProfileAbout from './ProfileAbout'; import ProfileExperience from "./ProfileExperience";
import ProfileExperience from './ProfileExperience'; import ProfileEducation from "./ProfileEducation";
import ProfileEducation from './ProfileEducation'; import ProfileGithub from "./ProfileGithub";
import ProfileGithub from './ProfileGithub'; import { getProfileById } from "../../actions/profile";
import { getProfileById } from '../../actions/profile';
const Profile = ({ getProfileById, profile: { profile }, auth }) => { const Profile = () => {
const profile = useSelector((state) => state.profile.profile);
const auth = useSelector((state) => state.auth);
const dispatch = useDispatch();
const { id } = useParams(); const { id } = useParams();
useEffect(() => { useEffect(() => {
getProfileById(id); async function fetchData(){
}, [getProfileById, id]); await dispatch(getProfileById(id));
}
fetchData();
}, [dispatch,id]);
return ( return (
<section className="container"> <section className="container">
@@ -76,15 +82,5 @@ const Profile = ({ getProfileById, profile: { profile }, auth }) => {
</section> </section>
); );
}; };
Profile.propTypes = {
getProfileById: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired,
auth: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({ export default Profile;
profile: state.profile,
auth: state.auth
});
export default connect(mapStateToProps, { getProfileById })(Profile);
@@ -1,5 +1,4 @@
import React, { Fragment } from 'react'; import React, { Fragment } from 'react';
import PropTypes from 'prop-types';
const ProfileAbout = ({ const ProfileAbout = ({
profile: { profile: {
@@ -27,8 +26,5 @@ const ProfileAbout = ({
</div> </div>
); );
ProfileAbout.propTypes = {
profile: PropTypes.object.isRequired
};
export default ProfileAbout; export default ProfileAbout;
@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import formatDate from '../../utils/formatDate'; import formatDate from '../../utils/formatDate';
const ProfileEducation = ({ const ProfileEducation = ({
@@ -22,9 +21,6 @@ const ProfileEducation = ({
</div> </div>
); );
ProfileEducation.propTypes = {
education: PropTypes.object.isRequired
};
export default ProfileEducation; export default ProfileEducation;
@@ -1,5 +1,4 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types';
import formatDate from '../../utils/formatDate'; import formatDate from '../../utils/formatDate';
const ProfileExperience = ({ const ProfileExperience = ({
@@ -22,9 +21,5 @@ const ProfileExperience = ({
</div> </div>
); );
ProfileExperience.propTypes = {
experience: PropTypes.object.isRequired
};
export default ProfileExperience; export default ProfileExperience;
+13 -20
View File
@@ -1,17 +1,21 @@
import React, { useEffect } from 'react'; import React, { useEffect } from "react";
import PropTypes from 'prop-types'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import { getGithubRepos } from "../../actions/profile";
import { getGithubRepos } from '../../actions/profile';
const ProfileGithub = ({ username, getGithubRepos, repos }) => { const ProfileGithub = ({ username }) => {
const repos = useSelector((state) => state.profile.repos);
const dispatch = useDispatch();
useEffect(() => { useEffect(() => {
getGithubRepos(username); async function fetchData() {
}, [getGithubRepos, username]); await dispatch(getGithubRepos(username));
}
fetchData();
}, [dispatch, username]);
return ( return (
<div className="profile-github"> <div className="profile-github">
<h2 className="text-primary my-1">Github Repos</h2> <h2 className="text-primary my-1">Github Repos</h2>
{repos.map(repo => ( {repos.map((repo) => (
<div key={repo.id} className="repo bg-white p-1 my-1"> <div key={repo.id} className="repo bg-white p-1 my-1">
<div> <div>
<h4> <h4>
@@ -38,15 +42,4 @@ const ProfileGithub = ({ username, getGithubRepos, repos }) => {
); );
}; };
ProfileGithub.propTypes = { export default ProfileGithub;
getGithubRepos: PropTypes.func.isRequired,
repos: PropTypes.array.isRequired,
username: PropTypes.string.isRequired
};
const mapStateToProps = state => ({
repos: state.profile.repos
});
export default connect(mapStateToProps, { getGithubRepos })(ProfileGithub);
+3 -9
View File
@@ -1,5 +1,4 @@
import React from 'react'; import React from "react";
import PropTypes from 'prop-types';
const ProfileTop = ({ const ProfileTop = ({
profile: { profile: {
@@ -8,8 +7,8 @@ const ProfileTop = ({
location, location,
website, website,
social, social,
user: { name, avatar } user: { name, avatar },
} },
}) => { }) => {
return ( return (
<div className="profile-top bg-primary p-2"> <div className="profile-top bg-primary p-2">
@@ -44,9 +43,4 @@ const ProfileTop = ({
); );
}; };
ProfileTop.propTypes = {
profile: PropTypes.object.isRequired
};
export default ProfileTop; export default ProfileTop;
@@ -1,6 +1,5 @@
import React from 'react'; import React from 'react';
import { Link } from 'react-router-dom'; import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
const ProfileItem = ({ const ProfileItem = ({
profile: { profile: {
@@ -35,9 +34,5 @@ const ProfileItem = ({
); );
}; };
ProfileItem.propTypes = {
profile: PropTypes.object.isRequired
};
export default ProfileItem; export default ProfileItem;
+15 -19
View File
@@ -1,15 +1,20 @@
import React, { Fragment, useEffect } from 'react'; import React, { Fragment, useEffect } from "react";
import PropTypes from 'prop-types'; import { useDispatch, useSelector } from "react-redux";
import { connect } from 'react-redux'; import Spinner from "../layout/Spinner";
import Spinner from '../layout/Spinner'; import ProfileItem from "./ProfileItem";
import ProfileItem from './ProfileItem'; import { getProfiles } from "../../actions/profile";
import { getProfiles } from '../../actions/profile';
const Profiles = () => {
const dispatch = useDispatch();
const Profiles = ({ getProfiles, profile: { profiles, loading } }) => {
useEffect(() => { useEffect(() => {
getProfiles(); async function fetchData() {
}, [getProfiles]); await dispatch(getProfiles());
}
fetchData();
}, [dispatch]);
const { profiles, loading } = useSelector((state) => state.profile);
return ( return (
<section className="container"> <section className="container">
{loading ? ( {loading ? (
@@ -36,13 +41,4 @@ const Profiles = ({ getProfiles, profile: { profiles, loading } }) => {
); );
}; };
Profiles.propTypes = { export default Profiles;
getProfiles: PropTypes.func.isRequired,
profile: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
profile: state.profile
});
export default connect(mapStateToProps, { getProfiles })(Profiles);
+7 -19
View File
@@ -1,26 +1,14 @@
import React from 'react'; import React from "react";
import { Navigate } from 'react-router-dom'; import { Navigate } from "react-router-dom";
import PropTypes from 'prop-types'; import { useSelector } from "react-redux";
import { connect } from 'react-redux'; import Spinner from "../layout/Spinner";
import Spinner from '../layout/Spinner';
const PrivateRoute = ({ const PrivateRoute = ({ component: Component }) => {
component: Component, const { isAuthenticated, loading } = useSelector((state) => state.auth);
auth: { isAuthenticated, loading }
}) => {
if (loading) return <Spinner />; if (loading) return <Spinner />;
if (isAuthenticated) return <Component />; if (isAuthenticated) return <Component />;
return <Navigate to="/login" />; return <Navigate to="/login" />;
}; };
PrivateRoute.propTypes = { export default PrivateRoute;
auth: PropTypes.object.isRequired
};
const mapStateToProps = (state) => ({
auth: state.auth
});
export default connect(mapStateToProps)(PrivateRoute);
+4 -3
View File
@@ -1,8 +1,9 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import {createRoot} from 'react-dom/client'
import App from './App'; import App from './App';
// Level - 0 // Level - 0
//Program stars from Here. Imports and renders App. //Program stars from Here. Imports and renders App.
ReactDOM.render(<App />, document.getElementById('root')); const root = createRoot(document.getElementById('root'));
root.render(<App/>)
+15 -14
View File
@@ -1,19 +1,20 @@
import { SET_ALERT, REMOVE_ALERT } from '../actions/types'; import { createSlice } from "@reduxjs/toolkit";
const initialState = []; const initialState = [];
function alertReducer(state = initialState, action) { const alertSlice = createSlice({
const { type, payload } = action; name: "alert",
initialState,
reducers: {
setAlert(state, action) {
return [...state, action.payload];
},
removeAlert(state, action) {
return state.filter((alert) => alert.id !== action.payload);
},
},
});
switch (type) { export const { setAlert, removeAlert } = alertSlice.actions;
case SET_ALERT:
return [...state, payload];
case REMOVE_ALERT:
return state.filter((alert) => alert.id !== payload);
default:
return state;
}
}
export default alertSlice.reducer;
export default alertReducer;
+54 -33
View File
@@ -1,55 +1,76 @@
import { import { createSlice } from "@reduxjs/toolkit";
REGISTER_SUCCESS,
//REGISTER_FAIL,
USER_LOADED,
AUTH_ERROR,
LOGIN_SUCCESS,
//LOGIN_FAIL,
LOGOUT,
ACCOUNT_DELETED
} from '../actions/types';
const initialState = { const initialState = {
token: localStorage.getItem('token'), token: localStorage.getItem("token"),
isAuthenticated: null, isAuthenticated: null,
loading: true, loading: true,
user: null user: null,
}; };
function authReducer(state = initialState, action) { const authSlice = createSlice({
const { type, payload } = action; name: "auth",
initialState,
switch (type) { reducers: {
case USER_LOADED: userLoaded(state, action) {
return { return {
...state, ...state,
isAuthenticated: true, isAuthenticated: true,
loading: false, loading: false,
user: payload user: action.payload,
}; };
case REGISTER_SUCCESS: },
case LOGIN_SUCCESS: registerSuccess(state, action) {
return { return {
...state, ...state,
...payload, ...action.payload,
isAuthenticated: true, isAuthenticated: true,
loading: false loading: false,
}; };
case ACCOUNT_DELETED: },
case AUTH_ERROR: loginSucces(state, action) {
case LOGOUT: return {
...state,
...action.payload,
isAuthenticated: true,
loading: false,
};
},
accountDeleted(state, action) {
return { return {
...state, ...state,
token: null, token: null,
isAuthenticated: false, isAuthenticated: false,
loading: false, loading: false,
user: null user: null,
}; };
default: },
return state; authError(state, action) {
} return {
} ...state,
token: null,
isAuthenticated: false,
export default authReducer; loading: false,
user: null,
};
},
logOut(state, action) {
return {
...state,
token: null,
isAuthenticated: false,
loading: false,
user: null,
};
},
},
});
export const {
logOut,
userLoaded,
loginSucces,
registerSuccess,
accountDeleted,
authError,
} = authSlice.actions;
export default authSlice.reducer;
-13
View File
@@ -1,13 +0,0 @@
import { combineReducers } from 'redux';
import alert from './alert';
import auth from './auth';
import profile from './profile';
import post from './post';
export default combineReducers({
alert,
auth,
profile,
post
});
+54 -46
View File
@@ -1,84 +1,92 @@
import { import { createSlice } from "@reduxjs/toolkit";
GET_POSTS,
POST_ERROR,
UPDATE_LIKES,
DELETE_POST,
ADD_POST,
GET_POST,
ADD_COMMENT,
REMOVE_COMMENT
} from '../actions/types';
const initialState = { const initialState = {
posts: [], posts: [],
post: null, post: null,
loading: true, loading: true,
error: {} error: {},
}; };
function postReducer(state = initialState, action) { const postSlice = createSlice({
const { type, payload } = action; name: "post",
initialState,
switch (type) { reducers: {
case GET_POSTS: getPostsAction(state, action) {
return { return {
...state, ...state,
posts: payload, posts: action.payload,
loading: false loading: false,
}; };
case GET_POST: },
getPostAction(state, action) {
return { return {
...state, ...state,
post: payload, post: action.payload,
loading: false loading: false,
}; };
case ADD_POST: },
addPostAction(state, action) {
return { return {
...state, ...state,
posts: [payload, ...state.posts], posts: [action.payload, ...state.posts],
loading: false loading: false,
}; };
case DELETE_POST: },
deletePostAction(state, action) {
return { return {
...state, ...state,
posts: state.posts.filter((post) => post._id !== payload), posts: state.posts.filter((post) => post._id !== action.payload),
loading: false loading: false,
}; };
case POST_ERROR: },
postError(state, action) {
return { return {
...state, ...state,
error: payload, error: action.payload,
loading: false loading: false,
}; };
case UPDATE_LIKES: },
updateLikes(state, action) {
return { return {
...state, ...state,
posts: state.posts.map((post) => posts: state.posts.map((post) =>
post._id === payload.id ? { ...post, likes: payload.likes } : post post._id === action.payload.id
? { ...post, likes: action.payload.likes }
: post
), ),
loading: false loading: false,
}; };
case ADD_COMMENT: },
addCommentAction(state, action) {
return { return {
...state, ...state,
post: { ...state.post, comments: payload }, post: { ...state.post, comments: action.payload },
loading: false loading: false,
}; };
case REMOVE_COMMENT: },
removeComment(state, action) {
return { return {
...state, ...state,
post: { post: {
...state.post, ...state.post,
comments: state.post.comments.filter( comments: state.post.comments.filter(
(comment) => comment._id !== payload (comment) => comment._id !== action.payload
) ),
}, },
loading: false loading: false,
}; };
default: },
return state; },
} });
} export const {
removeComment,
export default postReducer; addCommentAction,
updateLikes,
postError,
deletePostAction,
getPostAction,
getPostsAction,
addPostAction,
} = postSlice.actions;
export default postSlice.reducer;
+53 -43
View File
@@ -1,65 +1,75 @@
import { import { createSlice } from "@reduxjs/toolkit";
GET_PROFILE,
PROFILE_ERROR,
CLEAR_PROFILE,
UPDATE_PROFILE,
GET_PROFILES,
GET_REPOS,
NO_REPOS
} from '../actions/types';
const initialState = { const initialState = {
profile: null, profile: null,
profiles: [], profiles: [],
repos: [], repos: [],
loading: true, loading: true,
error: {} error: {},
}; };
const profileSlice = createSlice({
function profileReducer(state = initialState, action) { name: "profile",
const { type, payload } = action; initialState,
reducers: {
switch (type) { getProfile(state, action) {
case GET_PROFILE:
case UPDATE_PROFILE:
return { return {
...state, ...state,
profile: payload, profile: action.payload,
loading: false
};
case GET_PROFILES:
return {
...state,
profiles: payload,
loading: false
};
case PROFILE_ERROR:
return {
...state,
error: payload,
loading: false, loading: false,
profile: null
}; };
case CLEAR_PROFILE: },
updateProfile(state, action) {
return {
...state,
profile: action.payload,
loading: false,
};
},
getProfilesType(state, action) {
return {
...state,
profiles: action.payload,
loading: false,
};
},
profileError(state, action) {
return {
...state,
error: action.payload,
loading: false,
profile: null,
};
},
clearProfile(state, action) {
return { return {
...state, ...state,
profile: null, profile: null,
repos: [] repos: [],
}; };
case GET_REPOS: },
getRepos(state, action) {
return { return {
...state, ...state,
repos: payload, repos: action.payload,
loading: false loading: false,
}; };
case NO_REPOS: },
noRepos(state, action) {
return { return {
...state, ...state,
repos: [] repos: [],
}; };
default: },
return state; },
} });
} export const {
noRepos,
getRepos,
clearProfile,
profileError,
getProfilesType,
updateProfile,
getProfile,
} = profileSlice.actions;
export default profileReducer; export default profileSlice.reducer;
+12 -20
View File
@@ -1,28 +1,20 @@
import { createStore, applyMiddleware } from 'redux';
import { composeWithDevTools } from 'redux-devtools-extension';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import setAuthToken from './utils/setAuthToken'; import setAuthToken from './utils/setAuthToken';
import { configureStore } from '@reduxjs/toolkit';
const initialState = {}; import alertReducer from './reducers/alert';
import authReducer from './reducers/auth';
import profileReducer from './reducers/profile'
import postReducer from './reducers/post'
const middleware = [thunk];
const store = createStore( const store = configureStore({
rootReducer, reducer: {
initialState, alert: alertReducer,
composeWithDevTools(applyMiddleware(...middleware)) auth: authReducer,
); profile: profileReducer,
post: postReducer
}})
/*
NOTE: set up a store subscription listener
to store the users token in localStorage
*/
/*
initialize current state from redux store for subscription comparison
preventing undefined error
*/
let currentState = store.getState(); let currentState = store.getState();
store.subscribe(() => { store.subscribe(() => {
+1 -2
View File
@@ -1,6 +1,5 @@
import axios from 'axios'; import axios from 'axios';
import store from '../store'; import store from '../store';
import { LOGOUT } from '../actions/types';
const api = axios.create({ const api = axios.create({
baseURL: '/api', baseURL: '/api',
@@ -12,7 +11,7 @@ api.interceptors.response.use(
(res) => res, (res) => res,
(err) => { (err) => {
if (err.response.status === 401) { if (err.response.status === 401) {
store.dispatch({ type: LOGOUT }); store.dispatch({ type: 'auth/logOut' });
} }
return Promise.reject(err); return Promise.reject(err);
} }
+29276 -1158
View File
File diff suppressed because it is too large Load Diff
+10 -11
View File
@@ -12,20 +12,19 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"dependencies": { "dependencies": {
"axios": "^1.1.3", "axios": "^0.21.0",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"config": "^3.3.8", "client": "file:client",
"express": "^4.18.2", "config": "^3.3.3",
"express-validator": "^6.14.2", "express": "^4.17.1",
"gravatar": "^1.8.2", "express-validator": "^6.8.1",
"gravatar": "^1.8.1",
"jsonwebtoken": "^8.5.1", "jsonwebtoken": "^8.5.1",
"mongoose": "^6.6.5", "mongoose": "^5.11.8",
"normalize-url": "^5.0.0", "normalize-url": "^5.3.0"
"request": "^2.88.2",
"uuid": "^9.0.0"
}, },
"devDependencies": { "devDependencies": {
"concurrently": "^7.4.0", "concurrently": "^5.3.0",
"nodemon": "^2.0.20" "nodemon": "^2.0.6"
} }
} }