diff --git a/.gitignore b/.gitignore index 7a6bbdc..662c3e1 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,4 @@ node_modules App.css start_in_tmux.sh default.json +production.json diff --git a/client/.gitignore b/client/.gitignore deleted file mode 100644 index 4d29575..0000000 --- a/client/.gitignore +++ /dev/null @@ -1,23 +0,0 @@ -# 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* diff --git a/client/package-lock.json b/client/package-lock.json index 70796fd..bdbd150 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -13,7 +13,7 @@ "@mui/icons-material": "^6.1.1", "@mui/material": "^6.1.1", "@reduxjs/toolkit": "^1.9.5", - "@testing-library/jest-dom": "^5.16.5", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "@types/jest": "^27.5.2", @@ -142,7 +142,7 @@ "@babel/helper-validator-option": "^7.24.8", "browserslist": "^4.23.1", "lru-cache": "^5.1.1", - "semver": "^6.3.0" + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -175,6 +175,9 @@ }, "engines": { "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { @@ -1656,10 +1659,10 @@ "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", "@types/aria-query": "^5.0.1", - "aria-query": "^5.0.0", + "aria-query": "5.1.3", "chalk": "^4.1.0", "dom-accessibility-api": "^0.5.9", - "lz-string": "^1.4.4", + "lz-string": "^1.5.0", "pretty-format": "^27.0.2" }, "engines": { @@ -1809,7 +1812,7 @@ "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.3.0" + "@babel/types": "^7.20.7" } }, "node_modules/@types/estree": { @@ -1835,8 +1838,8 @@ "integrity": "sha512-mpT8LJJ4CMeeahobofYWIjFo0xonRS/HfxnVEPMPFSQdGUt1uHCnoPT7Zhb+sjDU2wz0oKV0OLUR0WzrHNgfeA==", "license": "MIT", "dependencies": { - "expect": "^29.0.0", - "pretty-format": "^29.0.0" + "jest-matcher-utils": "^27.0.0", + "pretty-format": "^27.0.0" } }, "node_modules/@types/parse-json": { @@ -2027,6 +2030,10 @@ { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } ], "license": "MIT", @@ -3527,7 +3534,7 @@ "react": "^16.8 || ^17.0 || ^18.0", "react-dom": "^16.8 || ^17.0 || ^18.0", "react-native": ">=0.59", - "redux": "^4" + "redux": "^4 || ^5.0.0-beta.0" }, "peerDependenciesMeta": { "@types/react": { diff --git a/client/src/App.js b/client/src/App.js deleted file mode 100644 index aef9957..0000000 --- a/client/src/App.js +++ /dev/null @@ -1,99 +0,0 @@ -import React, { useEffect } from 'react'; -import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; - -import Navbar from './components/layout/Navbar'; -import Landing from './components/layout/Landing'; -import Register from './components/auth/Register'; -import Login from './components/auth/Login'; -import Alert from './components/layout/Alert'; -import Dashboard from './components/dashboard/Dashboard'; -import ProfileForm from './components/profile-forms/ProfileForm'; -import AddExperience from './components/profile-forms/AddExperience'; -import AddEducation from './components/profile-forms/AddEducation'; -import Profiles from './components/profiles/Profiles'; -import Profile from './components/profile/Profile'; -import Posts from './components/posts/Posts'; -import Post from './components/post/Post'; -import NotFound from './components/layout/NotFound'; -import PrivateRoute from './components/routing/PrivateRoute'; - -// Redux -import { Provider } from 'react-redux'; -import store from './store'; -import { loadUser } from './actions/auth'; -import setAuthToken from './utils/setAuthToken'; - -import './App.css'; -import { logOut } from './reducers/auth'; - -// Level - 1 -// - -const App = () => { - useEffect(() => { - // check for token in LS when app first runs - if (localStorage.token) { - // if there is a token set axios headers for all requests - setAuthToken(localStorage.token); - } - // try to fetch a user, if no token or invalid token we - // will get a 401 response from our API - store.dispatch(loadUser()); - - // log user out from all tabs if they log out in one tab - window.addEventListener("storage", () => { - if (!localStorage.token) store.dispatch(logOut); - }); - }, []); - - return ( - - - - - - } /> - } /> - } /> - } /> - } /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> - } /> - } /> - - - - ); - /* - return ( - - - - } /> - - - - );*/ -}; - -export default App; diff --git a/client/src/actions/alert.js b/client/src/actions/alert.js deleted file mode 100644 index a409018..0000000 --- a/client/src/actions/alert.js +++ /dev/null @@ -1,9 +0,0 @@ -import { v4 as uuidv4 } from 'uuid'; -import {removeAlert, setAlert } from '../reducers/alert'; - -export const createAlert = (msg, alertType, timeout = 5000) => dispatch => { - const id = uuidv4(); - dispatch(setAlert({ msg, alertType, id })); - - setTimeout(() => dispatch(removeAlert(id)), timeout); -}; diff --git a/client/src/actions/auth.js b/client/src/actions/auth.js deleted file mode 100644 index 6f9cfc5..0000000 --- a/client/src/actions/auth.js +++ /dev/null @@ -1,68 +0,0 @@ -import api from "../utils/api"; -import { createAlert } from "./alert"; -import { - loginSucces, - authError, - registerSuccess, - logOut, - userLoaded, -} 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 -*/ - -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) => { - try { - const res = await api.get("/auth"); - - dispatch(userLoaded(res.data)); - } catch (err) { - dispatch(authError()); - } -}; - -// Register User -export const register = (formData) => async (dispatch) => { - try { - const res = await api.post("/users", formData); - - dispatch(registerSuccess(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/registerFail", - }); - } -}; - -export const logout = () => async (dispatch) => { - dispatch(logOut); -}; diff --git a/client/src/actions/post.js b/client/src/actions/post.js deleted file mode 100644 index 23d6666..0000000 --- a/client/src/actions/post.js +++ /dev/null @@ -1,124 +0,0 @@ -import { - removeComment, - addCommentAction, - updateLikes, - postError, - deletePostAction, - getPostAction, - getPostsAction, - addPostAction, -} from "../reducers/post"; -import api from "../utils/api"; -import { createAlert } from "./alert"; - -// Get posts -export const getPosts = () => async (dispatch) => { - try { - const res = await api.get("/posts"); - - dispatch(getPostsAction(res.data)); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Add like -export const addLike = (id) => async (dispatch) => { - try { - const res = await api.put(`/posts/like/${id}`); - - dispatch(updateLikes({ id, likes: res.data })); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Remove like -export const removeLike = (id) => async (dispatch) => { - try { - const res = await api.put(`/posts/unlike/${id}`); - - dispatch(updateLikes({ id, likes: res.data })); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Delete post -export const deletePost = (id) => async (dispatch) => { - try { - await api.delete(`/posts/${id}`); - - dispatch(deletePostAction(id)); - - dispatch(createAlert("Post Removed", "success")); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Add post -export const addPost = (formData) => async (dispatch) => { - try { - const res = await api.post("/posts", formData); - - dispatch(addPostAction(res.data)); - - dispatch(createAlert("Post Created", "success")); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Get post -export const getPost = (id) => async (dispatch) => { - try { - const res = await api.get(`/posts/${id}`); - - dispatch(getPostAction(res.data)); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Add comment -export const addComment = (postId, formData) => async (dispatch) => { - try { - const res = await api.post(`/posts/comment/${postId}`, formData); - - dispatch(addCommentAction(res.data)); - - dispatch(createAlert("Comment Added", "success")); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; - -// Delete comment -export const deleteComment = (postId, commentId) => async (dispatch) => { - try { - await api.delete(`/posts/comment/${postId}/${commentId}`); - - dispatch(removeComment(commentId)); - - dispatch(createAlert("Comment Removed", "success")); - } catch (err) { - dispatch( - postError({ msg: err.response.statusText, status: err.response.status }) - ); - } -}; diff --git a/client/src/actions/profile.js b/client/src/actions/profile.js deleted file mode 100644 index 1fca564..0000000 --- a/client/src/actions/profile.js +++ /dev/null @@ -1,210 +0,0 @@ -import api from "../utils/api"; -import { createAlert } from "./alert"; -import { - noRepos, - getRepos, - clearProfile, - profileError, - getProfilesType, - updateProfile, - getProfile, -} from "../reducers/profile"; -import { accountDeleted } from "../reducers/auth"; - - -// Get current users profile -export const getCurrentProfile = () => async (dispatch) => { - try { - const res = await api.get("/profile/me"); - - dispatch(getProfile(res.data)); - } catch (err) { - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Get all profiles -export const getProfiles = () => async (dispatch) => { - dispatch(clearProfile()); - - try { - const res = await api.get("/profile"); - - dispatch(getProfilesType(res.data)); - } catch (err) { - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Get profile by ID -export const getProfileById = (userId) => async (dispatch) => { - try { - const res = await api.get(`/profile/user/${userId}`); - - dispatch(getProfile(res.data)); - } catch (err) { - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Get Github repos -export const getGithubRepos = (username) => async (dispatch) => { - try { - const res = await api.get(`/profile/github/${username}`); - - dispatch(getRepos(res.data)); - } catch (err) { - dispatch(noRepos()); - } -}; - -// Create or update profile -export const createProfile = - (formData, edit = false) => - async (dispatch) => { - try { - const res = await api.post("/profile", formData); - - dispatch(getProfile(res.data)); - - dispatch( - createAlert(edit ? "Profile Updated" : "Profile Created", "success") - ); - - } catch (err) { - const errors = err.response.data.errors; - - if (errors) { - errors.forEach((error) => dispatch(createAlert(error.msg, "danger"))); - } - - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } - }; - -// Add Experience -export const addExperience = (formData) => async (dispatch) => { - try { - const res = await api.put("/profile/experience", formData); - - dispatch(updateProfile(res.data)); - - dispatch(createAlert("Experience Added", "success")); - - } catch (err) { - const errors = err.response.data.errors; - - if (errors) { - errors.forEach((error) => dispatch(createAlert(error.msg, "danger"))); - } - - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Add Education -export const addEducation = (formData) => async (dispatch) => { - try { - const res = await api.put("/profile/education", formData); - - dispatch(updateProfile(res.data)); - - dispatch(createAlert("Education Added", "success")); - - } catch (err) { - const errors = err.response.data.errors; - - if (errors) { - errors.forEach((error) => dispatch(createAlert(error.msg, "danger"))); - } - - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Delete experience -export const deleteExperience = (id) => async (dispatch) => { - try { - const res = await api.delete(`/profile/experience/${id}`); - - dispatch(updateProfile(res.data)); - - dispatch(createAlert("Experience Removed", "success")); - } catch (err) { - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Delete education -export const deleteEducation = (id) => async (dispatch) => { - try { - const res = await api.delete(`/profile/education/${id}`); - - dispatch(updateProfile(res.data)); - - dispatch(createAlert("Education Removed", "success")); - } catch (err) { - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } -}; - -// Delete account & profile -export const deleteAccount = () => async (dispatch) => { - if (window.confirm("Are you sure? This can NOT be undone!")) { - try { - await api.delete("/profile"); - - dispatch(clearProfile()); - dispatch(accountDeleted()); - - dispatch(createAlert("Your account has been permanently deleted")); - } catch (err) { - dispatch( - profileError({ - msg: err.response.statusText, - status: err.response.status, - }) - ); - } - } -}; diff --git a/client/src/components/auth/Login.js b/client/src/components/auth/Login.js deleted file mode 100644 index b1963f6..0000000 --- a/client/src/components/auth/Login.js +++ /dev/null @@ -1,61 +0,0 @@ -import React, { useState } from "react"; -import { Link, Navigate } from "react-router-dom"; -import { useDispatch, useSelector } from "react-redux"; -import { login } from "../../actions/auth"; - -const Login = () => { - const [formData, setFormData] = useState({ - email: "", - password: "", - }); - const dispatch = useDispatch(); - const { email, password } = formData; - const isAuthenticated = useSelector((state) => state.auth.isAuthenticated); - const onChange = (e) => - setFormData({ ...formData, [e.target.name]: e.target.value }); - - const onSubmit = async (e) => { - e.preventDefault(); - await dispatch(login(email, password)); - }; - - if (isAuthenticated) { - return ; - } - - return ( -
-

Sign In

-

- Sign Into Your Account -

-
-
- -
-
- -
- -
-

- Don't have an account? Sign Up -

-
- ); -}; - -export default Login; diff --git a/client/src/components/auth/Register.js b/client/src/components/auth/Register.js deleted file mode 100644 index 79749e2..0000000 --- a/client/src/components/auth/Register.js +++ /dev/null @@ -1,91 +0,0 @@ -import React, { useState } from 'react'; -import { useDispatch,useSelector } from 'react-redux'; -import { Link, Navigate } from 'react-router-dom'; -import { createAlert } from '../../actions/alert'; -import { register } from '../../actions/auth'; - - -const Register = () => { - const dispatch = useDispatch(); - const [formData, setFormData] = useState({ - name: '', - email: '', - password: '', - password2: '' - }); - const isAuthenticated = useSelector(state=>state.auth.isAuthenticated) - const { name, email, password, password2 } = formData; - - const onChange = (e) => - setFormData({ ...formData, [e.target.name]: e.target.value }); - - const onSubmit = async (e) => { - e.preventDefault(); - if (password !== password2) { - await dispatch(createAlert('Passwords do not match', 'danger')); - } else { - await dispatch(register({ name, email, password })); - } - }; - - if (isAuthenticated) { - return ; - } - - return ( -
-

Sign Up

-

- Create Your Account -

-
-
- -
-
- - - This site uses Gravatar so if you want a profile image, use a - Gravatar email - -
-
- -
-
- -
- -
-

- Already have an account? Sign In -

-
- ); -}; - -export default Register; diff --git a/client/src/components/dashboard/Dashboard.js b/client/src/components/dashboard/Dashboard.js deleted file mode 100644 index 677c54a..0000000 --- a/client/src/components/dashboard/Dashboard.js +++ /dev/null @@ -1,52 +0,0 @@ -import React, { useEffect } from "react"; -import { Link } from "react-router-dom"; -import { useDispatch, useSelector } from "react-redux"; -import DashboardActions from "./DashboardActions"; -import Experience from "./Experience"; -import Education from "./Education"; -import { getCurrentProfile, deleteAccount } from "../../actions/profile"; - -const Dashboard = () => { - const dispatch = useDispatch(); - useEffect(() => { - function fetchData() { - dispatch(getCurrentProfile()); - } - fetchData(); - }, [dispatch]); - const user = useSelector((state) => state.auth.user); - const profile = useSelector((state) => state.profile.profile); - return ( -
-

Dashboard

-

- Welcome {user && user.name} -

- {profile !== null ? ( - <> - - - - -
- -
- - ) : ( - <> -

You have not yet setup a profile, please add some info

- - Create Profile - - - )} -
- ); -}; - -export default Dashboard; diff --git a/client/src/components/dashboard/DashboardActions.js b/client/src/components/dashboard/DashboardActions.js deleted file mode 100644 index 3a63e5d..0000000 --- a/client/src/components/dashboard/DashboardActions.js +++ /dev/null @@ -1,20 +0,0 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; - -const DashboardActions = () => { - return ( -
- - Edit Profile - - - Add Experience - - - Add Education - -
- ); -}; - -export default DashboardActions; diff --git a/client/src/components/dashboard/Education.js b/client/src/components/dashboard/Education.js deleted file mode 100644 index 2c79532..0000000 --- a/client/src/components/dashboard/Education.js +++ /dev/null @@ -1,44 +0,0 @@ -import React, { Fragment } from "react"; -import {useDispatch } from "react-redux"; -import { deleteEducation } from "../../actions/profile"; -import formatDate from "../../utils/formatDate"; - -const Education = ({education}) => { - const dispatch = useDispatch(); - const educations = education.map((edu) => ( - - {edu.school} - {edu.degree} - - {formatDate(edu.from)} - {edu.to ? formatDate(edu.to) : "Now"} - - - - - - )); - - return ( - -

Education Credentials

- - - - - - - - - {educations} -
SchoolDegreeYears -
-
- ); -}; - -export default Education; diff --git a/client/src/components/dashboard/Experience.js b/client/src/components/dashboard/Experience.js deleted file mode 100644 index 5257f26..0000000 --- a/client/src/components/dashboard/Experience.js +++ /dev/null @@ -1,46 +0,0 @@ -import React, { Fragment } from 'react'; -import {useDispatch } from 'react-redux'; -import { deleteExperience } from '../../actions/profile'; -import formatDate from '../../utils/formatDate'; - -const Experience = ({experience}) => { - const dispatch = useDispatch(); - const experiences = experience.map((exp) => ( - - {exp.company} - {exp.title} - - {formatDate(exp.from)} - {exp.to ? formatDate(exp.to) : 'Now'} - - - - - - )); - - return ( - -

Experience Credentials

- - - - - - - - - {experiences} -
CompanyTitleYears -
-
- ); -}; - - - -export default Experience; diff --git a/client/src/components/layout/Alert.js b/client/src/components/layout/Alert.js deleted file mode 100644 index 7767998..0000000 --- a/client/src/components/layout/Alert.js +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; -import { useSelector } from "react-redux"; -const Alert = () => { - const alerts = useSelector((state) => state.alert); - return ( -
- {alerts.map((alert) => ( -
- {alert.msg} -
- ))} -
- ); -}; - -export default Alert; diff --git a/client/src/components/layout/Landing.js b/client/src/components/layout/Landing.js deleted file mode 100644 index 2434df5..0000000 --- a/client/src/components/layout/Landing.js +++ /dev/null @@ -1,34 +0,0 @@ -import React from "react"; -import { Link, Navigate } from "react-router-dom"; -import { useSelector } from "react-redux"; - -const Landing = () => { - const isAuthenticated = useSelector((state) => state.auth.isAuthenticated); - if (isAuthenticated) { - return ; - } - - return ( -
-
-
-

Developer Connector

-

- Create a developer profile/portfolio, share posts and get help from - other developers -

-
- - Sign Up - - - Login - -
-
-
-
- ); -}; - -export default Landing; diff --git a/client/src/components/layout/Navbar.js b/client/src/components/layout/Navbar.js deleted file mode 100644 index 0adbbbd..0000000 --- a/client/src/components/layout/Navbar.js +++ /dev/null @@ -1,57 +0,0 @@ -import React, { Fragment } from "react"; -import { Link } from "react-router-dom"; -import { useDispatch, useSelector } from "react-redux"; -import { logOut } from "../../reducers/auth"; - -const Navbar = () => { - const isAuthenticated = useSelector((state) => state.auth.isAuthenticated); - const dispatch = useDispatch(); - const authLinks = ( - - ); - - const guestLinks = ( -
    -
  • - Developers -
  • -
  • - Register -
  • -
  • - Login -
  • -
- ); - - return ( - - ); -}; - -export default Navbar; diff --git a/client/src/components/layout/NotFound.js b/client/src/components/layout/NotFound.js deleted file mode 100644 index 7a243da..0000000 --- a/client/src/components/layout/NotFound.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from 'react'; - -const NotFound = () => { - return ( -
-

- Page Not Found -

-

Sorry, this page does not exist

-
- ); -}; - -export default NotFound; diff --git a/client/src/components/layout/Spinner.js b/client/src/components/layout/Spinner.js deleted file mode 100644 index d4255c3..0000000 --- a/client/src/components/layout/Spinner.js +++ /dev/null @@ -1,14 +0,0 @@ -import React, { Fragment } from 'react'; -import spinner from './spinner.gif'; - -const Spinner = () => ( - - Loading... - -); - -export default Spinner; diff --git a/client/src/components/post/CommentForm.js b/client/src/components/post/CommentForm.js deleted file mode 100644 index 1fcf2d4..0000000 --- a/client/src/components/post/CommentForm.js +++ /dev/null @@ -1,39 +0,0 @@ -import React, { useState } from 'react'; -import { useDispatch } from 'react-redux'; -import { addComment } from '../../actions/post'; - -const CommentForm = ({ postId }) => { - const [text, setText] = useState(''); - const dispatch = useDispatch(); - - return ( -
-
-

Leave a Comment

-
-
{ - e.preventDefault(); - await dispatch(addComment(postId, { text })); - setText(''); - }} - > -