├── .gitignore
├── README.md
├── package-lock.json
├── package.json
├── public
├── _redirects
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── readmeResources
├── aboutme.PNG
├── addEducation.PNG
├── adminDashboead.PNG
├── adminlogin.PNG
├── contact.PNG
├── contactAdmin.PNG
├── editEdication.PNG
├── education.PNG
├── educationAdmin.PNG
├── experience.PNG
├── experienceAdmin.PNG
├── footer.PNG
├── home.PNG
├── projectAdmin.PNG
├── projects.PNG
├── skills.PNG
└── skilsAdmin.PNG
└── src
├── App.js
├── actions
├── educationAction.js
├── experienceAction.js
├── loginAction.js
├── messageAction.js
├── projectAction.js
└── skillAction.js
├── apis
├── educationApi.js
├── experienceApi.js
├── messageApi.js
├── projectApi.js
├── serverApi.js
├── skillApi.js
└── userApi.js
├── assets
└── images
│ ├── basry-logo.png
│ ├── dev.png
│ ├── dev.svg
│ ├── education.png
│ ├── experience.png
│ └── profil.png
├── components
├── Admin
│ ├── EducationModal.js
│ ├── ExperienceModal.js
│ ├── Modal.js
│ ├── ProjectModal.js
│ ├── SideBar
│ │ ├── SideBar.css
│ │ ├── SideBar.js
│ │ └── SidebarData.js
│ ├── SkillModal.js
│ └── Table.js
└── User
│ ├── About
│ ├── About.css
│ └── About.js
│ ├── Contacts
│ └── Contacts.js
│ ├── Education
│ └── Education.js
│ ├── Experience
│ └── Experience.js
│ ├── Footer
│ ├── Footer.css
│ └── Footer.js
│ ├── Login
│ ├── Login.js
│ └── styles.css
│ ├── Navbar
│ ├── Navbar.js
│ └── styles.css
│ ├── PageIntro
│ ├── PageIntro.js
│ └── styles.css
│ ├── Projects
│ └── Projects.js
│ └── Skills
│ └── Skills.js
├── index.css
├── index.js
├── pages
├── EducationAdmin.js
├── ExperienceAdmin.js
├── Home.js
├── MessageAdmin.js
├── NotFound.js
├── PortfolioUI.js
├── ProjectAdmin.js
└── SkillAdmin.js
├── reducers
├── education.js
├── experience.js
├── index.js
├── isLogged.js
├── message.js
├── project.js
└── skill.js
├── reportWebVitals.js
└── shared
├── SecureRoute.js
├── authorization.js
├── history.js
└── toast.js
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Basry Portfolio Frontend Part
2 |
3 | Dynamic personal website with admin panel which contains educations, experiences, skills, projects, contact fields.
4 | ## Technologies
5 | ##### MERN STACK: Mongo DB, ExpressJS, ReactJS, NodeJS.
6 |
7 | ## Dependancies
8 | * react
9 | * redux
10 | * react-redux
11 | * redux-thunk
12 | * react-router-dom
13 | * react-hook-form
14 | * axios
15 | * moment
16 | * react-datepicker
17 | * react-dom
18 | * react-icons
19 | * react-modal-image
20 | * react-scripts
21 | * react-skillbars
22 | * react-toastify
23 | * web-vitals
24 |
25 |
26 | ## Project Screen Shots
27 | ### Intro
28 | 
29 | ### About Me
30 | 
31 | ### Educations
32 | 
33 | ### Experiences
34 | 
35 | ### Skills
36 | 
37 | ### Projects
38 | 
39 | ### Contact Me
40 | 
41 | ### Footer
42 | 
43 | ### Login Admin Page
44 | 
45 | ### Dashboard Admin
46 | 
47 | ### Educations Admin
48 | 
49 | ### Add Education
50 | 
51 | ### Edit Education
52 | 
53 | ### Experiences Admin
54 | 
55 | ### Skills Admin
56 | 
57 | ### Project Admin
58 | 
59 | ### Contact Admin
60 | 
61 |
62 |
63 | ## Demo
64 | Click [Demo](https://basry.herokuapp.com/) to go to the demonstration page.
65 |
66 | ## Installation and Setup Instructions
67 |
68 | Clone down this repository. You will need `node` and `npm` installed globally on your machine.
69 |
70 | Installation:
71 |
72 | `npm install`
73 |
74 |
75 | To Start Server:
76 |
77 | `npm start`
78 |
79 | To Visit App:
80 |
81 | `localhost:3000/`
82 |
83 | ## Notes
84 | * please note that the application will not work before colone the API express, click [here](https://github.com/oussamabasry/portfolio-backend) to go to backend repository.
85 | * Change the server IP address in /src/app/serverApi.js file.
86 |
87 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "portfolio-frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.12.0",
7 | "@testing-library/react": "^11.2.6",
8 | "@testing-library/user-event": "^12.8.3",
9 | "axios": "^0.21.1",
10 | "jquery": "^3.6.0",
11 | "moment": "^2.29.1",
12 | "react": "^17.0.2",
13 | "react-datepicker": "^3.8.0",
14 | "react-dom": "^17.0.2",
15 | "react-hook-form": "^7.3.6",
16 | "react-icons": "^4.2.0",
17 | "react-modal-image": "^2.5.0",
18 | "react-redux": "^7.2.4",
19 | "react-router-dom": "^5.2.0",
20 | "react-scripts": "4.0.3",
21 | "react-skillbars": "^1.6.1",
22 | "react-toastify": "^7.0.4",
23 | "redux": "^4.1.0",
24 | "redux-thunk": "^2.3.0",
25 | "web-vitals": "^1.1.1"
26 | },
27 | "scripts": {
28 | "start": "react-scripts start",
29 | "build": "react-scripts build",
30 | "test": "react-scripts test",
31 | "eject": "react-scripts eject"
32 | },
33 | "eslintConfig": {
34 | "extends": [
35 | "react-app",
36 | "react-app/jest"
37 | ]
38 | },
39 | "browserslist": {
40 | "production": [
41 | ">0.2%",
42 | "not dead",
43 | "not op_mini all"
44 | ],
45 | "development": [
46 | "last 1 chrome version",
47 | "last 1 firefox version",
48 | "last 1 safari version"
49 | ]
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/public/favicon.ico
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
17 |
18 |
27 |
28 |
29 |
33 |
34 |
38 |
39 |
45 | Basry
46 |
47 |
48 | You need to enable JavaScript to run this app.
49 |
50 |
60 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/public/logo192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/public/logo192.png
--------------------------------------------------------------------------------
/public/logo512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/public/logo512.png
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | },
10 | {
11 | "src": "logo192.png",
12 | "type": "image/png",
13 | "sizes": "192x192"
14 | },
15 | {
16 | "src": "logo512.png",
17 | "type": "image/png",
18 | "sizes": "512x512"
19 | }
20 | ],
21 | "start_url": ".",
22 | "display": "standalone",
23 | "theme_color": "#000000",
24 | "background_color": "#ffffff"
25 | }
26 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | # https://www.robotstxt.org/robotstxt.html
2 | User-agent: *
3 | Disallow:
4 |
--------------------------------------------------------------------------------
/readmeResources/aboutme.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/aboutme.PNG
--------------------------------------------------------------------------------
/readmeResources/addEducation.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/addEducation.PNG
--------------------------------------------------------------------------------
/readmeResources/adminDashboead.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/adminDashboead.PNG
--------------------------------------------------------------------------------
/readmeResources/adminlogin.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/adminlogin.PNG
--------------------------------------------------------------------------------
/readmeResources/contact.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/contact.PNG
--------------------------------------------------------------------------------
/readmeResources/contactAdmin.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/contactAdmin.PNG
--------------------------------------------------------------------------------
/readmeResources/editEdication.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/editEdication.PNG
--------------------------------------------------------------------------------
/readmeResources/education.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/education.PNG
--------------------------------------------------------------------------------
/readmeResources/educationAdmin.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/educationAdmin.PNG
--------------------------------------------------------------------------------
/readmeResources/experience.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/experience.PNG
--------------------------------------------------------------------------------
/readmeResources/experienceAdmin.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/experienceAdmin.PNG
--------------------------------------------------------------------------------
/readmeResources/footer.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/footer.PNG
--------------------------------------------------------------------------------
/readmeResources/home.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/home.PNG
--------------------------------------------------------------------------------
/readmeResources/projectAdmin.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/projectAdmin.PNG
--------------------------------------------------------------------------------
/readmeResources/projects.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/projects.PNG
--------------------------------------------------------------------------------
/readmeResources/skills.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/skills.PNG
--------------------------------------------------------------------------------
/readmeResources/skilsAdmin.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/readmeResources/skilsAdmin.PNG
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { Router, Route, Switch } from "react-router-dom";
3 | import { useSelector } from "react-redux";
4 | import PortfolioUI from "./pages/PortfolioUI";
5 | import Login from "./components/User/Login/Login";
6 | import SideBar from "./components/Admin/SideBar/SideBar";
7 | import EducationAdmin from "./pages/EducationAdmin";
8 | import { ToastContainer } from "react-toastify";
9 | import history from "./shared/history";
10 | import SecureRoute from "./shared/SecureRoute";
11 | import ExperienceAdmin from "./pages/ExperienceAdmin";
12 | import SkillAdmin from "./pages/SkillAdmin";
13 | import MessageAdmin from "./pages/MessageAdmin";
14 | import isLogin from "./shared/authorization";
15 | import ProjectAdmin from "./pages/ProjectAdmin";
16 | import NotFound from "./pages/NotFound";
17 |
18 | function App() {
19 | const [isLogged, setIsLogged] = useState(isLogin);
20 | const login = useSelector((state) => state.login.isLogin);
21 |
22 | useEffect(() => {
23 | setIsLogged(isLogin);
24 | }, [login]);
25 |
26 | return (
27 |
28 |
29 | {isLogged && }
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
52 |
53 | );
54 | }
55 |
56 | export default App;
57 |
--------------------------------------------------------------------------------
/src/actions/educationAction.js:
--------------------------------------------------------------------------------
1 | import {
2 | addEducationApi,
3 | getEducationsApi,
4 | deleteEducationApi,
5 | updateEducationApi,
6 | } from "../apis/educationApi";
7 | import { toastSuccess, toastError } from "../shared/toast";
8 |
9 | export const getEducations = () => async (dispatch) => {
10 | try {
11 | const { data } = await getEducationsApi();
12 | dispatch({ type: "GET_EDUCATIONS", payload: data });
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | export const addEducation = (education) => async (dispatch) => {
19 | try {
20 | const { data } = await addEducationApi(education);
21 | dispatch({ type: "ADD_EDUCATION", payload: data });
22 | toastSuccess("Education Added Successfully");
23 | } catch (error) {
24 | console.log(error);
25 | toastError("Error while adding education");
26 | }
27 | };
28 |
29 | export const deleteEducation = (id) => async (dispatch) => {
30 | try {
31 | await deleteEducationApi(id);
32 | toastSuccess("Education Deleted Successfully");
33 | dispatch({ type: "DELETE_EDUCATION", payload: id });
34 | } catch (error) {
35 | console.log(error);
36 | toastError("Error while deleting education");
37 | }
38 | };
39 |
40 | export const updateEducation = (id, education) => async (dispatch) => {
41 | try {
42 | const { data } = await updateEducationApi(id, education);
43 | dispatch({
44 | type: "UPDATE_EDUCATION",
45 | payload: {...education,_id: data.education._id}
46 | });
47 | toastSuccess("Education Updated Successfully");
48 | } catch (error) {
49 | console.log(error);
50 | toastError("Error while Updated education");
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/actions/experienceAction.js:
--------------------------------------------------------------------------------
1 | import {
2 | addExperienceApi,
3 | getExperiencesApi,
4 | deleteExperienceApi,
5 | updateExperienceApi,
6 | } from "../apis/experienceApi";
7 | import { toastSuccess, toastError } from "../shared/toast";
8 |
9 | export const getExperiences = () => async (dispatch) => {
10 | try {
11 | const { data } = await getExperiencesApi();
12 | dispatch({ type: "GET_EXPERIENCES", payload: data });
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | export const addExperience = (experience) => async (dispatch) => {
19 | try {
20 | const { data } = await addExperienceApi(experience);
21 | dispatch({ type: "ADD_EXEPERIENCE", payload: data });
22 | toastSuccess("Experience Added Successfully");
23 | } catch (error) {
24 | console.log(error);
25 | toastError("Error while adding experience");
26 | }
27 | };
28 |
29 | export const deleteExperience = (id) => async (dispatch) => {
30 | try {
31 | await deleteExperienceApi(id);
32 | dispatch({ type: "DELETE_EXEPERIENCE", payload: id });
33 | toastSuccess("Experience deleted Successfully");
34 | } catch (error) {
35 | console.log(error);
36 | toastError("Error while deleting experience");
37 | }
38 | };
39 |
40 | export const updateExperience = (id, experience) => async (dispatch) => {
41 | try {
42 | const { data } = await updateExperienceApi(id, experience);
43 | dispatch({
44 | type: "UPDATE_EXEPERIENCE",
45 | payload:{...experience,_id: data.experience._id},
46 | });
47 | toastSuccess("Experience Updated Successfully");
48 | } catch (error) {
49 | console.log(error);
50 | toastError("Error while updating experience");
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/actions/loginAction.js:
--------------------------------------------------------------------------------
1 | import { loginApi } from "../apis/userApi";
2 | import { toastSuccess, toastError } from "../shared/toast";
3 | import history from "../shared/history";
4 | import api from "../apis/serverApi";
5 |
6 | export const loginUser = (authData) => async (dispatch) => {
7 | try {
8 | const { data } = await loginApi(authData);
9 | api.defaults.headers.common["Authorization"] = `Bearer ${data.token}`;
10 | localStorage.setItem(
11 | "userData",
12 | JSON.stringify({
13 | token: data.token,
14 | isLogged: true,
15 | })
16 | );
17 | toastSuccess("Login Successfully");
18 | dispatch({
19 | type: "SIGN_IN",
20 | payload: { isLogin: true, token: data.token },
21 | });
22 | history.push("/education");
23 | } catch (error) {
24 | console.log(error);
25 | toastError("Incorrect email or password");
26 | }
27 | };
28 |
29 | export const logoutUser = () => {
30 | delete api.defaults.headers.common["Authorization"];
31 | localStorage.setItem(
32 | "userData",
33 | JSON.stringify({
34 | token: null,
35 | isLogged: false,
36 | })
37 | );
38 | return {
39 | type: "LOGOUT",
40 | payload: { isLogin: false, token: null },
41 | };
42 | };
43 |
--------------------------------------------------------------------------------
/src/actions/messageAction.js:
--------------------------------------------------------------------------------
1 | import { getMessagesApi, updateMessageApi } from "../apis/messageApi";
2 |
3 | export const getMessages = () => async (dispatch) => {
4 | try {
5 | const { data } = await getMessagesApi();
6 | dispatch({ type: "GET_MESSAGES", payload: data });
7 | } catch (error) {
8 | console.log(error);
9 | }
10 | };
11 |
12 | export const updateMessage = (id, message) => async (dispatch) => {
13 | try {
14 | const { data } = await updateMessageApi(id, message);
15 | dispatch({
16 | type: "UPDATE_MESSAGE",
17 | payload: { ...message, _id: data.message._id },
18 | });
19 | } catch (error) {
20 | console.log(error);
21 | }
22 | };
23 |
--------------------------------------------------------------------------------
/src/actions/projectAction.js:
--------------------------------------------------------------------------------
1 | import {
2 | addProjectApi,
3 | getProjectsApi,
4 | deleteProjectApi,
5 | updateProjectApi,
6 | } from "../apis/projectApi";
7 | import { toastSuccess, toastError } from "../shared/toast";
8 |
9 | export const getprojects = () => async (dispatch) => {
10 | try {
11 | const { data } = await getProjectsApi();
12 | dispatch({ type: "GET_PROJECTS", payload: data });
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | export const addProject = (project) => async (dispatch) => {
19 | try {
20 | const { data } = await addProjectApi(project);
21 | dispatch({ type: "ADD_PROJECT", payload: data });
22 | toastSuccess("Project Added Successfully");
23 | } catch (error) {
24 | console.log(error);
25 | toastError("Error while adding project");
26 | }
27 | };
28 |
29 | export const deleteProject = (id) => async (dispatch) => {
30 | try {
31 | await deleteProjectApi(id);
32 | dispatch({ type: "DELETE_PROJECT", payload: id });
33 | toastSuccess("Project deleted Successfully");
34 | } catch (error) {
35 | console.log(error);
36 | toastError("Error while deleting project");
37 | }
38 | };
39 |
40 | export const updateProject = (id, project) => async (dispatch) => {
41 | try {
42 | const { data } = await updateProjectApi(id, project);
43 | dispatch({
44 | type: "UPDATE_PROJECT",
45 | payload: data.project,
46 | //payload:{...project,_id: data.project._id},
47 | });
48 | toastSuccess("Project Updated Successfully");
49 | } catch (error) {
50 | console.log(error);
51 | toastError("Error while updating project");
52 | }
53 | };
54 |
--------------------------------------------------------------------------------
/src/actions/skillAction.js:
--------------------------------------------------------------------------------
1 | import {
2 | addSkillApi,
3 | getSkillsApi,
4 | deleteSkillApi,
5 | updateSkillApi,
6 | } from "../apis/skillApi";
7 | import { toastSuccess, toastError } from "../shared/toast";
8 |
9 | export const getSkills = () => async (dispatch) => {
10 | try {
11 | const { data } = await getSkillsApi();
12 | dispatch({ type: "GET_SKILLS", payload: data });
13 | } catch (error) {
14 | console.log(error);
15 | }
16 | };
17 |
18 | export const addSkill = (skill) => async (dispatch) => {
19 | try {
20 | const { data } = await addSkillApi(skill);
21 | dispatch({ type: "ADD_SKILL", payload: data });
22 | toastSuccess("Skill Added Successfully");
23 | } catch (error) {
24 | console.log(error);
25 | toastError("Error while adding Skill");
26 | }
27 | };
28 |
29 | export const deleteSkill = (id) => async (dispatch) => {
30 | try {
31 | await deleteSkillApi(id);
32 | dispatch({ type: "DELETE_SKILL", payload: id });
33 | toastSuccess("Skill deleted successfully");
34 | } catch (error) {
35 | console.log(error);
36 | toastError("Error while deleting Skill");
37 | }
38 | };
39 |
40 | export const updateSkill = (id, skill) => async (dispatch) => {
41 | try {
42 | const { data } = await updateSkillApi(id, skill);
43 | dispatch({
44 | type: "UPDATE_SKILL",
45 | payload: { ...skill, _id: data.skill._id },
46 | });
47 | toastSuccess("Skill updated successfully");
48 | } catch (error) {
49 | console.log(error);
50 | toastError("Error while updating Skill");
51 | }
52 | };
53 |
--------------------------------------------------------------------------------
/src/apis/educationApi.js:
--------------------------------------------------------------------------------
1 | import api from "./serverApi";
2 |
3 | export const addEducationApi = (education) => {
4 | return api.post("/educations/", education);
5 | };
6 |
7 | export const getEducationsApi = () => {
8 | return api.get("/educations/");
9 | };
10 |
11 | export const deleteEducationApi = (educationId) => {
12 | return api.delete(`/educations/${educationId}`);
13 | };
14 |
15 | export const updateEducationApi = (educationId, education) => {
16 | return api.put(`/educations/${educationId}`, education);
17 | };
18 |
--------------------------------------------------------------------------------
/src/apis/experienceApi.js:
--------------------------------------------------------------------------------
1 | import api from "./serverApi";
2 |
3 | export const addExperienceApi = (experience) => {
4 | return api.post("/experiences/", experience);
5 | };
6 |
7 | export const getExperiencesApi = () => {
8 | return api.get("/experiences/");
9 | };
10 |
11 | export const deleteExperienceApi = (experienceId) => {
12 | return api.delete(`/experiences/${experienceId}`);
13 | };
14 |
15 | export const updateExperienceApi = (experienceId, experience) => {
16 | return api.put(`/experiences/${experienceId}`, experience);
17 | };
18 |
--------------------------------------------------------------------------------
/src/apis/messageApi.js:
--------------------------------------------------------------------------------
1 | import api from "./serverApi";
2 |
3 | export const getMessagesApi = () => {
4 | return api.get("/messages/", {
5 | headers: {
6 | Authorization: `Bearer ${
7 | JSON.parse(localStorage.getItem("userData")).token
8 | }`,
9 | },
10 | });
11 | };
12 |
13 | export const updateMessageApi = (messageId, message) => {
14 | return api.put(`/messages/${messageId}`, message);
15 | };
16 |
--------------------------------------------------------------------------------
/src/apis/projectApi.js:
--------------------------------------------------------------------------------
1 | import api from "./serverApi";
2 |
3 | export const addProjectApi = (project) => {
4 | return api.post("/projects/", project);
5 | };
6 |
7 | export const getProjectsApi = () => {
8 | return api.get("/projects/");
9 | };
10 |
11 | export const deleteProjectApi = (projectId) => {
12 | return api.delete(`/projects/${projectId}`);
13 | };
14 |
15 | export const updateProjectApi = (projectId, project) => {
16 | return api.put(`/projects/${projectId}`, project);
17 | };
18 |
--------------------------------------------------------------------------------
/src/apis/serverApi.js:
--------------------------------------------------------------------------------
1 | import axios from "axios";
2 |
3 | const api = axios.create({
4 | baseURL: "https://basryback.herokuapp.com",
5 | });
6 |
7 | export const setAuthorizationToken = (token) => {
8 | if (token) {
9 | axios.defaults.headers.common["Authorization"] = `Bearer ${token}`;
10 | } else {
11 | delete axios.defaults.headers.common["Authorization"];
12 | }
13 | };
14 |
15 | export default api;
16 |
17 | export const domainName= "https://basryback.herokuapp.com/";
18 |
--------------------------------------------------------------------------------
/src/apis/skillApi.js:
--------------------------------------------------------------------------------
1 | import api from "./serverApi";
2 |
3 | export const addSkillApi = (skill) => {
4 | return api.post("/skills/", skill);
5 | };
6 |
7 | export const getSkillsApi = () => {
8 | return api.get("/skills/");
9 | };
10 |
11 | export const deleteSkillApi = (skillId) => {
12 | return api.delete(`/skills/${skillId}`);
13 | };
14 |
15 | export const updateSkillApi = (skillId, skill) => {
16 | return api.put(`/skills/${skillId}`, skill);
17 | };
18 |
--------------------------------------------------------------------------------
/src/apis/userApi.js:
--------------------------------------------------------------------------------
1 | import api from "./serverApi";
2 |
3 | export const loginApi = (authData) => {
4 | return api.post("/users/login", authData);
5 | };
6 |
7 |
8 |
--------------------------------------------------------------------------------
/src/assets/images/basry-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/src/assets/images/basry-logo.png
--------------------------------------------------------------------------------
/src/assets/images/dev.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/src/assets/images/dev.png
--------------------------------------------------------------------------------
/src/assets/images/dev.svg:
--------------------------------------------------------------------------------
1 |
2 | image/svg+xml
665 |
--------------------------------------------------------------------------------
/src/assets/images/education.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/src/assets/images/education.png
--------------------------------------------------------------------------------
/src/assets/images/experience.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/src/assets/images/experience.png
--------------------------------------------------------------------------------
/src/assets/images/profil.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/oussamabasry/portfolio-frontend-react/f20796f0cb938cce010cb3a83eb2f75c08139bd0/src/assets/images/profil.png
--------------------------------------------------------------------------------
/src/components/Admin/EducationModal.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useForm } from "react-hook-form";
3 | import { useDispatch } from "react-redux";
4 | import { addEducation, updateEducation } from "../../actions/educationAction";
5 | import DatePicker from "react-datepicker";
6 | import "react-datepicker/dist/react-datepicker.css";
7 |
8 | const EducationModal = ({ id, header, edu, submitValue, colorButton }) => {
9 | const [startDate, setStartDate] = useState(new Date());
10 | const [endDate, setEndDate] = useState(new Date());
11 | const { register, handleSubmit, reset, setValue } = useForm();
12 | const dispatch = useDispatch();
13 |
14 | useEffect(() => {
15 | if (id === "editEducation") {
16 | setValue("title", edu.title);
17 | setValue("school", edu.school);
18 | setValue("city", edu.city);
19 | setStartDate(Date.parse(edu.startDate));
20 | setStartDate(Date.parse(edu.endDate));
21 | }
22 | }, [edu, id, setValue]);
23 |
24 | const onClick = (data) => {
25 | data.startDate = startDate;
26 | data.endDate = endDate;
27 | if (id === "editEducation") {
28 | dispatch(updateEducation(edu._id, data));
29 | } else {
30 | dispatch(addEducation(data));
31 | }
32 | reset();
33 | };
34 | return (
35 |
36 |
43 |
44 |
45 |
46 |
47 | {header}
48 |
49 |
55 |
56 |
133 |
134 |
139 | Close
140 |
141 |
147 | {submitValue}
148 |
149 |
150 |
151 |
152 |
153 |
154 | );
155 | };
156 |
157 | export default EducationModal;
158 |
--------------------------------------------------------------------------------
/src/components/Admin/ExperienceModal.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect, useState } from "react";
2 | import { useForm } from "react-hook-form";
3 | import { useDispatch } from "react-redux";
4 | import {
5 | addExperience,
6 | updateExperience,
7 | } from "../../actions/experienceAction";
8 | import DatePicker from "react-datepicker";
9 | import "react-datepicker/dist/react-datepicker.css";
10 |
11 | const ExperienceModal = ({ id, header, exp, submitValue, colorButton }) => {
12 | const [startDate, setStartDate] = useState(new Date());
13 | const [endDate, setEndDate] = useState(new Date());
14 | const { register, handleSubmit, reset, setValue } = useForm();
15 | const dispatch = useDispatch();
16 |
17 | useEffect(() => {
18 | if (id === "editExperience") {
19 | setValue("title", exp.title);
20 | setValue("company", exp.company);
21 | setValue("city", exp.city);
22 | setStartDate(Date.parse(exp.startDate));
23 | setEndDate(Date.parse(exp.endDate));
24 | setValue("description", exp.description);
25 | setValue("technologies", exp.technologies);
26 | }
27 | }, [exp, id, setValue]);
28 |
29 | const onClick = (data) => {
30 | data.startDate = startDate;
31 | data.endDate = endDate;
32 | if (id === "editExperience") {
33 | dispatch(updateExperience(exp._id, data));
34 | } else {
35 | dispatch(addExperience(data));
36 | }
37 | reset();
38 | };
39 | return (
40 |
41 |
48 |
49 |
50 |
51 |
52 | {header}
53 |
54 |
60 |
61 |
165 |
166 |
171 | Close
172 |
173 |
179 | {submitValue}
180 |
181 |
182 |
183 |
184 |
185 |
186 | );
187 | };
188 |
189 | export default ExperienceModal;
190 |
--------------------------------------------------------------------------------
/src/components/Admin/Modal.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const Modal = ({ id, header, submitValue, colorButton, children, onClick }) => {
4 | return (
5 |
6 |
13 |
14 |
15 |
16 |
17 | {header}
18 |
19 |
25 |
26 |
{children}
27 |
28 |
33 | Close
34 |
35 | onClick()}
40 | >
41 | {submitValue}
42 |
43 |
44 |
45 |
46 |
47 |
48 | );
49 | };
50 |
51 | export default Modal;
52 |
--------------------------------------------------------------------------------
/src/components/Admin/ProjectModal.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { useForm } from "react-hook-form";
3 | import { useDispatch } from "react-redux";
4 | import { addProject, updateProject } from "../../actions/projectAction";
5 |
6 | const ProjectModal = ({ id, header, proj, submitValue, colorButton }) => {
7 | const { register, handleSubmit, reset, setValue } = useForm();
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | if (id === "editProject") {
12 | setValue("title", proj.title);
13 | setValue("description", proj.description);
14 | setValue("technologies", proj.technologies);
15 | setValue("haveLink", proj.haveLink);
16 | setValue("link", proj.link);
17 | }
18 | }, [proj, id, setValue]);
19 |
20 | const onClick = (data) => {
21 | const formData = new FormData();
22 | formData.append("title", data.title);
23 | formData.append("description", data.description);
24 | formData.append("technologies", data.technologies);
25 | formData.append("haveLink", data.haveLink);
26 | formData.append("link", data.link);
27 | formData.append("projectImage", data.projectImage[0]);
28 | if (id === "editProject") {
29 | dispatch(updateProject(proj._id, formData));
30 | } else {
31 | dispatch(addProject(formData));
32 | }
33 | reset();
34 | };
35 | return (
36 |
37 |
44 |
45 |
46 |
47 |
48 | {header}
49 |
50 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Title
67 |
68 |
75 |
76 |
77 |
78 |
79 |
80 | Description
81 |
89 |
90 |
91 |
92 |
93 |
94 |
95 | Technologies
96 |
97 |
104 |
105 |
106 |
107 |
108 |
109 |
110 | Have Link ?
111 |
112 |
119 | Yes
120 | No
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 | Link
129 |
130 |
137 |
138 |
139 |
140 |
141 |
142 | Image
143 |
144 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
167 | Close
168 |
169 |
175 | {submitValue}
176 |
177 |
178 |
179 |
180 |
181 |
182 | );
183 | };
184 |
185 | export default ProjectModal;
186 |
--------------------------------------------------------------------------------
/src/components/Admin/SideBar/SideBar.css:
--------------------------------------------------------------------------------
1 | .sidebar {
2 | background-color: #060b26;
3 | height: 80px;
4 | display: flex;
5 | justify-content: flex-start;
6 | align-items: center;
7 | }
8 |
9 | .menu-bars {
10 | margin-left: 2rem;
11 | font-size: 2rem;
12 | background: none;
13 | }
14 |
15 | .nav-menu {
16 | background-color: #060b26;
17 | width: 250px;
18 | height: 100vh;
19 | display: flex;
20 | justify-content: center;
21 | position: fixed;
22 | top: 0;
23 | left: -100%;
24 | transition: 850ms;
25 | }
26 |
27 | .nav-menu.active {
28 | left: 0;
29 | transition: 350ms;
30 | }
31 |
32 | .nav-text {
33 | display: flex;
34 | justify-content: flex-start;
35 | align-items: center;
36 | padding: 8px 0px 8px 16px;
37 | list-style: none;
38 | height: 60px;
39 | }
40 |
41 | .nav-text a {
42 | text-decoration: none;
43 | color: #f5f5f5;
44 | font-size: 18px;
45 | width: 95%;
46 | height: 100%;
47 | display: flex;
48 | align-items: center;
49 | padding: 0 16px;
50 | border-radius: 4px;
51 | }
52 |
53 | .nav-text a:hover {
54 | background-color: #1a83ff;
55 | }
56 |
57 | .nav-menu-items {
58 | width: 100%;
59 | }
60 |
61 | .sidebar-toggle {
62 | background-color: #060b26;
63 | width: 100%;
64 | height: 80px;
65 | display: flex;
66 | justify-content: flex-start;
67 | align-items: center;
68 | }
69 |
70 | span {
71 | margin-left: 16px;
72 | }
73 |
--------------------------------------------------------------------------------
/src/components/Admin/SideBar/SideBar.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useDispatch } from "react-redux";
3 | import { useHistory } from "react-router-dom";
4 | import { logoutUser } from "../../../actions/loginAction";
5 | import * as FaIcons from "react-icons/fa";
6 | import * as AiIcons from "react-icons/ai";
7 | import { Link } from "react-router-dom";
8 | import { SidebarData } from "./SidebarData";
9 | import "./SideBar.css";
10 | import { IconContext } from "react-icons";
11 |
12 | function SideBar() {
13 | const [sidebar, setSidebar] = useState(false);
14 | const dispatch = useDispatch();
15 | const history = useHistory();
16 | const showSidebar = () => setSidebar(!sidebar);
17 |
18 | const onLogout = () => {
19 | dispatch(logoutUser());
20 | history.push("/");
21 | };
22 |
23 | return (
24 | <>
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | {SidebarData.map((item, index) => {
39 | return (
40 |
41 | {item.title === "Logout" ? (
42 |
43 | {item.icon}
44 | {item.title}
45 |
46 | ) : (
47 |
48 | {item.icon}
49 | {item.title}
50 |
51 | )}
52 |
53 | );
54 | })}
55 |
56 |
57 |
58 | >
59 | );
60 | }
61 |
62 | export default SideBar;
63 |
--------------------------------------------------------------------------------
/src/components/Admin/SideBar/SidebarData.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import * as FaIcons from "react-icons/fa";
3 | import * as AiIcons from "react-icons/ai";
4 | import * as IoIcons from "react-icons/io";
5 | import * as FcIcons from "react-icons/fc";
6 | import * as GoIcons from "react-icons/go";
7 |
8 | export const SidebarData = [
9 | {
10 | title: "Home",
11 | path: "/",
12 | icon: ,
13 | cName: "nav-text",
14 | },
15 | {
16 | title: "Educations",
17 | path: "/education",
18 | icon: ,
19 | cName: "nav-text",
20 | },
21 | {
22 | title: "Experiences",
23 | path: "/experience",
24 | icon: ,
25 | cName: "nav-text",
26 | },
27 | {
28 | title: "Skills",
29 | path: "/skill",
30 | icon: ,
31 | cName: "nav-text",
32 | },
33 | {
34 | title: "Projects",
35 | path: "/project",
36 | icon: ,
37 | cName: "nav-text",
38 | },
39 | {
40 | title: "Messages",
41 | path: "/messages",
42 | icon: ,
43 | cName: "nav-text",
44 | },
45 | {
46 | title: "Logout",
47 | path: "/support",
48 | icon: ,
49 | cName: "nav-text",
50 | },
51 | ];
52 |
--------------------------------------------------------------------------------
/src/components/Admin/SkillModal.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { useForm } from "react-hook-form";
3 | import { useDispatch } from "react-redux";
4 | import { addSkill, updateSkill } from "../../actions/skillAction";
5 |
6 | const SkillModal = ({ id, header, skil, submitValue, colorButton }) => {
7 | const {
8 | register,
9 | handleSubmit,
10 | reset,
11 | setValue,
12 | } = useForm();
13 | const dispatch = useDispatch();
14 |
15 | useEffect(() => {
16 | if (id === "editSkill") {
17 | setValue("type", skil.type);
18 | setValue("level", skil.level);
19 | }
20 | }, [skil, id, setValue]);
21 |
22 | const onClick = (data) => {
23 | if (id === "editSkill") {
24 | dispatch(updateSkill(skil._id, data));
25 | } else {
26 | dispatch(addSkill(data));
27 | }
28 | reset();
29 | };
30 | return (
31 |
32 |
39 |
40 |
41 |
42 |
43 | {header}
44 |
45 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 | Name
62 |
63 |
70 |
71 |
72 |
73 |
74 |
75 |
76 | Level
77 |
78 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
99 | Close
100 |
101 |
107 | {submitValue}
108 |
109 |
110 |
111 |
112 |
113 |
114 | );
115 | };
116 |
117 | export default SkillModal;
118 |
--------------------------------------------------------------------------------
/src/components/Admin/Table.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import moment from "moment";
3 | import { domainName } from "../../apis/serverApi";
4 |
5 | function Table({
6 | title,
7 | tableData,
8 | headerText,
9 | headerProprities,
10 | idModalEdit,
11 | idModalAdd,
12 | ondelteClick,
13 | onEditClick,
14 | }) {
15 | const dataRows = tableData.map((data) => {
16 | return (
17 |
18 | {headerProprities.map((prop, index) => {
19 | return (
20 |
21 | {(prop === "startDate" || prop === "endDate") &&
22 | moment(data[prop]).format("MMM YYYY")}
23 | {prop === "projectImage" && (
24 |
29 | )}
30 | {!(
31 | prop === "startDate" ||
32 | prop === "endDate" ||
33 | prop === "projectImage"
34 | ) && data[prop]}
35 |
36 | );
37 | })}
38 |
39 | ondelteClick(data)}
43 | >
44 | Delete
45 |
46 |
47 |
48 | onEditClick(data)}
54 | >
55 | Edit
56 |
57 |
58 |
59 | );
60 | });
61 |
62 | const headersRow = headerText.map((head, index) => {
63 | return (
64 |
65 | {head}
66 |
67 | );
68 | });
69 |
70 | return (
71 |
72 |
73 |
{title}
74 |
80 | Add {title}
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 | {headersRow}
89 |
90 |
91 |
92 |
93 | {dataRows}
94 |
95 |
96 |
97 | );
98 | }
99 |
100 | export default Table;
101 |
--------------------------------------------------------------------------------
/src/components/User/About/About.css:
--------------------------------------------------------------------------------
1 |
2 |
3 | .text-font{
4 | font-size: large;
5 | font-family: 'Itim', cursive;
6 | text-align: justify;
7 | }
--------------------------------------------------------------------------------
/src/components/User/About/About.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import profil from "../../../assets/images/profil.png";
3 | import "./About.css";
4 |
5 | const About = ({ reff }) => {
6 | return (
7 |
13 |
14 |
15 | About Me
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | I am an engineering student in the second year of Software
25 | Engineering and Distributed Computer Systems at the Higher Normal
26 | School of Technical Education Mohammedia (ENSET-M).
27 | Through my university studies, I acquired strong skills in
28 | the field of software engineering. The various projects and
29 | internships that I have carried out have enabled me to develop not
30 | only my hard skills but also my soft skills.
31 | Motivated, I know how to adapt, I have a sense of
32 | responsibility and organization.
33 |
34 |
35 |
36 |
37 |
38 | );
39 | };
40 |
41 | export default About;
42 |
--------------------------------------------------------------------------------
/src/components/User/Contacts/Contacts.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useForm } from "react-hook-form";
3 | import { toast } from "react-toastify";
4 | import serverApi from "../../../apis/serverApi";
5 | import { getMessages } from "../../../actions/messageAction";
6 | import { useDispatch } from "react-redux";
7 |
8 | const Contacts = ({ reff }) => {
9 | const {
10 | register,
11 | handleSubmit,
12 | reset,
13 | formState: { errors },
14 | } = useForm();
15 |
16 | const [loading, setloading] = useState(false);
17 |
18 | const dispatch = useDispatch();
19 |
20 | const onSubmit = async (data) => {
21 | try {
22 | setloading(true);
23 | await serverApi.post("/messages", data);
24 | setloading(false);
25 | toast.success("Sent Successfully", {
26 | position: "top-right",
27 | autoClose: 7000,
28 | hideProgressBar: false,
29 | closeOnClick: true,
30 | pauseOnHover: true,
31 | draggable: true,
32 | progress: undefined,
33 | });
34 | reset();
35 | dispatch(getMessages());
36 | } catch (error) {
37 | toast.error("an error happened while sending the message", {
38 | position: "top-right",
39 | autoClose: 7000,
40 | hideProgressBar: false,
41 | closeOnClick: true,
42 | pauseOnHover: true,
43 | draggable: true,
44 | progress: undefined,
45 | });
46 | }
47 | setloading(false);
48 | };
49 |
50 |
51 | return (
52 |
240 | );
241 | };
242 |
243 | export default Contacts;
244 |
--------------------------------------------------------------------------------
/src/components/User/Education/Education.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import educationImage from "../../../assets/images/education.png";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import { getEducations } from "../../../actions/educationAction";
5 | import moment from "moment";
6 |
7 | const Education = ({ reff }) => {
8 | const dispatch = useDispatch();
9 | const educations = useSelector((state) => state.educations);
10 |
11 | useEffect(() => {
12 | dispatch(getEducations());
13 | }, [dispatch]);
14 |
15 | const education = educations.map((edu) => {
16 | return (
17 |
18 |
19 | {edu.title}
20 |
21 |
22 | {edu.school}, {edu.city}
23 |
24 |
25 | {moment(edu.startDate).format("MMM YYYY")} -{" "}
26 | {moment(edu.endDate).format("MMM YYYY")}
27 |
28 |
29 |
30 | );
31 | });
32 |
33 | return (
34 |
40 |
41 |
42 | Education
43 |
44 |
45 |
46 |
47 |
52 |
53 |
54 |
57 |
58 |
59 |
60 | );
61 | };
62 |
63 | export default Education;
64 |
--------------------------------------------------------------------------------
/src/components/User/Experience/Experience.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import experienceImage from "../../../assets/images/experience.png";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import { getExperiences } from "../../../actions/experienceAction";
5 | import moment from "moment";
6 |
7 | const Experience = ({ reff }) => {
8 | const experiences = useSelector((state) => state.experiences);
9 | const dispatch = useDispatch();
10 |
11 | useEffect(() => {
12 | dispatch(getExperiences());
13 | }, [dispatch]);
14 |
15 | const experience = experiences.map((exp) => {
16 | return (
17 |
18 |
19 |
20 |
21 | {exp.title}
22 |
23 |
24 | {exp.company}, {exp.city}
25 |
26 |
27 | {moment(exp.startDate).format("MMM YYYY")} -{" "}
28 | {moment(exp.endDate).format("MMM YYYY")}
29 |
30 |
31 | {exp.description}
32 |
33 |
41 | Technologies:
42 |
43 |
44 | {" "}
45 | {exp.technologies}
46 |
47 |
48 |
49 |
50 |
51 | );
52 | });
53 |
54 | return (
55 |
61 |
62 |
63 | Experience
64 |
65 |
66 |
67 |
68 |
73 |
74 |
75 |
78 |
79 |
80 |
81 | );
82 | };
83 |
84 | export default Experience;
85 |
--------------------------------------------------------------------------------
/src/components/User/Footer/Footer.css:
--------------------------------------------------------------------------------
1 | .foot {
2 | background-color: #282d32;
3 | }
4 |
5 | .title {
6 | color: white;
7 | }
8 |
9 | .sub-title {
10 | color: #dddddd;
11 | }
12 |
13 | .icons-color {
14 | color: #fff;
15 | }
16 |
17 | .link-hover :hover {
18 | color: #00ffd4;
19 | }
20 |
--------------------------------------------------------------------------------
/src/components/User/Footer/Footer.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./Footer.css";
3 | import { Link } from "react-router-dom";
4 |
5 | const Footer = ({ onLinkClick }) => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 | Oussama Basry
13 |
14 |
15 | I am an engineering student in the second year of Software
16 | Engineering and Distributed Computer Systems at the Higher Normal
17 | School of Technical Education Mohammedia (ENSET-M).
18 |
19 |
20 |
21 |
22 |
23 | Liens
24 |
25 |
26 | onLinkClick("educationSection")}
28 | className="sub-title"
29 | style={{ textDecoration: "none" }}
30 | to="/"
31 | >
32 | {" "}
33 | Education
34 |
35 |
36 |
37 | onLinkClick("experienceSection")}
39 | to="/"
40 | className="sub-title"
41 | style={{ textDecoration: "none" }}
42 | >
43 | Experience
44 |
45 |
46 |
47 |
48 | onLinkClick("projectSection")}
50 | to="/"
51 | className="sub-title"
52 | style={{ textDecoration: "none" }}
53 | >
54 | Projects
55 |
56 |
57 |
58 | onLinkClick("skillSection")}
60 | to="/"
61 | className="sub-title"
62 | style={{ textDecoration: "none" }}
63 | >
64 | Skills
65 |
66 |
67 |
68 |
69 |
70 |
71 | Contact
72 |
73 |
Morocco, Casablanca, Mohammedia
74 |
obasry@gmail.com
75 |
+212 6 50 56 38 27
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 | {" "}
85 | Copyright ©2021 All rights reserved by:
86 |
87 | Oussama Basry
88 |
89 |
90 |
91 |
92 |
142 |
143 |
144 |
145 | );
146 | };
147 |
148 | export default Footer;
149 |
--------------------------------------------------------------------------------
/src/components/User/Login/Login.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./styles.css";
3 | import { useForm } from "react-hook-form";
4 | import { useDispatch } from "react-redux";
5 | import { loginUser } from "../../../actions/loginAction";
6 |
7 | const Login = () => {
8 | const {
9 | register,
10 | handleSubmit,
11 | formState: { errors },
12 | } = useForm();
13 |
14 | const dispatch = useDispatch();
15 |
16 | const onSubmit = async (data) => {
17 | dispatch(loginUser(data));
18 | };
19 |
20 | return (
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | Login only for administrator !{" "}
30 |
31 |
32 |
33 |
43 | {errors.email && errors.email.type === "required" && (
44 |
45 | You must enter your email
46 |
47 | )}
48 | {errors.email && errors.email.type === "pattern" && (
49 |
50 | You must enter a valid email
51 |
52 | )}
53 |
54 |
55 |
56 |
65 |
66 | {errors.password &&
67 | errors.password.type === "required" && (
68 |
69 | You must enter your password
70 |
71 | )}
72 |
73 |
74 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 | );
88 | };
89 |
90 | export default Login;
91 |
--------------------------------------------------------------------------------
/src/components/User/Login/styles.css:
--------------------------------------------------------------------------------
1 | :root {
2 | --input-padding-x: 1.5rem;
3 | --input-padding-y: 0.75rem;
4 | }
5 |
6 |
7 |
8 | .btn-login {
9 | font-size: 0.9rem;
10 | letter-spacing: 0.05rem;
11 | padding: 0.75rem 1rem;
12 | border-radius: 2rem;
13 | }
14 |
15 | .form-label-group {
16 | position: relative;
17 | margin-bottom: 1rem;
18 | }
19 |
20 | .form-label-group > input,
21 | .form-label-group > label {
22 | padding: var(--input-padding-y) var(--input-padding-x);
23 | height: auto;
24 | border-radius: 2rem;
25 | }
26 |
27 |
--------------------------------------------------------------------------------
/src/components/User/Navbar/Navbar.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import "../Navbar/styles.css";
3 | import logo from "../../../assets/images/basry-logo.png";
4 | import { Link } from "react-router-dom";
5 |
6 | const Navbar = ({ onLinkClick }) => {
7 | const [toggler, setToggler] = useState(true);
8 | const [navbar, setNavbar] = useState(false);
9 |
10 | const changeBackground = () => {
11 | window.scrollY > window.screen.height - window.screen.height * 0.27
12 | ? setNavbar(true)
13 | : setNavbar(false);
14 | };
15 | window.addEventListener("scroll", changeBackground);
16 |
17 | return (
18 |
23 |
24 |
25 |
26 |
27 |
setToggler(!toggler)}
36 | >
37 |
41 |
42 |
43 |
44 |
45 | onLinkClick("homeSection")}
47 | className="nav-link"
48 | to="/"
49 | >
50 | Home
51 |
52 |
53 |
54 | onLinkClick("aboutSection")}
56 | className="nav-link"
57 | to="/"
58 | data-offset="90"
59 | >
60 | About
61 |
62 |
63 |
64 | onLinkClick("educationSection")}
66 | className="nav-link"
67 | to="/"
68 | data-offset="90"
69 | >
70 | Education
71 |
72 |
73 |
74 | onLinkClick("experienceSection")}
76 | className="nav-link"
77 | to="/"
78 | data-offset="90"
79 | >
80 | Experience
81 |
82 |
83 |
84 | onLinkClick("skillSection")}
87 | className="nav-link"
88 | data-offset="30"
89 | >
90 | Skills
91 |
92 |
93 |
94 | onLinkClick("projectSection")}
96 | className="nav-link"
97 | to="/"
98 | data-offset="90"
99 | >
100 | Projects
101 |
102 |
103 |
104 | onLinkClick("contactSection")}
106 | className="nav-link"
107 | data-offset="90"
108 | to="/"
109 | >
110 | Contact
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 | Sign In
119 |
120 |
121 |
122 |
123 |
124 |
125 | );
126 | };
127 |
128 | export default Navbar;
129 |
--------------------------------------------------------------------------------
/src/components/User/Navbar/styles.css:
--------------------------------------------------------------------------------
1 |
2 | .rgba-gradient-nav {
3 | background-color: #eee !important;
4 | }
5 | .navbar-scroll-color{
6 | background-color: #fff !important;
7 | }
8 |
9 | .rgba-gradient {
10 | background-color: #eee;
11 | }
12 |
13 |
14 | ul li {
15 | text-align: center;
16 | padding: 0 10px 0 0;
17 | }
18 |
19 | ul li a {
20 | color: #fff !important;
21 | font-size: 13px;
22 | }
23 |
24 | .navbar .navbar-nav > li > a:hover,
25 | .navbar-default .navbar-nav > li > a:focus {
26 | color: #0275d8 !important;
27 | }
28 |
29 | .navbar {
30 | background-color: #fff;
31 | }
32 |
33 | .nav-link,
34 | .navbar-brand {
35 | color: black !important;
36 | font-size: 15px;
37 | }
38 |
39 |
--------------------------------------------------------------------------------
/src/components/User/PageIntro/PageIntro.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import "./styles.css";
3 | import homeImage from "../../../assets/images/dev.svg";
4 |
5 | const PageIntro = ({ reff }) => {
6 | return (
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 | Full Stack Web Developper
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 | );
46 | };
47 |
48 | export default PageIntro;
49 |
--------------------------------------------------------------------------------
/src/components/User/PageIntro/styles.css:
--------------------------------------------------------------------------------
1 | .rgba-gradient {
2 | background-color: #eee;
3 | }
4 |
5 | .header {
6 | height: 100vh;
7 | color: #fff;
8 | }
9 |
10 | .dev-img {
11 | animation: move 2.5s ease-in-out infinite;
12 | }
13 |
14 | @keyframes move {
15 | 0%,
16 | 100% {
17 | transform: translateY(0);
18 | }
19 | 50% {
20 | transform: translateY(40px);
21 | transform: translateX(20px);
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/components/User/Projects/Projects.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { getprojects } from "../../../actions/projectAction";
4 | import ModalImage from "react-modal-image";
5 | import { domainName } from "../../../apis/serverApi";
6 |
7 | const Projects = ({ reff }) => {
8 | const projects = useSelector((state) => state.projects);
9 | const dispatch = useDispatch();
10 |
11 | useEffect(() => {
12 | dispatch(getprojects());
13 | }, [dispatch]);
14 |
15 | const project = projects.map((proj) => {
16 | return (
17 |
21 |
22 |
27 |
28 |
29 |
{proj.title}
30 |
{proj.description}
31 |
39 | Technologies:
40 |
41 |
{proj.technologies}
42 | {proj.haveLink && (
43 |
49 | View project code
50 |
51 | )}
52 |
53 |
54 | );
55 | });
56 |
57 | return (
58 |
64 |
65 |
Projects
66 |
67 |
{project}
68 |
69 |
70 | );
71 | };
72 |
73 | export default Projects;
74 |
--------------------------------------------------------------------------------
/src/components/User/Skills/Skills.js:
--------------------------------------------------------------------------------
1 | import React, { useEffect } from "react";
2 | import SkillBar from "react-skillbars";
3 | import { useSelector, useDispatch } from "react-redux";
4 | import { getSkills } from "../../../actions/skillAction";
5 |
6 | const Skills = ({ reff }) => {
7 | const skills = useSelector((state) => state.skills);
8 | const dispatch = useDispatch();
9 |
10 | useEffect(() => {
11 | dispatch(getSkills());
12 | }, [dispatch]);
13 |
14 | const colors = {
15 | bar: "#3498db",
16 | title: {
17 | text: "#fff",
18 | background: "#2980b9",
19 | },
20 | };
21 |
22 | return (
23 |
24 |
25 |
26 | Skills
27 |
28 |
33 |
34 |
35 | );
36 | };
37 |
38 | export default Skills;
39 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin: 0;
3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen",
4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue",
5 | sans-serif;
6 | -webkit-font-smoothing: antialiased;
7 | -moz-osx-font-smoothing: grayscale;
8 | background-color: #eeeeee;
9 | }
10 |
11 | code {
12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
13 | monospace;
14 | }
15 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom";
3 | import "./index.css";
4 | import "react-toastify/dist/ReactToastify.css";
5 | import App from "./App";
6 | import reportWebVitals from "./reportWebVitals";
7 | import { createStore, applyMiddleware, compose } from "redux";
8 | import { Provider } from "react-redux";
9 | import allReducers from "./reducers";
10 | import thunk from "redux-thunk";
11 | import { getEducations } from "./actions/educationAction";
12 | import { getExperiences } from "./actions/experienceAction";
13 | import { getprojects } from "./actions/projectAction";
14 | import { getSkills } from "./actions/skillAction";
15 | import api from "./apis/serverApi";
16 |
17 | const composeEnhancer = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ || compose;
18 |
19 | const store = createStore(allReducers, composeEnhancer(applyMiddleware(thunk)));
20 | store.dispatch(getEducations());
21 | store.dispatch(getExperiences());
22 | store.dispatch(getprojects());
23 | store.dispatch(getSkills());
24 |
25 | const user = JSON.parse(localStorage.getItem("userData"));
26 |
27 | if (user !== null) {
28 | api.defaults.headers.common["Authorization"] = `Bearer ${user.token}`;
29 | }
30 |
31 | ReactDOM.render(
32 |
33 |
34 |
35 |
36 | ,
37 | document.getElementById("root")
38 | );
39 |
40 | // If you want to start measuring performance in your app, pass a function
41 | // to log results (for example: reportWebVitals(console.log))
42 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
43 | reportWebVitals();
44 |
--------------------------------------------------------------------------------
/src/pages/EducationAdmin.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { deleteEducation } from "../actions/educationAction";
4 | import EducationModal from "../components/Admin/EducationModal";
5 | import Table from "../components/Admin/Table";
6 |
7 | function EducationAdmin() {
8 | const educations = useSelector((state) => state.educations);
9 | const dispatch = useDispatch();
10 | const [selectedEduca, setSelectedEduca] = useState({
11 | title: "",
12 | school: "",
13 | city: "",
14 | startDate: "",
15 | endDate: "",
16 | });
17 |
18 | const ondelteClick = (education) => {
19 | dispatch(deleteEducation(education._id));
20 | };
21 |
22 | const onEditClick = (data) => {
23 | setSelectedEduca(data);
24 | };
25 |
26 | return (
27 |
54 | );
55 | }
56 |
57 | export default EducationAdmin;
58 |
--------------------------------------------------------------------------------
/src/pages/ExperienceAdmin.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { deleteExperience } from "../actions/experienceAction";
4 | import ExperienceModal from "../components/Admin/ExperienceModal";
5 | import Table from "../components/Admin/Table";
6 |
7 | const ExperienceAdmin = () => {
8 | const experiences = useSelector((state) => state.experiences);
9 | const dispatch = useDispatch();
10 | const [selectedExperience, setSelectedExperience] = useState({
11 | title: "",
12 | company: "",
13 | city: "",
14 | startDate: "",
15 | endDate: "",
16 | description: "",
17 | technologies: "",
18 | });
19 |
20 | const ondelteClick = (experience) => {
21 | dispatch(deleteExperience(experience._id));
22 | };
23 |
24 | const onEditClick = (experience) => {
25 | setSelectedExperience(experience);
26 | };
27 |
28 | return (
29 |
73 | );
74 | };
75 |
76 | export default ExperienceAdmin;
77 |
--------------------------------------------------------------------------------
/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 |
3 | function Home() {
4 | return (
5 |
6 |
Home
7 |
8 | );
9 | }
10 |
11 | export default Home;
12 |
--------------------------------------------------------------------------------
/src/pages/MessageAdmin.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import moment from "moment";
4 | import { updateMessage, getMessages } from "../actions/messageAction";
5 |
6 | const MessageAdmin = () => {
7 | const messages = useSelector((state) => state.messages);
8 | const dispatch = useDispatch();
9 | const [selectedMessage, setSelectedMessage] = useState(null);
10 |
11 | useEffect(() => {
12 | dispatch(getMessages());
13 | }, [dispatch]);
14 |
15 | const onMessageClick = (message) => {
16 | if (!message.isSeen) {
17 | dispatch(updateMessage(message._id, { ...message, isSeen: true }));
18 | }
19 | setSelectedMessage(message);
20 | };
21 |
22 | const listMessages = messages.map((msg) => {
23 | return (
24 | onMessageClick(msg)}
30 | >
31 |
32 |
{msg.subject}
33 | {moment(msg.date).format("MMMM Do YYYY, h:mm:ss a")}
34 |
35 |
36 | {msg.message.substring(0, 100)}
37 | {" . . ."}
38 |
39 |
See More
40 |
41 |
42 | Send by:
43 | {msg.name}
44 |
45 | {!msg.isSeen && (
46 |
1
47 | )}
48 |
49 |
50 | );
51 | });
52 | return (
53 |
54 |
55 |
56 |
Message Details
57 | {selectedMessage && (
58 |
62 |
63 |
From:
64 | {selectedMessage.name}
65 |
66 | Date:
67 | {moment(selectedMessage.date).format(
68 | "MMMM Do YYYY, h:mm:ss a"
69 | )}
70 |
71 |
72 |
73 |
74 |
Email:
75 |
{selectedMessage.email}
76 |
77 |
78 | Subject:
79 |
80 | {selectedMessage.subject}
81 |
82 |
83 |
84 |
Message:
85 |
{selectedMessage.message}
86 |
87 |
88 |
89 | )}
90 |
91 |
92 |
Conversations
93 |
{listMessages}
94 |
95 |
96 |
97 | );
98 | };
99 |
100 | export default MessageAdmin;
101 |
--------------------------------------------------------------------------------
/src/pages/NotFound.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 |
3 | const NotFound = () => {
4 | return (
5 |
6 |
Error 404 :{" "}Page Not Found
7 |
8 | );
9 | };
10 |
11 | export default NotFound;
12 |
--------------------------------------------------------------------------------
/src/pages/PortfolioUI.js:
--------------------------------------------------------------------------------
1 | import React, { useState, useEffect, useRef } from "react";
2 | import Contacts from "../components/User/Contacts/Contacts";
3 | import Education from "../components/User/Education/Education";
4 | import Experience from "../components/User/Experience/Experience";
5 | import About from "../components/User/About/About";
6 | import Footer from "../components/User/Footer/Footer";
7 | import PageIntro from "../components/User/PageIntro/PageIntro";
8 | import Projects from "../components/User/Projects/Projects";
9 | import Skills from "../components/User/Skills/Skills";
10 | import isLogin from "../shared/authorization";
11 | import { useSelector } from "react-redux";
12 | import Navbar from "../components/User/Navbar/Navbar";
13 |
14 | const PortfolioUI = () => {
15 | const [isLogged, setIsLogged] = useState(isLogin);
16 | const login = useSelector((state) => state.login.isLogin);
17 | const projectSection = useRef(null);
18 | const educationSection = useRef(null);
19 | const experienceSection = useRef(null);
20 | const skillSection = useRef(null);
21 | const contactSection = useRef(null);
22 | const homeSection = useRef(null);
23 | const aboutSection = useRef(null);
24 |
25 | useEffect(() => {
26 | setIsLogged(isLogin);
27 | }, [login]);
28 |
29 | const scrollUtil = (section) => {
30 | window.scrollTo({
31 | top: section.current.offsetTop,
32 | behavior: "smooth",
33 | });
34 | };
35 | const onLinkClick = (section) => {
36 | switch (section) {
37 | case "projectSection":
38 | scrollUtil(projectSection);
39 | break;
40 | case "aboutSection":
41 | scrollUtil(aboutSection);
42 | break;
43 | case "educationSection":
44 | scrollUtil(educationSection);
45 | break;
46 | case "experienceSection":
47 | scrollUtil(experienceSection);
48 | break;
49 | case "skillSection":
50 | scrollUtil(skillSection);
51 | break;
52 | case "contactSection":
53 | scrollUtil(contactSection);
54 | break;
55 | case "homeSection":
56 | scrollUtil(homeSection);
57 | break;
58 | default:
59 | scrollUtil(homeSection);
60 | }
61 | };
62 |
63 | return (
64 |
65 | {!isLogged &&
}
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 | );
76 | };
77 |
78 | export default PortfolioUI;
79 |
--------------------------------------------------------------------------------
/src/pages/ProjectAdmin.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { deleteProject } from "../actions/projectAction";
4 | import ProjectModal from "../components/Admin/ProjectModal";
5 | import Table from "../components/Admin/Table";
6 |
7 | function ProjectAdmin() {
8 | const projects = useSelector((state) => state.projects);
9 | const dispatch = useDispatch();
10 | const [selectedProject, setSelectedProject] = useState({
11 | title: "",
12 | description: "",
13 | technologies: "",
14 | link: "",
15 | projectImage: "",
16 | });
17 |
18 | const ondelteClick = (project) => {
19 | dispatch(deleteProject(project._id));
20 | };
21 |
22 | const onEditClick = (data) => {
23 | setSelectedProject(data);
24 | };
25 |
26 | return (
27 |
54 | );
55 | }
56 |
57 | export default ProjectAdmin;
58 |
--------------------------------------------------------------------------------
/src/pages/SkillAdmin.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { useSelector, useDispatch } from "react-redux";
3 | import { deleteSkill } from "../actions/skillAction";
4 | import SkillModal from "../components/Admin/SkillModal";
5 | import Table from "../components/Admin/Table";
6 |
7 | const SkillAdmin = () => {
8 | const skills = useSelector((state) => state.skills);
9 | const dispatch = useDispatch();
10 | const [selectedSkill, setSelectedSkill] = useState({
11 | type: "",
12 | level: 0,
13 | });
14 |
15 | const ondelteClick = (skill) => {
16 | dispatch(deleteSkill(skill._id));
17 | };
18 |
19 | const onEditClick = (skill) => {
20 | setSelectedSkill(skill);
21 | };
22 |
23 | return (
24 |
52 | );
53 | };
54 |
55 | export default SkillAdmin;
56 |
--------------------------------------------------------------------------------
/src/reducers/education.js:
--------------------------------------------------------------------------------
1 | const educationReducer = (state = [], action) => {
2 | switch (action.type) {
3 | case "GET_EDUCATIONS":
4 | return action.payload;
5 |
6 | case "ADD_EDUCATION":
7 | return [...state, action.payload];
8 |
9 | case "DELETE_EDUCATION":
10 | return state.filter((edu) => edu._id !== action.payload);
11 |
12 | case "UPDATE_EDUCATION":
13 | return state.map((edu) =>
14 | edu._id === action.payload._id ? action.payload : edu
15 | );
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default educationReducer;
22 |
--------------------------------------------------------------------------------
/src/reducers/experience.js:
--------------------------------------------------------------------------------
1 | const experienceReducer = (state = [], action) => {
2 | switch (action.type) {
3 | case "GET_EXPERIENCES":
4 | return action.payload;
5 |
6 | case "ADD_EXEPERIENCE":
7 | return [...state, action.payload];
8 |
9 | case "DELETE_EXEPERIENCE":
10 | return state.filter((exp) => exp._id !== action.payload);
11 |
12 | case "UPDATE_EXEPERIENCE":
13 | return state.map((exp) =>
14 | exp._id === action.payload._id ? action.payload : exp
15 | );
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default experienceReducer;
22 |
--------------------------------------------------------------------------------
/src/reducers/index.js:
--------------------------------------------------------------------------------
1 | import loggedReducer from "./isLogged";
2 | import educationReducer from "./education";
3 | import experienceReducer from "./experience";
4 | import skillReducer from "./skill";
5 | import projectReducer from "./project";
6 | import messageReducer from "./message";
7 | import { combineReducers } from "redux";
8 |
9 | const allReducers = combineReducers({
10 | educations: educationReducer,
11 | experiences: experienceReducer,
12 | skills: skillReducer,
13 | projects: projectReducer,
14 | messages: messageReducer,
15 | login: loggedReducer,
16 | });
17 |
18 | export default allReducers;
19 |
--------------------------------------------------------------------------------
/src/reducers/isLogged.js:
--------------------------------------------------------------------------------
1 | const loggedReducer = (
2 | state = { isLogin: localStorage.getItem("isLogged"), token: localStorage.getItem("token") },
3 | action
4 | ) => {
5 | switch (action.type) {
6 | case "SIGN_IN":
7 | return {
8 | ...state,
9 | isLogin: action.payload.isLogin,
10 | token: action.payload.token,
11 | };
12 | case "LOGOUT":
13 | return {
14 | ...state,
15 | isLogin: action.payload.isLogin,
16 | token: action.payload.token,
17 | };
18 | default:
19 | return state;
20 | }
21 | };
22 |
23 | export default loggedReducer;
24 |
--------------------------------------------------------------------------------
/src/reducers/message.js:
--------------------------------------------------------------------------------
1 | const messageReducer = (state = [], action) => {
2 | switch (action.type) {
3 | case "GET_MESSAGES":
4 | return action.payload;
5 | case "UPDATE_MESSAGE":
6 | return state.map((msg) =>
7 | msg._id === action.payload._id ? action.payload : msg
8 | );
9 | default:
10 | return state;
11 | }
12 | };
13 |
14 | export default messageReducer;
15 |
--------------------------------------------------------------------------------
/src/reducers/project.js:
--------------------------------------------------------------------------------
1 | const projectReducer = (state = [], action) => {
2 | switch (action.type) {
3 | case "GET_PROJECTS":
4 | return action.payload;
5 |
6 | case "ADD_PROJECT":
7 | return [...state, action.payload];
8 |
9 | case "DELETE_PROJECT":
10 | return state.filter((proj) => proj._id !== action.payload);
11 |
12 | case "UPDATE_PROJECT":
13 | return state.map((proj) =>
14 | proj._id === action.payload._id ? action.payload : proj
15 | );
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default projectReducer;
22 |
--------------------------------------------------------------------------------
/src/reducers/skill.js:
--------------------------------------------------------------------------------
1 | const skillReducer = (state = [], action) => {
2 | switch (action.type) {
3 | case "GET_SKILLS":
4 | return action.payload;
5 |
6 | case "ADD_SKILL":
7 | return [...state, action.payload];
8 |
9 | case "DELETE_SKILL":
10 | return state.filter((ski) => ski._id !== action.payload);
11 |
12 | case "UPDATE_SKILL":
13 | return state.map((ski) =>
14 | ski._id === action.payload._id ? action.payload : ski
15 | );
16 | default:
17 | return state;
18 | }
19 | };
20 |
21 | export default skillReducer;
22 |
--------------------------------------------------------------------------------
/src/reportWebVitals.js:
--------------------------------------------------------------------------------
1 | const reportWebVitals = onPerfEntry => {
2 | if (onPerfEntry && onPerfEntry instanceof Function) {
3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => {
4 | getCLS(onPerfEntry);
5 | getFID(onPerfEntry);
6 | getFCP(onPerfEntry);
7 | getLCP(onPerfEntry);
8 | getTTFB(onPerfEntry);
9 | });
10 | }
11 | };
12 |
13 | export default reportWebVitals;
14 |
--------------------------------------------------------------------------------
/src/shared/SecureRoute.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 | import { Redirect, Route } from "react-router-dom";
3 | import isLogin from "./authorization";
4 |
5 | const SecureRoute = (props) => {
6 | const [isLogged] = useState(isLogin);
7 | return (
8 |
11 | isLogged ? (
12 |
13 | ) : (
14 |
17 | )
18 | }
19 | >
20 | );
21 | };
22 |
23 | export default SecureRoute;
24 |
--------------------------------------------------------------------------------
/src/shared/authorization.js:
--------------------------------------------------------------------------------
1 | const auth = () => {
2 | const user = JSON.parse(localStorage.getItem("userData"));
3 | if (user && user.isLogged) {
4 | return user.isLogged;
5 | }
6 | return false;
7 | };
8 |
9 | export default auth;
10 |
--------------------------------------------------------------------------------
/src/shared/history.js:
--------------------------------------------------------------------------------
1 | import {createBrowserHistory} from 'history'
2 |
3 | export default createBrowserHistory();
--------------------------------------------------------------------------------
/src/shared/toast.js:
--------------------------------------------------------------------------------
1 | import { toast } from "react-toastify";
2 |
3 | export const toastSuccess = (message) => {
4 | toast.success(message, {
5 | position: "top-right",
6 | autoClose: 7000,
7 | hideProgressBar: false,
8 | closeOnClick: true,
9 | pauseOnHover: true,
10 | draggable: true,
11 | progress: undefined,
12 | });
13 | };
14 |
15 | export const toastError = (message) => {
16 | toast.error(message, {
17 | position: "top-right",
18 | autoClose: 7000,
19 | hideProgressBar: false,
20 | closeOnClick: true,
21 | pauseOnHover: true,
22 | draggable: true,
23 | progress: undefined,
24 | });
25 | };
26 |
--------------------------------------------------------------------------------