Login Logout Navbar ProfileForm moved to MUI
This commit is contained in:
Generated
+2071
-4047
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,10 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/react": "^11.11.1",
|
||||||
|
"@emotion/styled": "^11.11.0",
|
||||||
|
"@mui/icons-material": "^5.14.3",
|
||||||
|
"@mui/material": "^5.14.4",
|
||||||
"@reduxjs/toolkit": "^1.9.5",
|
"@reduxjs/toolkit": "^1.9.5",
|
||||||
"@testing-library/jest-dom": "^5.17.0",
|
"@testing-library/jest-dom": "^5.17.0",
|
||||||
"@testing-library/react": "^13.4.0",
|
"@testing-library/react": "^13.4.0",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.8 KiB |
@@ -1,49 +1,14 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
|
||||||
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<meta name="theme-color" content="#000000" />
|
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://use.fontawesome.com/releases/v5.8.1/css/all.css"
|
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"
|
||||||
integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf"
|
|
||||||
crossorigin="anonymous"
|
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta charset="utf-8" />
|
||||||
name="description"
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
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" />
|
|
||||||
<!--
|
|
||||||
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>
|
|
||||||
<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.
|
Before Width: | Height: | Size: 5.2 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 9.4 KiB |
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"short_name": "React App",
|
|
||||||
"name": "Create React App Sample",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "favicon.ico",
|
|
||||||
"sizes": "64x64 32x32 24x24 16x16",
|
|
||||||
"type": "image/x-icon"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo192.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "192x192"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "logo512.png",
|
|
||||||
"type": "image/png",
|
|
||||||
"sizes": "512x512"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"start_url": ".",
|
|
||||||
"display": "standalone",
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#ffffff"
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# https://www.robotstxt.org/robotstxt.html
|
|
||||||
User-agent: *
|
|
||||||
Disallow:
|
|
||||||
+16
-18
@@ -1,4 +1,3 @@
|
|||||||
|
|
||||||
/* Global Styles */
|
/* Global Styles */
|
||||||
:root {
|
:root {
|
||||||
--primary-color: #17a2b8;
|
--primary-color: #17a2b8;
|
||||||
@@ -15,7 +14,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
body {
|
body {
|
||||||
font-family: 'Raleway', sans-serif;
|
font-family: "Raleway", sans-serif;
|
||||||
font-size: 1rem;
|
font-size: 1rem;
|
||||||
line-height: 1.6;
|
line-height: 1.6;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
@@ -246,10 +245,10 @@ img {
|
|||||||
color: #888;
|
color: #888;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form input[type='text'],
|
.form input[type="text"],
|
||||||
.form input[type='email'],
|
.form input[type="email"],
|
||||||
.form input[type='password'],
|
.form input[type="password"],
|
||||||
.form input[type='date'],
|
.form input[type="date"],
|
||||||
.form select,
|
.form select,
|
||||||
.form textarea {
|
.form textarea {
|
||||||
display: block;
|
display: block;
|
||||||
@@ -259,7 +258,7 @@ img {
|
|||||||
border: 1px solid #ccc;
|
border: 1px solid #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
.form input[type='submit'],
|
.form input[type="submit"],
|
||||||
button {
|
button {
|
||||||
font: inherit;
|
font: inherit;
|
||||||
}
|
}
|
||||||
@@ -334,7 +333,7 @@ button {
|
|||||||
/* Landing Page */
|
/* Landing Page */
|
||||||
.landing {
|
.landing {
|
||||||
position: relative;
|
position: relative;
|
||||||
background: url('./img/vimCheatSheet.jpg') no-repeat center center/cover;
|
background: url("./img/vimCheatSheet.jpg") no-repeat center center/cover;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -365,10 +364,10 @@ button {
|
|||||||
.profile-grid {
|
.profile-grid {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'top top'
|
"top top"
|
||||||
'about about'
|
"about about"
|
||||||
'exp edu'
|
"exp edu"
|
||||||
'github github';
|
"github github";
|
||||||
grid-gap: 1rem;
|
grid-gap: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -551,11 +550,11 @@ button {
|
|||||||
|
|
||||||
.profile-grid {
|
.profile-grid {
|
||||||
grid-template-areas:
|
grid-template-areas:
|
||||||
'top'
|
"top"
|
||||||
'about'
|
"about"
|
||||||
'exp'
|
"exp"
|
||||||
'edu'
|
"edu"
|
||||||
'github';
|
"github";
|
||||||
}
|
}
|
||||||
|
|
||||||
.profile-about .skills {
|
.profile-about .skills {
|
||||||
@@ -583,4 +582,3 @@ button {
|
|||||||
right: 2rem;
|
right: 2rem;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+22
-23
@@ -1,30 +1,29 @@
|
|||||||
import React, { useEffect } from 'react';
|
import React, { useEffect } from "react";
|
||||||
import { BrowserRouter as Router, Route, Routes } from 'react-router-dom';
|
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
|
||||||
|
|
||||||
import Navbar from './components/layout/Navbar';
|
import Navbar from "./components/layout/Navbar";
|
||||||
import Landing from './components/layout/Landing';
|
import Landing from "./components/layout/Landing";
|
||||||
import Register from './components/auth/Register';
|
import Register from "./components/auth/Register";
|
||||||
import Login from './components/auth/Login';
|
import Login from "./components/auth/Login";
|
||||||
import Alert from './components/layout/Alert';
|
import Alert from "./components/layout/Alert";
|
||||||
import Dashboard from './components/dashboard/Dashboard';
|
import Dashboard from "./components/dashboard/Dashboard";
|
||||||
import ProfileForm from './components/profile-forms/ProfileForm';
|
import ProfileForm from "./components/profile-forms/ProfileForm";
|
||||||
import AddExperience from './components/profile-forms/AddExperience';
|
import AddExperience from "./components/profile-forms/AddExperience";
|
||||||
import AddEducation from './components/profile-forms/AddEducation';
|
import AddEducation from "./components/profile-forms/AddEducation";
|
||||||
import Profiles from './components/profiles/Profiles';
|
import Profiles from "./components/profiles/Profiles";
|
||||||
import Profile from './components/profile/Profile';
|
import Profile from "./components/profile/Profile";
|
||||||
import Posts from './components/posts/Posts';
|
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";
|
||||||
|
|
||||||
// Redux
|
// Redux
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from "react-redux";
|
||||||
import store from './store';
|
import store from "./store";
|
||||||
import { loadUser } from './actions/auth';
|
import { loadUser } from "./actions/auth";
|
||||||
import setAuthToken from './utils/setAuthToken';
|
import setAuthToken from "./utils/setAuthToken";
|
||||||
|
|
||||||
import './App.css';
|
import { logOut } from "./reducers/auth";
|
||||||
import { logOut } from './reducers/auth';
|
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
a {
|
||||||
|
text-decoration: none;
|
||||||
|
color: darkorange;
|
||||||
|
}
|
||||||
|
body {
|
||||||
|
background-color: whitesmoke;
|
||||||
|
}
|
||||||
@@ -1,8 +1,10 @@
|
|||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import { removeAlert, setAlert } from '../reducers/alert';
|
import { removeAlert, setAlert } from "../reducers/alert";
|
||||||
import { AppThunk } from '../types';
|
import { AppThunk } from "../types";
|
||||||
|
|
||||||
export const createAlert = (msg: string, alertType: string, timeout = 5000): AppThunk => dispatch => {
|
export const createAlert =
|
||||||
|
(msg: string, alertType: string, timeout = 5000): AppThunk =>
|
||||||
|
(dispatch) => {
|
||||||
const id = uuidv4();
|
const id = uuidv4();
|
||||||
dispatch(setAlert({ msg, alertType, id }));
|
dispatch(setAlert({ msg, alertType, id }));
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ import {
|
|||||||
import { AppThunk } from "../types";
|
import { AppThunk } from "../types";
|
||||||
import { AxiosError, isAxiosError } from "axios";
|
import { AxiosError, isAxiosError } from "axios";
|
||||||
|
|
||||||
export const login = (email: string, password: string): AppThunk => async (dispatch) => {
|
export const login =
|
||||||
|
(email: string, password: string): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
const body = { email, password };
|
const body = { email, password };
|
||||||
try {
|
try {
|
||||||
const res = await api.post("/auth", body);
|
const res = await api.post("/auth", body);
|
||||||
@@ -23,7 +25,9 @@ export const login = (email: string, password: string): AppThunk => async (dispa
|
|||||||
if (isAxiosError(err) && err.response !== undefined) {
|
if (isAxiosError(err) && err.response !== undefined) {
|
||||||
const errors = err.response.data.errors;
|
const errors = err.response.data.errors;
|
||||||
if (errors) {
|
if (errors) {
|
||||||
errors.forEach((error: any) => dispatch(createAlert(error.msg, "danger")));
|
errors.forEach((error: any) =>
|
||||||
|
dispatch(createAlert(error.msg, "danger")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "auth/loginFail",
|
type: "auth/loginFail",
|
||||||
@@ -33,7 +37,7 @@ export const login = (email: string, password: string): AppThunk => async (dispa
|
|||||||
//normal err
|
//normal err
|
||||||
}
|
}
|
||||||
// not error
|
// not error
|
||||||
}
|
};
|
||||||
|
|
||||||
export const loadUser = (): AppThunk => async (dispatch) => {
|
export const loadUser = (): AppThunk => async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
@@ -46,7 +50,9 @@ export const loadUser = (): AppThunk => async (dispatch) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Register User
|
// Register User
|
||||||
export const register = (formData: { name: string, email: string, password: string }): AppThunk => async (dispatch) => {
|
export const register =
|
||||||
|
(formData: { name: string; email: string; password: string }): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.post("/users", formData);
|
const res = await api.post("/users", formData);
|
||||||
|
|
||||||
@@ -54,20 +60,26 @@ export const register = (formData: { name: string, email: string, password: stri
|
|||||||
dispatch(loadUser());
|
dispatch(loadUser());
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
const errors = err.response.data.errors;
|
const errors = err.response.data.errors;
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
errors.forEach((error: any) => dispatch(createAlert(error.msg, "danger")));
|
errors.forEach((error: any) =>
|
||||||
|
dispatch(createAlert(error.msg, "danger")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: "auth/registerFail",
|
type: "auth/registerFail",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export const logout = (): AppThunk => async (dispatch) => {
|
export const logout = (): AppThunk => async (dispatch) => {
|
||||||
dispatch(logOut);
|
dispatch(logOut);
|
||||||
|
|||||||
+106
-38
@@ -21,9 +21,16 @@ export const getPosts = (): AppThunk => async (dispatch) => {
|
|||||||
dispatch(getPostsAction(res.data));
|
dispatch(getPostsAction(res.data));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -31,42 +38,61 @@ export const getPosts = (): AppThunk => async (dispatch) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add like
|
// Add like
|
||||||
export const addLike = (id: string): AppThunk => async (dispatch) => {
|
export const addLike =
|
||||||
|
(id: string): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.put(`/posts/like/${id}`);
|
const res = await api.put(`/posts/like/${id}`);
|
||||||
|
|
||||||
dispatch(updateLikes({ id, likes: res.data }));
|
dispatch(updateLikes({ id, likes: res.data }));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove like
|
// Remove like
|
||||||
export const removeLike = (id: string): AppThunk => async (dispatch) => {
|
export const removeLike =
|
||||||
|
(id: string): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.put(`/posts/unlike/${id}`);
|
const res = await api.put(`/posts/unlike/${id}`);
|
||||||
|
|
||||||
dispatch(updateLikes({ id, likes: res.data }));
|
dispatch(updateLikes({ id, likes: res.data }));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Delete post
|
// Delete post
|
||||||
export const deletePost = (id: string): AppThunk => async (dispatch) => {
|
export const deletePost =
|
||||||
|
(id: string): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
await api.delete(`/posts/${id}`);
|
await api.delete(`/posts/${id}`);
|
||||||
|
|
||||||
@@ -75,17 +101,25 @@ export const deletePost = (id: string): AppThunk => async (dispatch) => {
|
|||||||
dispatch(createAlert("Post Removed", "success"));
|
dispatch(createAlert("Post Removed", "success"));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add post
|
// Add post
|
||||||
export const addPost = (formData: { text: string }): AppThunk => async (dispatch) => {
|
export const addPost =
|
||||||
|
(formData: { text: string }): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.post("/posts", formData);
|
const res = await api.post("/posts", formData);
|
||||||
|
|
||||||
@@ -94,33 +128,51 @@ export const addPost = (formData: { text: string }): AppThunk => async (dispatch
|
|||||||
dispatch(createAlert("Post Created", "success"));
|
dispatch(createAlert("Post Created", "success"));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
|
||||||
// Get post
|
// Get post
|
||||||
export const getPost = (id: string): AppThunk => async (dispatch) => {
|
export const getPost =
|
||||||
|
(id: string): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.get(`/posts/${id}`);
|
const res = await api.get(`/posts/${id}`);
|
||||||
|
|
||||||
dispatch(getPostAction(res.data));
|
dispatch(getPostAction(res.data));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
// Add comment
|
// Add comment
|
||||||
export const addComment = (postId: string, formData: { text: string }): AppThunk => async (dispatch) => {
|
export const addComment =
|
||||||
|
(postId: string, formData: { text: string }): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.post(`/posts/comment/${postId}`, formData);
|
const res = await api.post(`/posts/comment/${postId}`, formData);
|
||||||
|
|
||||||
@@ -129,16 +181,25 @@ export const addComment = (postId: string, formData: { text: string }): AppThunk
|
|||||||
dispatch(createAlert("Comment Added", "success"));
|
dispatch(createAlert("Comment Added", "success"));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
// Delete comment
|
// Delete comment
|
||||||
export const deleteComment = (postId: string, commentId: string): AppThunk => async (dispatch) => {
|
export const deleteComment =
|
||||||
|
(postId: string, commentId: string): AppThunk =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
await api.delete(`/posts/comment/${postId}/${commentId}`);
|
await api.delete(`/posts/comment/${postId}/${commentId}`);
|
||||||
|
|
||||||
@@ -147,11 +208,18 @@ export const deleteComment = (postId: string, commentId: string): AppThunk => as
|
|||||||
dispatch(createAlert("Comment Removed", "success"));
|
dispatch(createAlert("Comment Removed", "success"));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
dispatch(
|
dispatch(
|
||||||
postError({ msg: err.response.statusText, status: err.response.status })
|
postError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -15,22 +15,28 @@ import { AppThunk, EducationType, ExperienceType } from "../types";
|
|||||||
|
|
||||||
const errorHandle = (dispatch: any, err: unknown) => {
|
const errorHandle = (dispatch: any, err: unknown) => {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (err !== undefined && "response" in err && err.response !== undefined) {
|
||||||
dispatch(profileError({ msg: err.response.statusText, status: err.response.status, }), err)
|
dispatch(
|
||||||
}
|
profileError({
|
||||||
|
msg: err.response.statusText,
|
||||||
|
status: err.response.status,
|
||||||
|
}),
|
||||||
|
err,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// Get current users profile
|
// Get current users profile
|
||||||
export const getCurrentProfile = (): AppThunk<Promise<void>> => async (dispatch) => {
|
export const getCurrentProfile =
|
||||||
|
(): AppThunk<Promise<void>> => async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.get("/profile/me");
|
const res = await api.get("/profile/me");
|
||||||
|
|
||||||
dispatch(getProfile(res.data));
|
dispatch(getProfile(res.data));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
errorHandle(dispatch, err)
|
errorHandle(dispatch, err);
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
};
|
||||||
// Get all profiles
|
// Get all profiles
|
||||||
export const getProfiles = (): AppThunk<Promise<void>> => async (dispatch) => {
|
export const getProfiles = (): AppThunk<Promise<void>> => async (dispatch) => {
|
||||||
dispatch(clearProfile());
|
dispatch(clearProfile());
|
||||||
@@ -39,23 +45,27 @@ export const getProfiles = (): AppThunk<Promise<void>> => async (dispatch) => {
|
|||||||
|
|
||||||
dispatch(getProfilesType(res.data));
|
dispatch(getProfilesType(res.data));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
errorHandle(dispatch, err)
|
errorHandle(dispatch, err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get profile by ID
|
// Get profile by ID
|
||||||
export const getProfileById = (userId: string): AppThunk<Promise<void>> => async (dispatch) => {
|
export const getProfileById =
|
||||||
|
(userId: string): AppThunk<Promise<void>> =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.get(`/profile/user/${userId}`);
|
const res = await api.get(`/profile/user/${userId}`);
|
||||||
|
|
||||||
dispatch(getProfile(res.data));
|
dispatch(getProfile(res.data));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
errorHandle(dispatch, err)
|
errorHandle(dispatch, err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get Github repos
|
// Get Github repos
|
||||||
export const getGithubRepos = (username: string): AppThunk<Promise<void>> => async (dispatch) => {
|
export const getGithubRepos =
|
||||||
|
(username: string): AppThunk<Promise<void>> =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.get(`/profile/github/${username}`);
|
const res = await api.get(`/profile/github/${username}`);
|
||||||
|
|
||||||
@@ -67,19 +77,19 @@ export const getGithubRepos = (username: string): AppThunk<Promise<void>> => asy
|
|||||||
|
|
||||||
// Create or update profile
|
// Create or update profile
|
||||||
type FormDataType = {
|
type FormDataType = {
|
||||||
company: string,
|
company: string;
|
||||||
website: string,
|
website: string;
|
||||||
location: string,
|
location: string;
|
||||||
status: string,
|
status: string;
|
||||||
skills: string,
|
skills: string;
|
||||||
githubusername: string,
|
githubusername: string;
|
||||||
bio: string,
|
bio: string;
|
||||||
twitter: string,
|
twitter: string;
|
||||||
facebook: string,
|
facebook: string;
|
||||||
linkedin: string,
|
linkedin: string;
|
||||||
youtube: string,
|
youtube: string;
|
||||||
instagram: string,
|
instagram: string;
|
||||||
}
|
};
|
||||||
export const createProfile =
|
export const createProfile =
|
||||||
(formData: FormDataType, edit = false): AppThunk<Promise<void>> =>
|
(formData: FormDataType, edit = false): AppThunk<Promise<void>> =>
|
||||||
async (dispatch) => {
|
async (dispatch) => {
|
||||||
@@ -89,21 +99,26 @@ export const createProfile =
|
|||||||
dispatch(getProfile(res.data));
|
dispatch(getProfile(res.data));
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
createAlert(edit ? "Profile Updated" : "Profile Created", "success")
|
createAlert(edit ? "Profile Updated" : "Profile Created", "success"),
|
||||||
);
|
);
|
||||||
|
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
const errors = err.response.data.errors;
|
const errors = err.response.data.errors;
|
||||||
if (errors) {
|
if (errors) {
|
||||||
errors.forEach((error: any) => dispatch(createAlert(error.msg, "danger")));
|
errors.forEach((error: any) =>
|
||||||
|
dispatch(createAlert(error.msg, "danger")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
dispatch(
|
dispatch(
|
||||||
profileError({
|
profileError({
|
||||||
msg: err.response.statusText,
|
msg: err.response.statusText,
|
||||||
status: err.response.status,
|
status: err.response.status,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -111,28 +126,35 @@ export const createProfile =
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add Experience
|
// Add Experience
|
||||||
export const addExperience = (formData: Omit<ExperienceType, '_id'>): AppThunk<Promise<void>> => async (dispatch) => {
|
export const addExperience =
|
||||||
|
(formData: Omit<ExperienceType, "_id">): AppThunk<Promise<void>> =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.put("/profile/experience", formData);
|
const res = await api.put("/profile/experience", formData);
|
||||||
|
|
||||||
dispatch(updateProfile(res.data));
|
dispatch(updateProfile(res.data));
|
||||||
|
|
||||||
dispatch(createAlert("Experience Added", "success"));
|
dispatch(createAlert("Experience Added", "success"));
|
||||||
|
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
const errors = err.response.data.errors;
|
const errors = err.response.data.errors;
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
errors.forEach((error: any) => dispatch(createAlert(error.msg, "danger")));
|
errors.forEach((error: any) =>
|
||||||
|
dispatch(createAlert(error.msg, "danger")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
profileError({
|
profileError({
|
||||||
msg: err.response.statusText,
|
msg: err.response.statusText,
|
||||||
status: err.response.status,
|
status: err.response.status,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -140,28 +162,35 @@ export const addExperience = (formData: Omit<ExperienceType, '_id'>): AppThunk<P
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Add Education
|
// Add Education
|
||||||
export const addEducation = (formData: Omit<EducationType, '_id'>): AppThunk<Promise<void>> => async (dispatch) => {
|
export const addEducation =
|
||||||
|
(formData: Omit<EducationType, "_id">): AppThunk<Promise<void>> =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.put("/profile/education", formData);
|
const res = await api.put("/profile/education", formData);
|
||||||
|
|
||||||
dispatch(updateProfile(res.data));
|
dispatch(updateProfile(res.data));
|
||||||
|
|
||||||
dispatch(createAlert("Education Added", "success"));
|
dispatch(createAlert("Education Added", "success"));
|
||||||
|
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
if (err instanceof AxiosError) {
|
if (err instanceof AxiosError) {
|
||||||
if (err !== undefined && 'response' in err && err.response !== undefined) {
|
if (
|
||||||
|
err !== undefined &&
|
||||||
|
"response" in err &&
|
||||||
|
err.response !== undefined
|
||||||
|
) {
|
||||||
const errors = err.response.data.errors;
|
const errors = err.response.data.errors;
|
||||||
|
|
||||||
if (errors) {
|
if (errors) {
|
||||||
errors.forEach((error: any) => dispatch(createAlert(error.msg, "danger")));
|
errors.forEach((error: any) =>
|
||||||
|
dispatch(createAlert(error.msg, "danger")),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dispatch(
|
dispatch(
|
||||||
profileError({
|
profileError({
|
||||||
msg: err.response.statusText,
|
msg: err.response.statusText,
|
||||||
status: err.response.status,
|
status: err.response.status,
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -169,7 +198,9 @@ export const addEducation = (formData: Omit<EducationType, '_id'>): AppThunk<Pro
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Delete experience
|
// Delete experience
|
||||||
export const deleteExperience = (id: string): AppThunk<Promise<void>> => async (dispatch) => {
|
export const deleteExperience =
|
||||||
|
(id: string): AppThunk<Promise<void>> =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.delete(`/profile/experience/${id}`);
|
const res = await api.delete(`/profile/experience/${id}`);
|
||||||
|
|
||||||
@@ -177,12 +208,14 @@ export const deleteExperience = (id: string): AppThunk<Promise<void>> => async (
|
|||||||
|
|
||||||
dispatch(createAlert("Experience Removed", "success"));
|
dispatch(createAlert("Experience Removed", "success"));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
errorHandle(dispatch, err)
|
errorHandle(dispatch, err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delete education
|
// Delete education
|
||||||
export const deleteEducation = (id: string): AppThunk<Promise<void>> => async (dispatch) => {
|
export const deleteEducation =
|
||||||
|
(id: string): AppThunk<Promise<void>> =>
|
||||||
|
async (dispatch) => {
|
||||||
try {
|
try {
|
||||||
const res = await api.delete(`/profile/education/${id}`);
|
const res = await api.delete(`/profile/education/${id}`);
|
||||||
|
|
||||||
@@ -190,12 +223,13 @@ export const deleteEducation = (id: string): AppThunk<Promise<void>> => async (d
|
|||||||
|
|
||||||
dispatch(createAlert("Education Removed", "success"));
|
dispatch(createAlert("Education Removed", "success"));
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
errorHandle(dispatch, err)
|
errorHandle(dispatch, err);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Delete account & profile
|
// Delete account & profile
|
||||||
export const deleteAccount = (): AppThunk<Promise<void>> => async (dispatch) => {
|
export const deleteAccount =
|
||||||
|
(): AppThunk<Promise<void>> => 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");
|
||||||
@@ -203,9 +237,11 @@ export const deleteAccount = (): AppThunk<Promise<void>> => async (dispatch) =>
|
|||||||
dispatch(clearProfile());
|
dispatch(clearProfile());
|
||||||
dispatch(accountDeleted());
|
dispatch(accountDeleted());
|
||||||
|
|
||||||
dispatch(createAlert("Your account has been permanently deleted", "danger"));
|
dispatch(
|
||||||
|
createAlert("Your account has been permanently deleted", "danger"),
|
||||||
|
);
|
||||||
} catch (err: unknown) {
|
} catch (err: unknown) {
|
||||||
errorHandle(dispatch, err)
|
errorHandle(dispatch, err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,17 +1,30 @@
|
|||||||
import React, { useState } from "react";
|
import * as React from 'react';
|
||||||
import { Link, Navigate } from "react-router-dom";
|
import { Link, Navigate } from "react-router-dom";
|
||||||
|
|
||||||
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
||||||
import { login } from "../../actions/auth";
|
import { login } from "../../actions/auth";
|
||||||
|
|
||||||
const Login = () => {
|
import Avatar from '@mui/material/Avatar';
|
||||||
const [email, setEmail] = useState('')
|
import Button from '@mui/material/Button';
|
||||||
const [password, setPassword] = useState('')
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
||||||
|
|
||||||
const onSubmit = async (e: React.SyntheticEvent) => {
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
event.preventDefault();
|
||||||
await dispatch(login(email, password));
|
const data = new FormData(event.currentTarget);
|
||||||
|
const email = data.get('email') as string
|
||||||
|
const password = data.get('pasword') as string
|
||||||
|
if (email && password)
|
||||||
|
dispatch(login(email, password));
|
||||||
};
|
};
|
||||||
|
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
@@ -19,39 +32,54 @@ const Login = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<Container component="main" maxWidth="xs">
|
||||||
<h1 className="large text-primary">Sign In</h1>
|
<CssBaseline />
|
||||||
<p className="lead">
|
<Box
|
||||||
<i className="fas fa-user" /> Sign Into Your Account
|
sx={{
|
||||||
</p>
|
marginTop: 8,
|
||||||
<form className="form" onSubmit={onSubmit}>
|
display: 'flex',
|
||||||
<div className="form-group">
|
flexDirection: 'column',
|
||||||
<input
|
alignItems: 'center',
|
||||||
type="email"
|
}}
|
||||||
placeholder="Email Address"
|
>
|
||||||
|
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||||
|
<LockOutlinedIcon />
|
||||||
|
</Avatar>
|
||||||
|
<Typography component="h1" variant="h5">
|
||||||
|
Log In
|
||||||
|
</Typography>
|
||||||
|
<Box component="form" onSubmit={handleSubmit} noValidate sx={{ mt: 1 }}>
|
||||||
|
<TextField
|
||||||
|
margin="normal"
|
||||||
|
required
|
||||||
|
fullWidth
|
||||||
|
id="email"
|
||||||
|
label="Email Address"
|
||||||
name="email"
|
name="email"
|
||||||
value={email}
|
autoComplete="email"
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
<TextField
|
||||||
<div className="form-group">
|
margin="normal"
|
||||||
<input
|
required
|
||||||
type="password"
|
fullWidth
|
||||||
placeholder="Password"
|
|
||||||
name="password"
|
name="password"
|
||||||
value={password}
|
label="Password"
|
||||||
onChange={(e)=>setPassword(e.target.value)}
|
type="password"
|
||||||
//used to be "6"
|
id="password"
|
||||||
minLength={6}
|
autoComplete="current-password"
|
||||||
/>
|
/>
|
||||||
</div>
|
<Button
|
||||||
<input type="submit" className="btn btn-primary" value="Login" />
|
type="submit"
|
||||||
</form>
|
fullWidth
|
||||||
<p className="my-1">
|
variant="contained"
|
||||||
Don't have an account? <Link to="/register">Sign Up</Link>
|
sx={{ mt: 3, mb: 2 }}
|
||||||
</p>
|
>
|
||||||
</section>
|
LOG IN
|
||||||
|
</Button>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
<p> Don't have an account? <Link to="/register">Sign Up</Link></p>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Login;
|
|
||||||
|
|||||||
@@ -1,24 +1,36 @@
|
|||||||
import React, { useState } from 'react';
|
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
||||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
import { Navigate, Link } from "react-router-dom";
|
||||||
import { Link, Navigate } from 'react-router-dom';
|
import { createAlert } from "../../actions/alert";
|
||||||
import { createAlert } from '../../actions/alert';
|
import { register } from "../../actions/auth";
|
||||||
import { register } from '../../actions/auth';
|
|
||||||
|
import * as React from 'react';
|
||||||
|
import Avatar from '@mui/material/Avatar';
|
||||||
|
import Button from '@mui/material/Button';
|
||||||
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
import TextField from '@mui/material/TextField';
|
||||||
|
import Grid from '@mui/material/Grid';
|
||||||
|
import Box from '@mui/material/Box';
|
||||||
|
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
||||||
|
import Typography from '@mui/material/Typography';
|
||||||
|
import Container from '@mui/material/Container';
|
||||||
|
|
||||||
|
|
||||||
const Register = () => {
|
export default function SignUp() {
|
||||||
|
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const [name, setName] = useState('')
|
|
||||||
const [email, setEmail] = useState('')
|
|
||||||
const [password, setPassword] = useState('')
|
|
||||||
const [password2, setPassword2] = useState('')
|
|
||||||
|
|
||||||
const isAuthenticated = useAppSelector(state => state.auth.isAuthenticated)
|
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
event.preventDefault();
|
||||||
const onSubmit = async (e: React.SyntheticEvent) => {
|
const data = new FormData(event.currentTarget);
|
||||||
e.preventDefault();
|
const password = data.get('password') as string
|
||||||
|
const password2 = data.get('password') as string
|
||||||
|
const email = data.get('email') as string
|
||||||
|
const name = data.get('name') as string
|
||||||
if (password !== password2) {
|
if (password !== password2) {
|
||||||
dispatch(createAlert('Passwords do not match', 'danger'));
|
dispatch(createAlert("Passwords do not match", "danger"));
|
||||||
} else {
|
} else {
|
||||||
|
console.log(name,email, password)
|
||||||
dispatch(register({ name, email, password }));
|
dispatch(register({ name, email, password }));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -28,59 +40,82 @@ const Register = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<Container component="main" maxWidth="xs">
|
||||||
<h1 className="large text-primary">Sign Up</h1>
|
<CssBaseline />
|
||||||
<p className="lead">
|
<Box
|
||||||
<i className="fas fa-user" /> Create Your Account
|
sx={{
|
||||||
</p>
|
marginTop: 8,
|
||||||
<form className="form" onSubmit={onSubmit}>
|
display: 'flex',
|
||||||
<div className="form-group">
|
flexDirection: 'column',
|
||||||
<input
|
alignItems: 'center',
|
||||||
type="text"
|
}}
|
||||||
placeholder="Name"
|
>
|
||||||
|
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
||||||
|
<LockOutlinedIcon />
|
||||||
|
</Avatar>
|
||||||
|
<Typography component="h1" variant="h5">
|
||||||
|
Sign up
|
||||||
|
</Typography>
|
||||||
|
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
|
||||||
|
<Grid container spacing={2}>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<TextField
|
||||||
name="name"
|
name="name"
|
||||||
value={name}
|
required
|
||||||
onChange={(e) => setName(e.target.value)}
|
fullWidth
|
||||||
|
id="name"
|
||||||
|
label="Name"
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
<div className="form-group">
|
<Grid item xs={12}>
|
||||||
<input
|
<TextField
|
||||||
type="email"
|
required
|
||||||
placeholder="Email Address"
|
fullWidth
|
||||||
|
id="email"
|
||||||
|
label="Email Address"
|
||||||
name="email"
|
name="email"
|
||||||
value={email}
|
|
||||||
onChange={(e) => setEmail(e.target.value)}
|
|
||||||
/>
|
/>
|
||||||
<small className="form-text">
|
</Grid>
|
||||||
This site uses Gravatar so if you want a profile image, use a
|
<Grid item xs={12}>
|
||||||
Gravatar email
|
<TextField
|
||||||
</small>
|
required
|
||||||
</div>
|
fullWidth
|
||||||
<div className="form-group">
|
|
||||||
<input
|
|
||||||
type="password"
|
|
||||||
placeholder="Password"
|
|
||||||
name="password"
|
name="password"
|
||||||
value={password}
|
label="Password"
|
||||||
onChange={(e) => setPassword(e.target.value)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<input
|
|
||||||
type="password"
|
type="password"
|
||||||
placeholder="Confirm Password"
|
id="password"
|
||||||
name="password2"
|
|
||||||
value={password2}
|
|
||||||
onChange={(e) => setPassword2(e.target.value)}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
<input type="submit" className="btn btn-primary" value="Register" />
|
<Grid item xs={12}>
|
||||||
</form>
|
<TextField
|
||||||
<p className="my-1">
|
required
|
||||||
Already have an account? <Link to="/login">Sign In</Link>
|
fullWidth
|
||||||
</p>
|
name="password2"
|
||||||
</section>
|
label="Repeat Password"
|
||||||
|
type="password"
|
||||||
|
id="password2"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
fullWidth
|
||||||
|
variant="contained"
|
||||||
|
sx={{ mt: 3, mb: 2 }}
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
<Grid container justifyContent="flex-end">
|
||||||
|
<Grid item>
|
||||||
|
Already have an account?{' '}
|
||||||
|
<Link to={'/login'} >
|
||||||
|
Sign in
|
||||||
|
</Link>
|
||||||
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Register;
|
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
import React, { useEffect } from "react";
|
import React, { useEffect } from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { useAppDispatch, useAppSelector } from '../../utils/hooks';
|
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
||||||
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";
|
||||||
|
import { Box, Typography } from "@mui/material";
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -17,11 +18,9 @@ const Dashboard = () => {
|
|||||||
const user = useAppSelector((state) => state.auth.user);
|
const user = useAppSelector((state) => state.auth.user);
|
||||||
const profile = useAppSelector((state) => state.profile.profile);
|
const profile = useAppSelector((state) => state.profile.profile);
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<Box component='main' justifyContent='center' flexDirection='column' minHeight='50vh' display='flex' alignItems='center'>
|
||||||
<h1 className="large text-primary">Dashboard</h1>
|
<Typography variant="h3" component='h2'>Dashboard </Typography>
|
||||||
<p className="lead">
|
<Typography variant="h5" component='h3' display='block'> Welcome {user && user.name}</Typography>
|
||||||
<i className="fas fa-user" /> Welcome {user && user.name}
|
|
||||||
</p>
|
|
||||||
{profile !== null ? (
|
{profile !== null ? (
|
||||||
<>
|
<>
|
||||||
<DashboardActions />
|
<DashboardActions />
|
||||||
@@ -31,7 +30,6 @@ const Dashboard = () => {
|
|||||||
<div className="my-2">
|
<div className="my-2">
|
||||||
<button
|
<button
|
||||||
className="btn btn-danger"
|
className="btn btn-danger"
|
||||||
|
|
||||||
onClick={async () => await dispatch(deleteAccount())}
|
onClick={async () => await dispatch(deleteAccount())}
|
||||||
>
|
>
|
||||||
<i className="fas fa-user" /> Delete My Account
|
<i className="fas fa-user" /> Delete My Account
|
||||||
@@ -40,13 +38,13 @@ const Dashboard = () => {
|
|||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<p>You have not yet setup a profile, please add some info</p>
|
<Typography>You have not yet setup a profile, please add some info</Typography>
|
||||||
<Link to="/create-profile" className="btn btn-primary my-1">
|
<Link to="/create-profile">
|
||||||
Create Profile
|
Create Profile
|
||||||
</Link>
|
</Link>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
</section>
|
</Box>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,17 +1,17 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
|
|
||||||
const DashboardActions = () => {
|
const DashboardActions = () => {
|
||||||
return (
|
return (
|
||||||
<div className='dash-buttons'>
|
<div className="dash-buttons">
|
||||||
<Link to='/edit-profile' className='btn btn-light'>
|
<Link to="/edit-profile" className="btn btn-light">
|
||||||
<i className='fas fa-user-circle text-primary' /> Edit Profile
|
<i className="fas fa-user-circle text-primary" /> Edit Profile
|
||||||
</Link>
|
</Link>
|
||||||
<Link to='/add-experience' className='btn btn-light'>
|
<Link to="/add-experience" className="btn btn-light">
|
||||||
<i className='fab fa-black-tie text-primary' /> Add Experience
|
<i className="fab fa-black-tie text-primary" /> Add Experience
|
||||||
</Link>
|
</Link>
|
||||||
<Link to='/add-education' className='btn btn-light'>
|
<Link to="/add-education" className="btn btn-light">
|
||||||
<i className='fas fa-graduation-cap text-primary' /> Add Education
|
<i className="fas fa-graduation-cap text-primary" /> Add Education
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from "react";
|
||||||
import { deleteExperience } from '../../actions/profile';
|
import { deleteExperience } from "../../actions/profile";
|
||||||
import { ExperienceType } from '../../types';
|
import { ExperienceType } from "../../types";
|
||||||
import formatDate from '../../utils/formatDate';
|
import formatDate from "../../utils/formatDate";
|
||||||
import { useAppDispatch } from '../../utils/hooks';
|
import { useAppDispatch } from "../../utils/hooks";
|
||||||
|
|
||||||
const Experience = ({ experience }: { experience: ExperienceType[] }) => {
|
const Experience = ({ experience }: { experience: ExperienceType[] }) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
@@ -11,7 +11,7 @@ const Experience = ({experience}:{experience:ExperienceType[]}) => {
|
|||||||
<td>{exp.company}</td>
|
<td>{exp.company}</td>
|
||||||
<td className="hide-sm">{exp.title}</td>
|
<td className="hide-sm">{exp.title}</td>
|
||||||
<td>
|
<td>
|
||||||
{formatDate(exp.from)} - {exp.to ? formatDate(exp.to) : 'Now'}
|
{formatDate(exp.from)} - {exp.to ? formatDate(exp.to) : "Now"}
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<button
|
<button
|
||||||
@@ -42,6 +42,4 @@ const Experience = ({experience}:{experience:ExperienceType[]}) => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default Experience;
|
export default Experience;
|
||||||
|
|||||||
@@ -1,56 +1,194 @@
|
|||||||
import React, { Fragment } from "react";
|
import React from "react";
|
||||||
import { Link } from "react-router-dom";
|
import { Link } from "react-router-dom";
|
||||||
import { logOut } from "../../reducers/auth";
|
import { logOut } from "../../reducers/auth";
|
||||||
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
||||||
|
|
||||||
|
import AppBar from "@mui/material/AppBar";
|
||||||
|
import Box from "@mui/material/Box";
|
||||||
|
import Toolbar from "@mui/material/Toolbar";
|
||||||
|
import IconButton from "@mui/material/IconButton";
|
||||||
|
import Typography from "@mui/material/Typography";
|
||||||
|
import Menu from "@mui/material/Menu";
|
||||||
|
import Container from "@mui/material/Container";
|
||||||
|
import Button from "@mui/material/Button";
|
||||||
|
import MenuItem from "@mui/material/MenuItem";
|
||||||
|
import MenuIcon from "@mui/icons-material/Menu";
|
||||||
|
import AdbIcon from "@mui/icons-material/Adb";
|
||||||
|
|
||||||
|
const linkStyle = { color: 'inherit', textDecoration: 'none' }
|
||||||
|
|
||||||
const Navbar = () => {
|
const Navbar = () => {
|
||||||
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
const isAuthenticated = useAppSelector((state) => state.auth.isAuthenticated);
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const authLinks = (
|
|
||||||
<ul>
|
|
||||||
<li>
|
|
||||||
<Link to="/profiles">Developers</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to="/posts">Posts</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to="/dashboard">
|
|
||||||
<i className="fas fa-user" /> <span className="hide-sm">Profile</span>
|
|
||||||
</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a onClick={()=>dispatch(logOut())} href="#!">
|
|
||||||
<i className="fas fa-sign-out-alt" />{' '}
|
|
||||||
<span className="hide-sm">Logout</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
);
|
|
||||||
|
|
||||||
const guestLinks = (
|
const [anchorElNav, setAnchorElNav] = React.useState<null | HTMLElement>(
|
||||||
<ul>
|
null,
|
||||||
<li>
|
|
||||||
<Link to="/profiles">Developers</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to="/register">Register</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link to="/login">Login</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
);
|
);
|
||||||
|
const handleOpenNavMenu = (event: React.MouseEvent<HTMLElement>) => {
|
||||||
|
setAnchorElNav(event.currentTarget);
|
||||||
|
};
|
||||||
|
const handleCloseNavMenu = () => {
|
||||||
|
setAnchorElNav(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const authLinksSmall = [
|
||||||
|
(<MenuItem onClick={handleCloseNavMenu} key={1}>
|
||||||
|
<Link style={linkStyle} to={'/profiles'}>Developers</Link>
|
||||||
|
</MenuItem>),
|
||||||
|
(<MenuItem onClick={handleCloseNavMenu} key={2}>
|
||||||
|
<Link style={linkStyle} to={'/posts'}>Posts</Link>
|
||||||
|
</MenuItem>),
|
||||||
|
(<MenuItem onClick={handleCloseNavMenu} key={3}>
|
||||||
|
<Link style={linkStyle} to={'/profile'}>Profile</Link>
|
||||||
|
</MenuItem>),
|
||||||
|
(<MenuItem onClick={handleCloseNavMenu} key={4}>
|
||||||
|
<a style={linkStyle} onClick={() => dispatch(logOut())} href="#!">
|
||||||
|
<span>Logout</span>
|
||||||
|
</a>
|
||||||
|
</MenuItem>)]
|
||||||
|
const guestLinksSmall = [
|
||||||
|
(
|
||||||
|
<MenuItem onClick={handleCloseNavMenu} key={1}>
|
||||||
|
<Link style={linkStyle} to={'/profiles'}>Developers</Link>
|
||||||
|
</MenuItem>),
|
||||||
|
(<MenuItem onClick={handleCloseNavMenu} key={2}>
|
||||||
|
<Link style={linkStyle} to={'/register'}>Register</Link>
|
||||||
|
</MenuItem>),
|
||||||
|
(<MenuItem onClick={handleCloseNavMenu} key={3}>
|
||||||
|
<Link style={linkStyle} to={'/login'}>Login</Link>
|
||||||
|
</MenuItem>)]
|
||||||
|
const authLinksBig = [
|
||||||
|
(<Button key={1}
|
||||||
|
sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={'/profiles'} >Developers</Link>
|
||||||
|
</Button>),
|
||||||
|
(<Button key={2}
|
||||||
|
sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={'/posts'}>posts</Link>
|
||||||
|
</Button>),
|
||||||
|
(<Button
|
||||||
|
key={3} sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={'/profile'}>Profile</Link>
|
||||||
|
</Button>
|
||||||
|
), (<Button
|
||||||
|
key={4} sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<a style={linkStyle} onClick={() => dispatch(logOut())} href="#!">
|
||||||
|
<span>Logout</span>
|
||||||
|
</a>
|
||||||
|
</Button>
|
||||||
|
)]
|
||||||
|
|
||||||
|
const guestLinksBig = [
|
||||||
|
(<Button key={1}
|
||||||
|
sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={'/profiles'} >Developers</Link>
|
||||||
|
</Button>),
|
||||||
|
(<Button key={2}
|
||||||
|
sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={'/register'}>Register</Link>
|
||||||
|
</Button>),
|
||||||
|
(<Button key={3}
|
||||||
|
sx={{ my: 2, color: "white", display: "block" }}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={'/login'}>Login</Link>
|
||||||
|
</Button>)
|
||||||
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav className="navbar bg-dark">
|
<AppBar position="static">
|
||||||
<h1>
|
<Container maxWidth="xl">
|
||||||
<Link to="/posts">
|
<Toolbar disableGutters>
|
||||||
<i className="fas fa-code" /> DevConnector
|
{/*Icon for big dislay*/}
|
||||||
</Link>
|
<AdbIcon sx={{ display: { xs: "none", md: "flex" }, mr: 1 }} />
|
||||||
</h1>
|
{/*LOGO link for bi display*/}
|
||||||
<Fragment>{isAuthenticated ? authLinks : guestLinks}</Fragment>
|
<Typography
|
||||||
</nav>
|
variant="h6"
|
||||||
|
noWrap
|
||||||
|
component="h6"
|
||||||
|
sx={{
|
||||||
|
mr: 2,
|
||||||
|
display: { xs: "none", md: "flex" },
|
||||||
|
fontFamily: "monospace",
|
||||||
|
fontWeight: 700,
|
||||||
|
letterSpacing: ".3rem",
|
||||||
|
color: "inherit",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={"/posts"}>DevConnect</Link>
|
||||||
|
</Typography>
|
||||||
|
{/* Small display menu*/}
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
flexGrow: 1,
|
||||||
|
display: { xs: "flex", md: "none" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* Small display button */}
|
||||||
|
<IconButton
|
||||||
|
size="large"
|
||||||
|
aria-label="account of current user"
|
||||||
|
aria-controls="menu-appbar"
|
||||||
|
aria-haspopup="true"
|
||||||
|
onClick={handleOpenNavMenu}
|
||||||
|
color="inherit"
|
||||||
|
>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
{/* The actual menu that pops when you click the button */}
|
||||||
|
<Menu
|
||||||
|
id="menu-appbar"
|
||||||
|
anchorEl={anchorElNav}
|
||||||
|
anchorOrigin={{
|
||||||
|
vertical: "bottom",
|
||||||
|
horizontal: "left",
|
||||||
|
}}
|
||||||
|
keepMounted
|
||||||
|
transformOrigin={{
|
||||||
|
vertical: "top",
|
||||||
|
horizontal: "left",
|
||||||
|
}}
|
||||||
|
open={Boolean(anchorElNav)}
|
||||||
|
onClose={handleCloseNavMenu}
|
||||||
|
sx={{
|
||||||
|
display: { xs: "block", md: "none" },
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{isAuthenticated ? authLinksSmall.map(el => el) : guestLinksSmall.map(el => el)}
|
||||||
|
</Menu>
|
||||||
|
</Box>
|
||||||
|
{/* Icon for small display */}
|
||||||
|
<AdbIcon sx={{ display: { xs: "flex", md: "none" }, mr: 1 }} />
|
||||||
|
{/* LOGO text link small display*/}
|
||||||
|
<Typography
|
||||||
|
variant="h5"
|
||||||
|
noWrap
|
||||||
|
component="h5"
|
||||||
|
sx={{
|
||||||
|
mr: 2,
|
||||||
|
display: { xs: "flex", md: "none" },
|
||||||
|
flexGrow: 1,
|
||||||
|
fontFamily: "monospace",
|
||||||
|
letterSpacing: ".3rem",
|
||||||
|
color: "inherit",
|
||||||
|
textDecoration: "none",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Link style={linkStyle} to={"/posts"}>DevConnect</Link>
|
||||||
|
</Typography>
|
||||||
|
{/* Menu bar for big display*/}
|
||||||
|
<Box sx={{ flexGrow: 1, display: { xs: "none", md: "flex" } }}>
|
||||||
|
{isAuthenticated ? authLinksBig.map(el => el) : guestLinksBig.map(el => el)}
|
||||||
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
</Container>
|
||||||
|
</AppBar>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
|
|
||||||
const NotFound = () => {
|
const NotFound = () => {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from "react";
|
||||||
import spinner from './spinner.gif';
|
import spinner from "./spinner.gif";
|
||||||
|
|
||||||
const Spinner = () => (
|
const Spinner = () => (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<img
|
<img
|
||||||
src={spinner}
|
src={spinner}
|
||||||
style={{ width: '200px', margin: 'auto', display: 'block' }}
|
style={{ width: "200px", margin: "auto", display: "block" }}
|
||||||
alt="Loading..."
|
alt="Loading..."
|
||||||
/>
|
/>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|||||||
@@ -1,39 +1,37 @@
|
|||||||
import React, { useState } from 'react';
|
import React, { useState } from "react";
|
||||||
import { addComment } from '../../actions/post';
|
import { addComment } from "../../actions/post";
|
||||||
import { useAppDispatch } from '../../utils/hooks';
|
import { useAppDispatch } from "../../utils/hooks";
|
||||||
|
|
||||||
const CommentForm = ({ postId }: { postId: string }) => {
|
const CommentForm = ({ postId }: { postId: string }) => {
|
||||||
const [text, setText] = useState('');
|
const [text, setText] = useState("");
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='post-form'>
|
<div className="post-form">
|
||||||
<div className='bg-primary p'>
|
<div className="bg-primary p">
|
||||||
<h3>Leave a Comment</h3>
|
<h3>Leave a Comment</h3>
|
||||||
</div>
|
</div>
|
||||||
<form
|
<form
|
||||||
className='form my-1'
|
className="form my-1"
|
||||||
onSubmit={async (e) => {
|
onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await dispatch(addComment(postId, { text }));
|
await dispatch(addComment(postId, { text }));
|
||||||
setText('');
|
setText("");
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<textarea
|
<textarea
|
||||||
name='text'
|
name="text"
|
||||||
cols={30}
|
cols={30}
|
||||||
rows={5}
|
rows={5}
|
||||||
placeholder='Comment the post'
|
placeholder="Comment the post"
|
||||||
value={text}
|
value={text}
|
||||||
onChange={e => setText(e.target.value)}
|
onChange={(e) => setText(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
<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>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default CommentForm;
|
||||||
export default CommentForm
|
|
||||||
|
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ import { deleteComment } from "../../actions/post";
|
|||||||
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
||||||
import { Comment } from "../../types";
|
import { Comment } from "../../types";
|
||||||
interface CommentItemProps {
|
interface CommentItemProps {
|
||||||
postId: string,
|
postId: string;
|
||||||
comment: Comment
|
comment: Comment;
|
||||||
}
|
}
|
||||||
const CommentItem = ({
|
const CommentItem = ({
|
||||||
postId,
|
postId,
|
||||||
|
|||||||
@@ -7,7 +7,9 @@ import { useAppDispatch, useAppSelector } from "../../utils/hooks";
|
|||||||
|
|
||||||
const PostItem = ({
|
const PostItem = ({
|
||||||
post: { _id, text, name, avatar, user, likes, comments, date },
|
post: { _id, text, name, avatar, user, likes, comments, date },
|
||||||
}: { post: Post }) => {
|
}: {
|
||||||
|
post: Post;
|
||||||
|
}) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const auth = useAppSelector((state) => state.auth);
|
const auth = useAppSelector((state) => state.auth);
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ const Posts = () => {
|
|||||||
}, [dispatch]);
|
}, [dispatch]);
|
||||||
const posts = useAppSelector((state) => state.post.posts);
|
const posts = useAppSelector((state) => state.post.posts);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<section className="container">
|
||||||
<h1 className="large text-primary">Posts</h1>
|
<h1 className="large text-primary">Posts</h1>
|
||||||
|
|||||||
@@ -19,8 +19,9 @@ const AddEducation = () => {
|
|||||||
const { school, degree, fieldofstudy, from, to, description, current } =
|
const { school, degree, fieldofstudy, from, to, description, current } =
|
||||||
formData;
|
formData;
|
||||||
|
|
||||||
const onChange = (event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>) =>
|
const onChange = (
|
||||||
setFormData({ ...formData, [event.target.name]: event.target.value });
|
event: React.ChangeEvent<HTMLTextAreaElement | HTMLInputElement>,
|
||||||
|
) => setFormData({ ...formData, [event.target.name]: event.target.value });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<section className="container">
|
||||||
@@ -34,7 +35,9 @@ const AddEducation = () => {
|
|||||||
className="form"
|
className="form"
|
||||||
onSubmit={async (e) => {
|
onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await dispatch(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
|
// i have no idea how this works used to work, i removed the navigate function from the addEducation and it does now
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
@@ -113,5 +116,4 @@ const AddEducation = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export default AddEducation;
|
export default AddEducation;
|
||||||
|
|||||||
@@ -1,25 +1,26 @@
|
|||||||
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 { addExperience } from '../../actions/profile';
|
import { addExperience } from "../../actions/profile";
|
||||||
import { useAppDispatch } from '../../utils/hooks';
|
import { useAppDispatch } from "../../utils/hooks";
|
||||||
|
|
||||||
const AddExperience = () => {
|
const AddExperience = () => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const [formData, setFormData] = useState({
|
const [formData, setFormData] = useState({
|
||||||
company: '',
|
company: "",
|
||||||
title: '',
|
title: "",
|
||||||
location: '',
|
location: "",
|
||||||
from: '',
|
from: "",
|
||||||
to: '',
|
to: "",
|
||||||
current: false,
|
current: false,
|
||||||
description: ''
|
description: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
const { company, title, location, from, to, current, description } = formData;
|
const { company, title, location, from, to, current, description } = formData;
|
||||||
|
|
||||||
const onChange = (e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) =>
|
const onChange = (
|
||||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>,
|
||||||
|
) => setFormData({ ...formData, [e.target.name]: e.target.value });
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<section className="container">
|
||||||
@@ -33,7 +34,9 @@ const AddExperience = () => {
|
|||||||
className="form"
|
className="form"
|
||||||
onSubmit={async (e) => {
|
onSubmit={async (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
await dispatch(addExperience(formData)).then(() => navigate('/dashboard'));
|
await dispatch(addExperience(formData)).then(() =>
|
||||||
|
navigate("/dashboard"),
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
@@ -79,7 +82,7 @@ const AddExperience = () => {
|
|||||||
onChange={() => {
|
onChange={() => {
|
||||||
setFormData({ ...formData, current: !current });
|
setFormData({ ...formData, current: !current });
|
||||||
}}
|
}}
|
||||||
/>{' '}
|
/>{" "}
|
||||||
Current Job
|
Current Job
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { Button, Box, Container, CssBaseline, Grid, InputLabel, MenuItem, Select, TextField, Typography } from "@mui/material";
|
||||||
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 { createProfile, getCurrentProfile } from "../../actions/profile";
|
import { createProfile, getCurrentProfile } from "../../actions/profile";
|
||||||
@@ -19,13 +20,10 @@ const initialState = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const ProfileForm = () => {
|
const ProfileForm = () => {
|
||||||
|
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const { profile, loading }: { profile: any, loading: boolean } = useAppSelector((state) => state.profile);
|
const { profile, loading }: { profile: any; loading: boolean } =
|
||||||
//TODO
|
useAppSelector((state) => state.profile);
|
||||||
// issue with for in
|
|
||||||
|
|
||||||
const [formData, setFormData] = useState(initialState);
|
const [formData, setFormData] = useState(initialState);
|
||||||
|
|
||||||
const creatingProfile = useMatch("/create-profile");
|
const creatingProfile = useMatch("/create-profile");
|
||||||
@@ -37,7 +35,7 @@ const ProfileForm = () => {
|
|||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// if there is no profile, attempt to fetch one
|
// if there is no profile, attempt to fetch one
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
await dispatch(getCurrentProfile())
|
await dispatch(getCurrentProfile());
|
||||||
}
|
}
|
||||||
if (!profile) fetchData();
|
if (!profile) fetchData();
|
||||||
|
|
||||||
@@ -60,208 +58,222 @@ const ProfileForm = () => {
|
|||||||
}
|
}
|
||||||
}, [loading, dispatch, profile]);
|
}, [loading, dispatch, profile]);
|
||||||
|
|
||||||
const {
|
|
||||||
company,
|
const onSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
|
||||||
|
const editing = profile ? true : false;
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
const data = new FormData(e.currentTarget);
|
||||||
|
const website = data.get('website') as string
|
||||||
|
const location = data.get('location') as string
|
||||||
|
const skills = data.get('skills') as string
|
||||||
|
const githubusername = data.get('githubUser') as string
|
||||||
|
const company = data.get('company') as string
|
||||||
|
const bio = data.get('bio') as string
|
||||||
|
const twitter = data.get('twitter') as string
|
||||||
|
const facebook = data.get('facebook') as string
|
||||||
|
const youtube = data.get('youtube') as string
|
||||||
|
const linkedin = data.get('linkedin') as string
|
||||||
|
const instagram = data.get('instagram') as string
|
||||||
|
const status =data.get('status') as string
|
||||||
|
console.log(data)
|
||||||
|
await dispatch(createProfile({
|
||||||
website,
|
website,
|
||||||
location,
|
location,
|
||||||
status,
|
|
||||||
skills,
|
skills,
|
||||||
githubusername,
|
githubusername,
|
||||||
|
company,
|
||||||
bio,
|
bio,
|
||||||
twitter,
|
twitter,
|
||||||
facebook,
|
facebook,
|
||||||
linkedin,
|
|
||||||
youtube,
|
youtube,
|
||||||
|
linkedin,
|
||||||
instagram,
|
instagram,
|
||||||
} = formData;
|
status
|
||||||
|
}, editing)).then(() => {
|
||||||
const onChange = (e: React.ChangeEvent<HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement>) =>
|
|
||||||
setFormData({ ...formData, [e.target.name]: e.target.value });
|
|
||||||
|
|
||||||
const onSubmit = async (e: React.SyntheticEvent) => {
|
|
||||||
const editing = profile ? true : false;
|
|
||||||
e.preventDefault();
|
|
||||||
await dispatch(createProfile(formData, editing)).then(() => {
|
|
||||||
if (!editing) navigate("/dashboard");
|
if (!editing) navigate("/dashboard");
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section className="container">
|
<Container className="container">
|
||||||
<h1 className="large text-primary">
|
<CssBaseline />
|
||||||
|
<Typography component='h1' variant="h4">
|
||||||
{creatingProfile ? "Create Your Profile" : "Edit Your Profile"}
|
{creatingProfile ? "Create Your Profile" : "Edit Your Profile"}
|
||||||
</h1>
|
</Typography>
|
||||||
<p className="lead">
|
<Typography component='p' variant='body1'>
|
||||||
<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>
|
</Typography>
|
||||||
<small>* = required field</small>
|
<Typography variant="body2">* = required field</Typography>
|
||||||
<form className="form" onSubmit={onSubmit}>
|
<Box
|
||||||
<div className="form-group">
|
component="form"
|
||||||
<select name="status" value={status} onChange={onChange}>
|
noValidate onSubmit={onSubmit} sx={{ mt: 3 }}>
|
||||||
<option>* Select Professional Status</option>
|
<Grid container spacing={2}>
|
||||||
<option value="Developer">Developer</option>
|
<Grid item xs={6}>
|
||||||
<option value="Junior Developer">Junior Developer</option>
|
<TextField
|
||||||
<option value="Senior Developer">Senior Developer</option>
|
|
||||||
<option value="Manager">Manager</option>
|
|
||||||
<option value="Student or Learning">Student or Learning</option>
|
|
||||||
<option value="Instructor">Instructor or Teacher</option>
|
|
||||||
<option value="Intern">Intern</option>
|
|
||||||
<option value="Other">Other</option>
|
|
||||||
</select>
|
|
||||||
<small className="form-text">
|
|
||||||
Give us an idea of where you are at in your career
|
|
||||||
</small>
|
|
||||||
</div>
|
|
||||||
<div className="form-group">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Company"
|
|
||||||
name="company"
|
name="company"
|
||||||
value={company}
|
fullWidth
|
||||||
onChange={onChange}
|
label="Company"
|
||||||
|
autoFocus
|
||||||
/>
|
/>
|
||||||
<small className="form-text">
|
</Grid>
|
||||||
Could be your own company or one you work for
|
<Grid item xs={6}>
|
||||||
</small>
|
<Typography paddingY='1rem'>
|
||||||
</div>
|
Could be your own company or onee you work for
|
||||||
<div className="form-group">
|
</Typography>
|
||||||
<input
|
</Grid>
|
||||||
type="text"
|
<Grid item xs={6}>
|
||||||
placeholder="Website"
|
<TextField
|
||||||
name="website"
|
name="website"
|
||||||
value={website}
|
fullWidth
|
||||||
onChange={onChange}
|
label="Website"
|
||||||
/>
|
/>
|
||||||
<small className="form-text">
|
</Grid>
|
||||||
Could be your own or a company website
|
<Grid item xs={6}>
|
||||||
</small>
|
<Typography paddingY='1rem'>
|
||||||
</div>
|
Could be your own a or a company website
|
||||||
<div className="form-group">
|
</Typography>
|
||||||
<input
|
</Grid>
|
||||||
type="text"
|
<Grid item xs={6}>
|
||||||
placeholder="Location"
|
<TextField
|
||||||
name="location"
|
name="location"
|
||||||
value={location}
|
fullWidth
|
||||||
onChange={onChange}
|
label="Location"
|
||||||
/>
|
/>
|
||||||
<small className="form-text">
|
</Grid>
|
||||||
City & state suggested (eg. Boston, MA)
|
<Grid item xs={6}>
|
||||||
</small>
|
<Typography paddingY='1rem'>
|
||||||
</div>
|
City & state suggest(eg. Boston MA)
|
||||||
<div className="form-group">
|
</Typography>
|
||||||
<input
|
</Grid>
|
||||||
type="text"
|
<Grid item xs={6}>
|
||||||
placeholder="* Skills"
|
<TextField
|
||||||
name="skills"
|
name="skills"
|
||||||
value={skills}
|
required
|
||||||
onChange={onChange}
|
fullWidth
|
||||||
|
label="Skills"
|
||||||
/>
|
/>
|
||||||
<small className="form-text">
|
</Grid>
|
||||||
Please use comma separated values (eg. HTML,CSS,JavaScript,PHP)
|
<Grid item xs={6}>
|
||||||
</small>
|
<Typography>
|
||||||
</div>
|
Please use comma separeted values(eg. HTML, CSS, JavaScript, PHP)
|
||||||
<div className="form-group">
|
</Typography>
|
||||||
<input
|
</Grid>
|
||||||
type="text"
|
<Grid item xs={6}>
|
||||||
placeholder="Github Username"
|
<TextField
|
||||||
name="githubusername"
|
name="githubUser"
|
||||||
value={githubusername}
|
fullWidth
|
||||||
onChange={onChange}
|
label="Github Username"
|
||||||
/>
|
/>
|
||||||
<small className="form-text">
|
</Grid>
|
||||||
If you want your latest repos and a Github link, include your
|
<Grid item xs={6}>
|
||||||
username
|
<Typography>
|
||||||
</small>
|
If you want your latest reepos add a Github link, include your username
|
||||||
</div>
|
</Typography>
|
||||||
<div className="form-group">
|
</Grid>
|
||||||
<textarea
|
<Grid item xs={6}>
|
||||||
placeholder="A short bio of yourself"
|
<InputLabel id='status'>Status *</InputLabel>
|
||||||
name="bio"
|
<Select fullWidth
|
||||||
value={bio}
|
labelId="status"
|
||||||
onChange={onChange}
|
name='status'
|
||||||
/>
|
required
|
||||||
<small className="form-text">Tell us a little about yourself</small>
|
placeholder='Select Profesional status'
|
||||||
</div>
|
defaultValue={""}
|
||||||
|
|
||||||
<div className="my-2">
|
|
||||||
<button
|
|
||||||
onClick={() => toggleSocialInputs(!displaySocialInputs)}
|
|
||||||
type="button"
|
|
||||||
className="btn btn-light"
|
|
||||||
>
|
>
|
||||||
|
<MenuItem value="">None</MenuItem>
|
||||||
|
<MenuItem value="Developer">Developer</MenuItem>
|
||||||
|
<MenuItem value="Junior Developer">Junior Developer</MenuItem>
|
||||||
|
<MenuItem value="Senior Developer">Senior Developer</MenuItem>
|
||||||
|
<MenuItem value="Manager">Manager</MenuItem>
|
||||||
|
<MenuItem value="Student or Learning">Student or Learning</MenuItem>
|
||||||
|
<MenuItem value="Instructor">Instructor or Teacher</MenuItem>
|
||||||
|
<MenuItem value="Intern">Intern</MenuItem>
|
||||||
|
<MenuItem value="Other">Other</MenuItem>
|
||||||
|
</Select>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
|
<Typography paddingTop='2.3rem' >Select Profesional Status</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Typography>
|
||||||
|
Tell us a little about yourself
|
||||||
|
</Typography>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12} >
|
||||||
|
<TextField
|
||||||
|
name="bio"
|
||||||
|
multiline
|
||||||
|
rows={4}
|
||||||
|
fullWidth
|
||||||
|
label="A short bio of yourself"
|
||||||
|
/>
|
||||||
|
</Grid>
|
||||||
|
<Grid item xs={12}>
|
||||||
|
<Button
|
||||||
|
variant='contained'
|
||||||
|
onClick={() => toggleSocialInputs(!displaySocialInputs)}>
|
||||||
Add Social Network Links
|
Add Social Network Links
|
||||||
</button>
|
</Button>
|
||||||
<span>Optional</span>
|
</Grid>
|
||||||
</div>
|
{displaySocialInputs ? (
|
||||||
|
<>
|
||||||
{displaySocialInputs && (
|
<Grid item xs={6}>
|
||||||
<Fragment>
|
<TextField
|
||||||
<div className="form-group social-input">
|
fullWidth
|
||||||
<i className="fab fa-twitter fa-2x" />
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
placeholder="Twitter URL"
|
|
||||||
name="twitter"
|
name="twitter"
|
||||||
value={twitter}
|
label="Twitter URL"
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
<div className="form-group social-input">
|
</Grid>
|
||||||
<i className="fab fa-facebook fa-2x" />
|
<Grid item xs={6}>
|
||||||
<input
|
<TextField
|
||||||
type="text"
|
fullWidth
|
||||||
placeholder="Facebook URL"
|
|
||||||
name="facebook"
|
name="facebook"
|
||||||
value={facebook}
|
label="FaceBook URL"
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
<div className="form-group social-input">
|
</Grid>
|
||||||
<i className="fab fa-youtube fa-2x" />
|
<Grid item xs={6}>
|
||||||
<input
|
<TextField
|
||||||
type="text"
|
fullWidth
|
||||||
placeholder="YouTube URL"
|
|
||||||
name="youtube"
|
name="youtube"
|
||||||
value={youtube}
|
label="YouTube URL"
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
<div className="form-group social-input">
|
</Grid>
|
||||||
<i className="fab fa-linkedin fa-2x" />
|
<Grid item xs={6}>
|
||||||
<input
|
<TextField
|
||||||
type="text"
|
fullWidth
|
||||||
placeholder="Linkedin URL"
|
|
||||||
name="linkedin"
|
name="linkedin"
|
||||||
value={linkedin}
|
label="Linkedin URL"
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
|
<Grid item xs={6}>
|
||||||
<div className="form-group social-input">
|
</Grid>
|
||||||
<i className="fab fa-instagram fa-2x" />
|
<Grid item xs={6}>
|
||||||
<input
|
<TextField
|
||||||
type="text"
|
fullWidth
|
||||||
placeholder="Instagram URL"
|
|
||||||
name="instagram"
|
name="instagram"
|
||||||
value={instagram}
|
label="Instagram URL"
|
||||||
onChange={onChange}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</Grid>
|
||||||
</Fragment>
|
</>) : null}
|
||||||
)}
|
<Grid item xs={8}>
|
||||||
|
<Button variant='contained' type="submit">Sumbit query</Button>
|
||||||
<input type="submit" className="btn btn-primary my-1" />
|
</Grid>
|
||||||
<Link className="btn btn-light my-1" to="/dashboard">
|
<Grid item xs={2}>
|
||||||
Go Back
|
<Button variant='outlined' >
|
||||||
</Link>
|
<Link style={{ color: 'inherit', textDecoration: 'none' }} to='/dashboard'>Go Back</Link>
|
||||||
</form>
|
</Button>
|
||||||
</section>
|
</Grid>
|
||||||
|
</Grid>
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,7 @@ const Profile = () => {
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
async function fetchData() {
|
async function fetchData() {
|
||||||
if (typeof id === 'string')
|
if (typeof id === "string") await dispatch(getProfileById(id));
|
||||||
await dispatch(getProfileById(id));
|
|
||||||
}
|
}
|
||||||
fetchData();
|
fetchData();
|
||||||
}, [dispatch, id]);
|
}, [dispatch, id]);
|
||||||
|
|||||||
@@ -1,31 +1,32 @@
|
|||||||
import React, { Fragment } from 'react';
|
import React, { Fragment } from "react";
|
||||||
import { ProfileType } from '../../types';
|
import { ProfileType } from "../../types";
|
||||||
|
|
||||||
const ProfileAbout = ({
|
const ProfileAbout = ({
|
||||||
profile: {
|
profile: {
|
||||||
bio,
|
bio,
|
||||||
skills,
|
skills,
|
||||||
user: { name }
|
user: { name },
|
||||||
}
|
},
|
||||||
}:{profile:ProfileType}) => (
|
}: {
|
||||||
<div className='profile-about bg-light p-2'>
|
profile: ProfileType;
|
||||||
|
}) => (
|
||||||
|
<div className="profile-about bg-light p-2">
|
||||||
{bio && (
|
{bio && (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<h2 className='text-primary'>{name.trim().split(' ')[0]}s Bio</h2>
|
<h2 className="text-primary">{name.trim().split(" ")[0]}s Bio</h2>
|
||||||
<p>{bio}</p>
|
<p>{bio}</p>
|
||||||
<div className='line' />
|
<div className="line" />
|
||||||
</Fragment>
|
</Fragment>
|
||||||
)}
|
)}
|
||||||
<h2 className='text-primary'>Skill Set</h2>
|
<h2 className="text-primary">Skill Set</h2>
|
||||||
<div className='skills'>
|
<div className="skills">
|
||||||
{skills.map((skill, index) => (
|
{skills.map((skill, index) => (
|
||||||
<div key={index} className='p-1'>
|
<div key={index} className="p-1">
|
||||||
<i className='fas fa-check' /> {skill}
|
<i className="fas fa-check" /> {skill}
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
export default ProfileAbout;
|
export default ProfileAbout;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { EducationType } from '../../types';
|
import { EducationType } from "../../types";
|
||||||
import formatDate from '../../utils/formatDate';
|
import formatDate from "../../utils/formatDate";
|
||||||
|
|
||||||
const ProfileEducation = ({
|
const ProfileEducation = ({
|
||||||
education: { school, degree, fieldofstudy, current, to, from, description }
|
education: { school, degree, fieldofstudy, current, to, from, description },
|
||||||
}: { education: EducationType }) => (
|
}: {
|
||||||
|
education: EducationType;
|
||||||
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-dark">{school}</h3>
|
<h3 className="text-dark">{school}</h3>
|
||||||
<p>
|
<p>
|
||||||
{formatDate(from)} - {to ? formatDate(to) : 'Now'}
|
{formatDate(from)} - {to ? formatDate(to) : "Now"}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Degree: </strong> {degree}
|
<strong>Degree: </strong> {degree}
|
||||||
@@ -22,6 +24,4 @@ const ProfileEducation = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export default ProfileEducation;
|
export default ProfileEducation;
|
||||||
|
|||||||
@@ -1,14 +1,16 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { ExperienceType } from '../../types';
|
import { ExperienceType } from "../../types";
|
||||||
import formatDate from '../../utils/formatDate';
|
import formatDate from "../../utils/formatDate";
|
||||||
|
|
||||||
const ProfileExperience = ({
|
const ProfileExperience = ({
|
||||||
experience: { company, title, location, current, to, from, description }
|
experience: { company, title, location, current, to, from, description },
|
||||||
}: { experience: ExperienceType }) => (
|
}: {
|
||||||
|
experience: ExperienceType;
|
||||||
|
}) => (
|
||||||
<div>
|
<div>
|
||||||
<h3 className="text-dark">{company}</h3>
|
<h3 className="text-dark">{company}</h3>
|
||||||
<p>
|
<p>
|
||||||
{formatDate(from)} - {to ? formatDate(to) : 'Now'}
|
{formatDate(from)} - {to ? formatDate(to) : "Now"}
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<strong>Position: </strong> {title}
|
<strong>Position: </strong> {title}
|
||||||
@@ -22,5 +24,4 @@ const ProfileExperience = ({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
export default ProfileExperience;
|
export default ProfileExperience;
|
||||||
|
|||||||
@@ -10,7 +10,9 @@ const ProfileTop = ({
|
|||||||
social,
|
social,
|
||||||
user: { name, avatar },
|
user: { name, avatar },
|
||||||
},
|
},
|
||||||
}: { profile: ProfileType }) => {
|
}: {
|
||||||
|
profile: ProfileType;
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className="profile-top bg-primary p-2">
|
<div className="profile-top bg-primary p-2">
|
||||||
<img className="round-img my-1" src={avatar} alt="" />
|
<img className="round-img my-1" src={avatar} alt="" />
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from "react-router-dom";
|
||||||
import { ProfileType } from '../../types';
|
import { ProfileType } from "../../types";
|
||||||
|
|
||||||
const ProfileItem = ({
|
const ProfileItem = ({
|
||||||
profile: {
|
profile: {
|
||||||
@@ -8,26 +8,28 @@ const ProfileItem = ({
|
|||||||
status,
|
status,
|
||||||
company,
|
company,
|
||||||
location,
|
location,
|
||||||
skills
|
skills,
|
||||||
}
|
},
|
||||||
}: { profile: ProfileType }) => {
|
}: {
|
||||||
|
profile: ProfileType;
|
||||||
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className='profile bg-light'>
|
<div className="profile bg-light">
|
||||||
<img src={avatar} alt='' className='round-img' />
|
<img src={avatar} alt="" className="round-img" />
|
||||||
<div>
|
<div>
|
||||||
<h2>{name}</h2>
|
<h2>{name}</h2>
|
||||||
<p>
|
<p>
|
||||||
{status} {company && <span> at {company}</span>}
|
{status} {company && <span> at {company}</span>}
|
||||||
</p>
|
</p>
|
||||||
<p className='my-1'>{location && <span>{location}</span>}</p>
|
<p className="my-1">{location && <span>{location}</span>}</p>
|
||||||
<Link to={`/profile/${_id}`} className='btn btn-primary'>
|
<Link to={`/profile/${_id}`} className="btn btn-primary">
|
||||||
View Profile
|
View Profile
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
<ul>
|
<ul>
|
||||||
{skills.slice(0, 4).map((skill, index) => (
|
{skills.slice(0, 4).map((skill, index) => (
|
||||||
<li key={index} className='text-primary'>
|
<li key={index} className="text-primary">
|
||||||
<i className='fas fa-check' /> {skill}
|
<i className="fas fa-check" /> {skill}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
@@ -36,4 +38,3 @@ const ProfileItem = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
export default ProfileItem;
|
export default ProfileItem;
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,11 @@ import { Navigate } from "react-router-dom";
|
|||||||
import Spinner from "../layout/Spinner";
|
import Spinner from "../layout/Spinner";
|
||||||
import { useAppSelector } from "../../utils/hooks";
|
import { useAppSelector } from "../../utils/hooks";
|
||||||
|
|
||||||
const PrivateRoute = ({ component: Component }: { component: () => JSX.Element }) => {
|
const PrivateRoute = ({
|
||||||
|
component: Component,
|
||||||
|
}: {
|
||||||
|
component: () => JSX.Element;
|
||||||
|
}) => {
|
||||||
const { isAuthenticated, loading } = useAppSelector((state) => state.auth);
|
const { isAuthenticated, loading } = useAppSelector((state) => state.auth);
|
||||||
if (loading) return <Spinner />;
|
if (loading) return <Spinner />;
|
||||||
if (isAuthenticated) return <Component />;
|
if (isAuthenticated) return <Component />;
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
declare module "*.gif" {
|
declare module "*.gif" {
|
||||||
const path: string;
|
const path: string;
|
||||||
export default path
|
export default path;
|
||||||
}
|
}
|
||||||
// bonkers have no idea how this works
|
// bonkers have no idea how this works
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
import React from 'react';
|
import React from "react";
|
||||||
import ReactDOM from 'react-dom/client';
|
import ReactDOM from "react-dom/client";
|
||||||
import App from './App';
|
import App from "./App";
|
||||||
|
|
||||||
const root = ReactDOM.createRoot(
|
const root = ReactDOM.createRoot(
|
||||||
document.getElementById('root') as HTMLElement
|
document.getElementById("root") as HTMLElement,
|
||||||
);
|
);
|
||||||
root.render(
|
root.render(
|
||||||
<React.StrictMode>
|
<React.StrictMode>
|
||||||
<App />
|
<App />
|
||||||
</React.StrictMode>
|
</React.StrictMode>,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import { createSlice } from "@reduxjs/toolkit";
|
|||||||
import type { Alert } from "../types";
|
import type { Alert } from "../types";
|
||||||
import { PayloadAction } from "@reduxjs/toolkit";
|
import { PayloadAction } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
type AlertState = Alert[]
|
type AlertState = Alert[];
|
||||||
|
|
||||||
const initialState: AlertState = [];
|
const initialState: AlertState = [];
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { createSlice } from "@reduxjs/toolkit";
|
import { createSlice } from "@reduxjs/toolkit";
|
||||||
import { PayloadAction } from "@reduxjs/toolkit";
|
import { PayloadAction } from "@reduxjs/toolkit";
|
||||||
import type { User } from '../types'
|
import type { User } from "../types";
|
||||||
|
|
||||||
interface authState {
|
interface authState {
|
||||||
token: string | null
|
token: string | null;
|
||||||
isAuthenticated: boolean | null
|
isAuthenticated: boolean | null;
|
||||||
loading: boolean
|
loading: boolean;
|
||||||
user: User | null
|
user: User | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const initialState: authState = {
|
const initialState: authState = {
|
||||||
|
|||||||
@@ -3,10 +3,10 @@ import { PayloadAction } from "@reduxjs/toolkit";
|
|||||||
import type { Post, Comment } from "../types";
|
import type { Post, Comment } from "../types";
|
||||||
|
|
||||||
interface postState {
|
interface postState {
|
||||||
posts: Post[]
|
posts: Post[];
|
||||||
post: Post | null
|
post: Post | null;
|
||||||
loading: boolean
|
loading: boolean;
|
||||||
error: {}
|
error: {};
|
||||||
//Todo erros
|
//Todo erros
|
||||||
}
|
}
|
||||||
const initialState: postState = {
|
const initialState: postState = {
|
||||||
@@ -62,7 +62,7 @@ const postSlice = createSlice({
|
|||||||
posts: state.posts.map((post) =>
|
posts: state.posts.map((post) =>
|
||||||
post._id === action.payload.id
|
post._id === action.payload.id
|
||||||
? { ...post, likes: action.payload.likes }
|
? { ...post, likes: action.payload.likes }
|
||||||
: post
|
: post,
|
||||||
),
|
),
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
@@ -74,9 +74,9 @@ const postSlice = createSlice({
|
|||||||
...state,
|
...state,
|
||||||
post: { ...state.post, comments: action.payload },
|
post: { ...state.post, comments: action.payload },
|
||||||
loading: false,
|
loading: false,
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
throw new Error("post missing from state");
|
||||||
throw new Error('post missing from state')
|
|
||||||
},
|
},
|
||||||
removeComment(state, action: PayloadAction<String>) {
|
removeComment(state, action: PayloadAction<String>) {
|
||||||
if ("post" in state && state.post !== null) {
|
if ("post" in state && state.post !== null) {
|
||||||
@@ -85,13 +85,13 @@ const postSlice = createSlice({
|
|||||||
post: {
|
post: {
|
||||||
...state.post,
|
...state.post,
|
||||||
comments: state.post.comments.filter(
|
comments: state.post.comments.filter(
|
||||||
(comment) => comment._id !== action.payload
|
(comment) => comment._id !== action.payload,
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
loading: false,
|
loading: false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
throw new Error('post missing from state')
|
throw new Error("post missing from state");
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,12 +2,12 @@ import { createSlice } from "@reduxjs/toolkit";
|
|||||||
import { Repo, ProfileType } from "../types";
|
import { Repo, ProfileType } from "../types";
|
||||||
|
|
||||||
type ProfileState = {
|
type ProfileState = {
|
||||||
profile: ProfileType | null
|
profile: ProfileType | null;
|
||||||
profiles: ProfileType[]
|
profiles: ProfileType[];
|
||||||
repos: Repo[]
|
repos: Repo[];
|
||||||
loading: boolean
|
loading: boolean;
|
||||||
error: {}
|
error: {};
|
||||||
}
|
};
|
||||||
|
|
||||||
const initialState: ProfileState = {
|
const initialState: ProfileState = {
|
||||||
profile: null,
|
profile: null,
|
||||||
|
|||||||
+11
-13
@@ -1,20 +1,19 @@
|
|||||||
import setAuthToken from './utils/setAuthToken';
|
import setAuthToken from "./utils/setAuthToken";
|
||||||
import { configureStore } from '@reduxjs/toolkit';
|
import { configureStore } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
import alertReducer from './reducers/alert';
|
|
||||||
import authReducer from './reducers/auth';
|
|
||||||
import profileReducer from './reducers/profile'
|
|
||||||
import postReducer from './reducers/post'
|
|
||||||
|
|
||||||
|
import alertReducer from "./reducers/alert";
|
||||||
|
import authReducer from "./reducers/auth";
|
||||||
|
import profileReducer from "./reducers/profile";
|
||||||
|
import postReducer from "./reducers/post";
|
||||||
|
|
||||||
const store = configureStore({
|
const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
alert: alertReducer,
|
alert: alertReducer,
|
||||||
auth: authReducer,
|
auth: authReducer,
|
||||||
profile: profileReducer,
|
profile: profileReducer,
|
||||||
post: postReducer
|
post: postReducer,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
let currentState = store.getState();
|
let currentState = store.getState();
|
||||||
|
|
||||||
@@ -29,8 +28,7 @@ store.subscribe(() => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
export type RootState = ReturnType<typeof store.getState>
|
export type RootState = ReturnType<typeof store.getState>;
|
||||||
export type AppDispatch = typeof store.dispatch
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
|
||||||
export default store;
|
export default store;
|
||||||
|
|
||||||
|
|||||||
+83
-80
@@ -1,93 +1,96 @@
|
|||||||
|
import { AnyAction, ThunkAction } from "@reduxjs/toolkit";
|
||||||
|
import { RootState } from "./store";
|
||||||
|
|
||||||
import { AnyAction, ThunkAction } from "@reduxjs/toolkit"
|
export type AppThunk<ReturnType = void> = ThunkAction<
|
||||||
import { RootState } from "./store"
|
ReturnType,
|
||||||
|
RootState,
|
||||||
|
unknown,
|
||||||
|
AnyAction
|
||||||
|
>;
|
||||||
|
|
||||||
export type AppThunk<ReturnType = void> = ThunkAction<ReturnType, RootState, unknown, AnyAction>
|
export type FormData = { name: string; email: string; password: string };
|
||||||
|
|
||||||
export type FormData = { name: string, email: string, password: string }
|
|
||||||
|
|
||||||
export type Repo = {
|
export type Repo = {
|
||||||
id: string
|
id: string;
|
||||||
html_url: string
|
html_url: string;
|
||||||
name: string
|
name: string;
|
||||||
description: string
|
description: string;
|
||||||
stargazers_count: number
|
stargazers_count: number;
|
||||||
watchers_count: number
|
watchers_count: number;
|
||||||
forks_count: number
|
forks_count: number;
|
||||||
}
|
};
|
||||||
export type Post = {
|
export type Post = {
|
||||||
user: string,
|
user: string;
|
||||||
text: string
|
text: string;
|
||||||
name: string,
|
name: string;
|
||||||
avatar: string,
|
avatar: string;
|
||||||
likes: User[]
|
likes: User[];
|
||||||
comments: Comment[]
|
comments: Comment[];
|
||||||
date: string
|
date: string;
|
||||||
_id: string
|
_id: string;
|
||||||
}
|
};
|
||||||
export type Comment = {
|
export type Comment = {
|
||||||
user: string
|
user: string;
|
||||||
text: string
|
text: string;
|
||||||
name: string,
|
name: string;
|
||||||
avatar: string,
|
avatar: string;
|
||||||
date: string
|
date: string;
|
||||||
_id: string
|
_id: string;
|
||||||
}
|
};
|
||||||
export type User = {
|
export type User = {
|
||||||
name: string,
|
name: string;
|
||||||
email: string,
|
email: string;
|
||||||
password: string,
|
password: string;
|
||||||
avatar: string,
|
avatar: string;
|
||||||
data: string
|
data: string;
|
||||||
_id: string
|
_id: string;
|
||||||
}
|
};
|
||||||
|
|
||||||
export type ProfileType = {
|
export type ProfileType = {
|
||||||
user: User
|
user: User;
|
||||||
company: string,
|
company: string;
|
||||||
website: string
|
website: string;
|
||||||
location: string
|
location: string;
|
||||||
status: string
|
status: string;
|
||||||
skills: string[]
|
skills: string[];
|
||||||
bio: string
|
bio: string;
|
||||||
githubusername: string
|
githubusername: string;
|
||||||
experience: ExperienceType[]
|
experience: ExperienceType[];
|
||||||
date: string
|
date: string;
|
||||||
education: EducationType[]
|
education: EducationType[];
|
||||||
social: Social
|
social: Social;
|
||||||
_id: string
|
_id: string;
|
||||||
|
};
|
||||||
}
|
|
||||||
export type ExperienceType = {
|
export type ExperienceType = {
|
||||||
title: string
|
title: string;
|
||||||
company: string
|
company: string;
|
||||||
location: string
|
location: string;
|
||||||
from: string
|
from: string;
|
||||||
to: string
|
to: string;
|
||||||
current: boolean
|
current: boolean;
|
||||||
description: string
|
description: string;
|
||||||
_id: string
|
_id: string;
|
||||||
}
|
};
|
||||||
export type EducationType = {
|
export type EducationType = {
|
||||||
school: string
|
school: string;
|
||||||
degree: string
|
degree: string;
|
||||||
fieldofstudy: string
|
fieldofstudy: string;
|
||||||
from: string
|
from: string;
|
||||||
to: string
|
to: string;
|
||||||
current: boolean
|
current: boolean;
|
||||||
description: string
|
description: string;
|
||||||
_id: string
|
_id: string;
|
||||||
}
|
};
|
||||||
export type Social = {
|
export type Social = {
|
||||||
youtube: string
|
youtube: string;
|
||||||
twitter: string
|
twitter: string;
|
||||||
facebook: string
|
facebook: string;
|
||||||
linkedin: string
|
linkedin: string;
|
||||||
instagram: string
|
instagram: string;
|
||||||
_id: string
|
_id: string;
|
||||||
}
|
};
|
||||||
export type Alert = {
|
export type Alert = {
|
||||||
msg: string
|
msg: string;
|
||||||
alertType: string
|
alertType: string;
|
||||||
id: string
|
id: string;
|
||||||
}
|
};
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
import axios from 'axios';
|
import axios from "axios";
|
||||||
import store from '../store';
|
import store from "../store";
|
||||||
|
|
||||||
const api = axios.create({
|
const api = axios.create({
|
||||||
baseURL: '/api',
|
baseURL: "/api",
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json'
|
"Content-Type": "application/json",
|
||||||
}
|
},
|
||||||
});
|
});
|
||||||
api.interceptors.response.use(
|
api.interceptors.response.use(
|
||||||
(res) => res,
|
(res) => res,
|
||||||
(err) => {
|
(err) => {
|
||||||
if (err.response.status === 401) {
|
if (err.response.status === 401) {
|
||||||
store.dispatch({ type: 'auth/logOut' });
|
store.dispatch({ type: "auth/logOut" });
|
||||||
}
|
}
|
||||||
return Promise.reject(err);
|
return Promise.reject(err);
|
||||||
}
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
export default api;
|
export default api;
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { useDispatch, useSelector } from 'react-redux'
|
import { useDispatch, useSelector } from "react-redux";
|
||||||
import type { TypedUseSelectorHook } from 'react-redux'
|
import type { TypedUseSelectorHook } from "react-redux";
|
||||||
import type { RootState, AppDispatch } from '../store'
|
import type { RootState, AppDispatch } from "../store";
|
||||||
|
|
||||||
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
// Use throughout your app instead of plain `useDispatch` and `useSelector`
|
||||||
export const useAppDispatch: () => AppDispatch = useDispatch
|
export const useAppDispatch: () => AppDispatch = useDispatch;
|
||||||
export const useAppDispatch2 = () => useDispatch<AppDispatch>
|
export const useAppDispatch2 = () => useDispatch<AppDispatch>;
|
||||||
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector
|
export const useAppSelector: TypedUseSelectorHook<RootState> = useSelector;
|
||||||
|
|||||||
@@ -1,14 +1,14 @@
|
|||||||
import api from './api';
|
import api from "./api";
|
||||||
|
|
||||||
// store our JWT in LS and set axios headers if we do have a token
|
// store our JWT in LS and set axios headers if we do have a token
|
||||||
|
|
||||||
const setAuthToken = (token: any) => {
|
const setAuthToken = (token: any) => {
|
||||||
if (token) {
|
if (token) {
|
||||||
api.defaults.headers.common['x-auth-token'] = token;
|
api.defaults.headers.common["x-auth-token"] = token;
|
||||||
localStorage.setItem('token', token);
|
localStorage.setItem("token", token);
|
||||||
} else {
|
} else {
|
||||||
delete api.defaults.headers.common['x-auth-token'];
|
delete api.defaults.headers.common["x-auth-token"];
|
||||||
localStorage.removeItem('token');
|
localStorage.removeItem("token");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,7 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es5",
|
"target": "es5",
|
||||||
"lib": [
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"dom",
|
|
||||||
"dom.iterable",
|
|
||||||
"esnext"
|
|
||||||
],
|
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
"esModuleInterop": true,
|
"esModuleInterop": true,
|
||||||
@@ -20,7 +16,5 @@
|
|||||||
"noEmit": true,
|
"noEmit": true,
|
||||||
"jsx": "react-jsx"
|
"jsx": "react-jsx"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["src"]
|
||||||
"src"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user