├── public
├── _redirects
└── index.html
├── .env
├── src
├── utils
│ └── currencyFormatter.js
├── index.css
├── components
│ ├── Footer.js
│ ├── ServerStatusCheck .js
│ ├── Navbar.js
│ ├── ProjectDetails.js
│ └── ProjectForm.js
├── hooks
│ ├── useAuthContext.js
│ ├── useProjectsContext.js
│ ├── useLogout.js
│ ├── useLogin.js
│ ├── useSignup.js
│ └── useServerStatus.js
├── index.js
├── context
│ ├── AuthContext.js
│ └── ProjectContext.js
├── App.js
└── pages
│ ├── Home.js
│ ├── Signup.js
│ └── Login.js
├── tailwind.config.js
├── .gitignore
├── package.json
└── README.md
/public/_redirects:
--------------------------------------------------------------------------------
1 | /* /index.html 200
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | REACT_APP_BASE_URL=https://proxima-owyi.onrender.com
--------------------------------------------------------------------------------
/src/utils/currencyFormatter.js:
--------------------------------------------------------------------------------
1 | export const currencyFormatter = (amount) => {
2 | return amount?.toLocaleString("en-US", {
3 | style: "currency",
4 | currency: "USD",
5 | });
6 | };
7 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | @import url("https://fonts.googleapis.com/css2?family=Inter:wght@100;200;300;400;500;600;700;800;900&display=swap");
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
--------------------------------------------------------------------------------
/src/components/Footer.js:
--------------------------------------------------------------------------------
1 | const Footer = () => {
2 | return (
3 |
6 | );
7 | };
8 |
9 | export default Footer;
10 |
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | module.exports = {
3 | content: ["./src/**/*.{js,jsx,ts,tsx}"],
4 | theme: {
5 | extend: {
6 | fontFamily: {
7 | sans: ["Inter, sans-serif"],
8 | },
9 | },
10 | },
11 | plugins: [],
12 | };
13 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Proxima
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/src/hooks/useAuthContext.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { AuthContext } from "../context/AuthContext";
3 |
4 | export const useAuthContext = () => {
5 | const context = useContext(AuthContext);
6 |
7 | if (!context) {
8 | throw new Error(
9 | "You must call useAuthContext inside a AuthContextProvider"
10 | );
11 | }
12 | return context;
13 | };
14 |
--------------------------------------------------------------------------------
/src/hooks/useProjectsContext.js:
--------------------------------------------------------------------------------
1 | import { useContext } from "react";
2 | import { ProjectContext } from "../context/ProjectContext";
3 |
4 | export const useProjectsContext = () => {
5 | const context = useContext(ProjectContext);
6 |
7 | if (!context) {
8 | throw new Error(
9 | "You must call useProjectsContext inside a ProjectContextProvider"
10 | );
11 | }
12 | return context;
13 | };
14 |
--------------------------------------------------------------------------------
/.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 |
--------------------------------------------------------------------------------
/src/hooks/useLogout.js:
--------------------------------------------------------------------------------
1 | import { useAuthContext } from "./useAuthContext";
2 | import { useProjectsContext } from "../hooks/useProjectsContext";
3 |
4 | export const useLogout = () => {
5 | const { dispatch: logoutDispatch } = useAuthContext();
6 | const { dispatch: projectsDispatch } = useProjectsContext();
7 |
8 | const logout = () => {
9 | // Clear local storage
10 | localStorage.removeItem("user");
11 |
12 | //Dispacth logout
13 | logoutDispatch({ type: "LOGOUT" });
14 | projectsDispatch({ type: "SET_PROJECTS", payload: [] });
15 | };
16 |
17 | return { logout };
18 | };
19 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import { BrowserRouter } from "react-router-dom";
4 | import App from "./App";
5 | import { AuthContextProvider } from "./context/AuthContext";
6 | import { ProjectContextProvider } from "./context/ProjectContext";
7 | import "./index.css";
8 |
9 | const root = ReactDOM.createRoot(document.getElementById("root"));
10 | root.render(
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | );
21 |
--------------------------------------------------------------------------------
/src/context/AuthContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useReducer } from "react";
2 |
3 | const initialState = {
4 | user: localStorage.getItem("user")
5 | ? JSON.parse(localStorage.getItem("user"))
6 | : null,
7 | };
8 |
9 | export const authReducer = (state, action) => {
10 | switch (action.type) {
11 | case "LOGIN":
12 | return {
13 | ...state,
14 | user: action.payload,
15 | };
16 | case "LOGOUT":
17 | return {
18 | ...state,
19 | user: null,
20 | };
21 | default:
22 | return state;
23 | }
24 | };
25 |
26 | export const AuthContext = createContext();
27 |
28 | export const AuthContextProvider = ({ children }) => {
29 | const [state, dispatch] = useReducer(authReducer, initialState);
30 |
31 | return (
32 |
33 | {children}
34 |
35 | );
36 | };
37 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import { Routes, Route, Navigate } from "react-router-dom";
2 | import Home from "./pages/Home";
3 | import Footer from "./components/Footer";
4 | import Navbar from "./components/Navbar";
5 | import Login from "./pages/Login";
6 | import Signup from "./pages/Signup";
7 | import { useAuthContext } from "./hooks/useAuthContext";
8 |
9 | function App() {
10 | const { user } = useAuthContext();
11 |
12 | return (
13 |
14 |
15 |
16 | : } />
17 | : }
20 | />
21 | : }
24 | />
25 | } />
26 |
27 |
28 |
29 | );
30 | }
31 |
32 | export default App;
33 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "frontend",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "@testing-library/jest-dom": "^5.16.5",
7 | "@testing-library/react": "^13.4.0",
8 | "@testing-library/user-event": "^13.5.0",
9 | "moment": "^2.29.4",
10 | "react": "^18.2.0",
11 | "react-dom": "^18.2.0",
12 | "react-router-dom": "^6.8.1",
13 | "react-scripts": "5.0.1",
14 | "web-vitals": "^2.1.4"
15 | },
16 | "scripts": {
17 | "start": "react-scripts start",
18 | "build": "react-scripts build",
19 | "test": "react-scripts test",
20 | "eject": "react-scripts eject"
21 | },
22 | "eslintConfig": {
23 | "extends": [
24 | "react-app",
25 | "react-app/jest"
26 | ]
27 | },
28 | "browserslist": {
29 | "production": [
30 | ">0.2%",
31 | "not dead",
32 | "not op_mini all"
33 | ],
34 | "development": [
35 | "last 1 chrome version",
36 | "last 1 firefox version",
37 | "last 1 safari version"
38 | ]
39 | },
40 | "devDependencies": {
41 | "tailwindcss": "^3.2.6"
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/components/ServerStatusCheck .js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import { useServerStatus } from "../hooks/useServerStatus";
3 |
4 | const ServerStatusCheck = ({ children }) => {
5 | const { status, error, isChecking, initialCheckDone, checkServerStatus } =
6 | useServerStatus();
7 |
8 | if (
9 | (status === "loading" || status === "idle") &&
10 | initialCheckDone === false
11 | ) {
12 | return (
13 |
14 |
Please wait, the server is waking up...
15 |
This may take up to 30 seconds for the first request.
16 | {isChecking &&
Checking server status...
}
17 |
18 | );
19 | }
20 |
21 | if (status === "failed") {
22 | return (
23 |
24 |
Error: {error}
25 |
28 |
29 | );
30 | }
31 |
32 | return <>{children}>;
33 | };
34 |
35 | export default ServerStatusCheck;
36 |
--------------------------------------------------------------------------------
/src/hooks/useLogin.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useAuthContext } from "./useAuthContext";
3 |
4 | export const useLogin = () => {
5 | const [error, setError] = useState(null);
6 | const [loading, setLoading] = useState(false);
7 |
8 | const { dispatch } = useAuthContext();
9 |
10 | const login = async (email, password) => {
11 | setLoading(true);
12 | setError(null);
13 |
14 | const res = await fetch(
15 | `${process.env.REACT_APP_BASE_URL}/api/user/login`,
16 | {
17 | method: "POST",
18 | headers: {
19 | "Content-Type": "application/json",
20 | },
21 | body: JSON.stringify({ email, password }),
22 | }
23 | );
24 |
25 | const json = await res.json();
26 |
27 | // res.ok === false
28 | if (!res.ok) {
29 | setLoading(false);
30 | setError(json.error);
31 | }
32 |
33 | if (res.ok) {
34 | //Update auth context
35 | dispatch({ type: "LOGIN", payload: json });
36 |
37 | //Save user in to the local storage
38 | localStorage.setItem("user", JSON.stringify(json));
39 |
40 | setLoading(false);
41 | }
42 | };
43 |
44 | return { login, error, loading };
45 | };
46 |
--------------------------------------------------------------------------------
/src/hooks/useSignup.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useAuthContext } from "./useAuthContext";
3 |
4 | export const useSignup = () => {
5 | const [error, setError] = useState(null);
6 | const [loading, setLoading] = useState(false);
7 |
8 | const { dispatch } = useAuthContext();
9 |
10 | const signup = async (fullName, email, password) => {
11 | setLoading(true);
12 | setError(null);
13 |
14 | const res = await fetch(
15 | `${process.env.REACT_APP_BASE_URL}/api/user/signup`,
16 | {
17 | method: "POST",
18 | headers: {
19 | "Content-Type": "application/json",
20 | },
21 | body: JSON.stringify({ fullName, email, password }),
22 | }
23 | );
24 |
25 | const json = await res.json();
26 |
27 | //res.ok ===false
28 | if (!res.ok) {
29 | setLoading(false);
30 | setError(json.error);
31 | }
32 |
33 | if (res.ok) {
34 | //Update auth context
35 | dispatch({ type: "LOGIN", payload: json });
36 |
37 | //Save user in to local storage
38 | localStorage.setItem("user", JSON.stringify(json));
39 |
40 | setLoading(false);
41 | }
42 | };
43 |
44 | return { signup, error, loading };
45 | };
46 |
--------------------------------------------------------------------------------
/src/hooks/useServerStatus.js:
--------------------------------------------------------------------------------
1 | import { useState, useEffect } from "react";
2 |
3 | export const useServerStatus = () => {
4 | const [status, setStatus] = useState("idle");
5 | const [error, setError] = useState(null);
6 | const [isChecking, setIsChecking] = useState(false);
7 | const [initialCheckDone, setInitialCheckDone] = useState(false);
8 |
9 | const checkServerStatus = async () => {
10 | if (isChecking) return;
11 |
12 | setIsChecking(true);
13 | setError(null);
14 |
15 | try {
16 | const response = await fetch(
17 | `${process.env.REACT_APP_BASE_URL}/api/health`
18 | );
19 | if (response.ok) {
20 | setStatus("succeeded");
21 | setInitialCheckDone(true);
22 | } else {
23 | throw new Error("Server is not responding");
24 | }
25 | } catch (error) {
26 | setStatus("failed");
27 | setError(error.message);
28 | } finally {
29 | setIsChecking(false);
30 | setInitialCheckDone(true);
31 | }
32 | };
33 |
34 | useEffect(() => {
35 | checkServerStatus();
36 | const intervalId = setInterval(checkServerStatus, 5000); // Check every 5 seconds
37 |
38 | return () => clearInterval(intervalId);
39 | }, []);
40 |
41 | return { status, error, isChecking, initialCheckDone, checkServerStatus };
42 | };
43 |
--------------------------------------------------------------------------------
/src/context/ProjectContext.js:
--------------------------------------------------------------------------------
1 | import { createContext, useReducer } from "react";
2 |
3 | const initialState = {
4 | projects: [],
5 | };
6 |
7 | export const projectsReducer = (state, action) => {
8 | switch (action.type) {
9 | case "SET_PROJECTS":
10 | return {
11 | ...state,
12 | projects: action.payload,
13 | };
14 | case "CREATE_PROJECT":
15 | return {
16 | ...state,
17 | projects: [action.payload, ...state.projects],
18 | };
19 | case "DELETE_PROJECT":
20 | return {
21 | ...state,
22 | projects: state.projects.filter(
23 | (project) => project._id !== action.payload._id
24 | ),
25 | };
26 | case "UPDATE_PROJECT":
27 | const [existingProject] = state.projects.filter(
28 | (project) => project._id === action.payload._id
29 | );
30 | return {
31 | ...state,
32 | projects: [
33 | action.payload,
34 | ...state.projects.filter(
35 | (project) => project._id !== existingProject._id
36 | ),
37 | ],
38 | };
39 | default:
40 | return state;
41 | }
42 | };
43 |
44 | export const ProjectContext = createContext();
45 |
46 | export const ProjectContextProvider = ({ children }) => {
47 | const [state, dispatch] = useReducer(projectsReducer, initialState);
48 |
49 | return (
50 |
51 | {children}
52 |
53 | );
54 | };
55 |
--------------------------------------------------------------------------------
/src/pages/Home.js:
--------------------------------------------------------------------------------
1 | import { useEffect } from "react";
2 | import ProjectDetails from "../components/ProjectDetails";
3 | import ProjectForm from "../components/ProjectForm";
4 | import { useAuthContext } from "../hooks/useAuthContext";
5 | import { useProjectsContext } from "../hooks/useProjectsContext";
6 |
7 | const Home = () => {
8 | const { projects, dispatch } = useProjectsContext();
9 | const { user } = useAuthContext();
10 |
11 | useEffect(() => {
12 | const getAllProjects = async () => {
13 | const res = await fetch(
14 | `${process.env.REACT_APP_BASE_URL}/api/projects`,
15 | {
16 | headers: {
17 | Authorization: `Bearer ${user.token}`,
18 | },
19 | }
20 | );
21 | const json = await res.json();
22 |
23 | if (res.ok) {
24 | dispatch({ type: "SET_PROJECTS", payload: json });
25 | }
26 | };
27 |
28 | if (user) {
29 | getAllProjects();
30 | }
31 | }, [dispatch, user]);
32 |
33 | return (
34 |
35 |
36 |
37 | {projects.length < 1 ? (
38 | "No project available here!"
39 | ) : (
40 |
41 | {projects.length === 1 ? "Your Project" : "Your all projects"}
42 |
43 | )}
44 |
45 |
46 | {projects &&
47 | projects.map((project) => (
48 |
49 | ))}
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default Home;
58 |
--------------------------------------------------------------------------------
/src/components/Navbar.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { Link } from "react-router-dom";
3 | import { useAuthContext } from "../hooks/useAuthContext";
4 | import { useLogout } from "../hooks/useLogout";
5 |
6 | const Navbar = () => {
7 | const [hover, setHover] = useState(false);
8 | const { user } = useAuthContext();
9 | const { logout } = useLogout();
10 |
11 | const handleLogout = () => {
12 | logout();
13 | };
14 |
15 | return (
16 |
17 |
18 | Proxima
19 |
20 |
21 |
70 |
71 | );
72 | };
73 |
74 | export default Navbar;
75 |
--------------------------------------------------------------------------------
/src/pages/Signup.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useSignup } from "../hooks/useSignup";
3 |
4 | const Signup = () => {
5 | const [fullName, setFullName] = useState("");
6 | const [email, setEmail] = useState("");
7 | const [password, setPassword] = useState("");
8 |
9 | const { signup, error, loading } = useSignup();
10 |
11 | const handleSignup = async (e) => {
12 | e.preventDefault();
13 |
14 | //Signup user
15 | await signup(fullName, email, password);
16 | };
17 |
18 | return (
19 |
87 | );
88 | };
89 |
90 | export default Signup;
91 |
--------------------------------------------------------------------------------
/src/components/ProjectDetails.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useProjectsContext } from "../hooks/useProjectsContext";
3 | import { useAuthContext } from "../hooks/useAuthContext";
4 | import { currencyFormatter } from "../utils/currencyFormatter";
5 | import moment from "moment";
6 | import ProjectForm from "./ProjectForm";
7 |
8 | const ProjectDetails = ({ project }) => {
9 | const [isModalOpen, setIsModalOpen] = useState(false);
10 | const [isOverlayOpen, setIsOverlayOpen] = useState(false);
11 |
12 | const { dispatch } = useProjectsContext();
13 | const { user } = useAuthContext();
14 |
15 | const handleDelete = async () => {
16 | if (!user) {
17 | return;
18 | }
19 |
20 | const res = await fetch(
21 | `${process.env.REACT_APP_BASE_URL}/api/projects/${project._id}`,
22 | {
23 | method: "DELETE",
24 | headers: {
25 | Authorization: `Bearer ${user.token}`,
26 | },
27 | }
28 | );
29 |
30 | const json = await res.json();
31 |
32 | if (res.ok) {
33 | dispatch({ type: "DELETE_PROJECT", payload: json });
34 | }
35 | };
36 |
37 | const handleUpdate = () => {
38 | setIsModalOpen(true);
39 | setIsOverlayOpen(true);
40 | };
41 |
42 | const handleOverlay = () => {
43 | setIsModalOpen(false);
44 | setIsOverlayOpen(false);
45 | };
46 |
47 | return (
48 |
49 |
50 | {project._id}
51 |
{project.title}
52 |
53 | {project.tech}
54 |
55 |
56 |
57 |
58 | Budget: {currencyFormatter(project.budget)}
59 |
60 | Added: {moment(project.createdAt).format("MMM DD, hh:mm A")}
61 |
62 |
63 | Updated: {moment(project.updatedAt).format("MMM DD, hh:mm A")}
64 |
65 |
66 |
67 | Manager: {project.manager}
68 | Developers: {project.dev}
69 |
70 | Duration:{" "}
71 | {`${project.duration} Week${project.duration === 1 ? "" : "s"}`}
72 |
73 |
74 |
75 |
76 |
82 |
88 |
89 |
90 | {/* OVERLAY */}
91 |
97 |
98 | {/* MODAL */}
99 |
104 |
Update project
105 |
110 |
111 |
112 | );
113 | };
114 |
115 | export default ProjectDetails;
116 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Proxima: Project Management Web Application
2 |
3 | Proxima is a project management application built using the MERN stack. The application allows users to create, update, and delete their own projects with ease. Proxima is designed with high-level security in mind, featuring strong JWT authentication and front-end route protection to ensure the safety and privacy of user data.
4 |
5 | ## Features
6 |
7 | Proxima is a feature-rich project management application, offering users a wide range of useful tools and functions, including:
8 |
9 | - **Effortlessly manage your projects:** With Proxima's intuitive interface, you can create, update, and delete projects quickly and easily. All project-related information is centralized, allowing you to stay organized and focused.
10 | - **State-of-the-Art Security Measures:** Proxima prioritizes the safety and security of your data with its highly robust security features. The app uses JWT authentication and frontend route protection to provide a secure platform for your project management needs, giving you peace of mind that your data is always protected.
11 | - **Personalized Project Views:** User-specific project views, so each user can only see the projects they have created. This ensures that project information is kept private and secure, while also giving users the flexibility to manage their projects according to their unique needs.
12 | - **Project Timeline Management:** Users with a powerful timeline management tool, allowing them to track the creation date of a project, as well as its last update date. With this feature, users can easily monitor the progress of their projects and ensure that they are staying on schedule. This helps teams to optimize their workflows, allocate resources efficiently, and meet project deadlines.
13 | - **Intuitive and Streamlined UI:** User interface is designed to be both intuitive and streamlined, making project management a breeze. The clean and modern design of the interface allows users to easily navigate the app's many features, while also providing a visually pleasing and enjoyable user experience.
14 |
15 | ## Tools
16 |
17 | Proxima is built using the MERN stack, featuring the following powerful tools:
18 |
19 | - **MongoDB:** A highly flexible NoSQL database, ideal for managing large and complex data sets.
20 | - **Express:** A popular and highly flexible backend web application framework for Node.js.
21 | - **React:** A powerful and popular frontend JavaScript library, ideal for building user interfaces.
22 | - **Node.js:** A powerful and popular server-side JavaScript runtime environment.
23 | - **Tailwind CSS:** A highly customizable CSS framework, designed to make building sleek and intuitive user interfaces a breeze.
24 |
25 | ## Installation
26 |
27 | To install Proxima, Follow the simple steps below:
28 |
29 | > **Note**
30 | > **You must have Node.js and MongoDB installed on your system!**
31 | 1. Clone the `client` repository using
32 |
33 | ```
34 | git clone https://github.com/masudranashawon/proxima-client.git`
35 | ```
36 |
37 | 2. Clone the `server` repository using
38 |
39 | ```
40 | git clone https://github.com/masudranashawon/proxima-server.git
41 | ```
42 |
43 | 3. Install the required dependencies in both the `client` and `server` directories by running
44 |
45 | ```
46 | npm install
47 | ```
48 |
49 | **Or**
50 |
51 | ```
52 | npm i
53 | ```
54 |
55 | 4. Create a `.env` file in the root directory of `server` and add the following variables:
56 |
57 | - `MONGO_URI`: the MongoDB connection string
58 | - `SECRET`: a secret string for JWT authentication
59 |
60 | 5. Create a `.env` file in the root directory of `client` and add the following variable:
61 | - `REACT_APP_BASE_URL`: for example `http://localhost:5000`
62 | 6. Start the backend `server` by running
63 |
64 | ```
65 | npm start
66 | ```
67 |
68 | 7. Start the `frontend` by running
69 |
70 | ```
71 | npm start
72 | ```
73 |
74 | ## Links
75 |
76 | - [Live Demo](https://proxima-application.netlify.app)
77 | - [Front-End Repository](https://github.com/masudranashawon/proxima-client)
78 | - [Back-End Repository](https://github.com/masudranashawon/proxima-server)
79 |
80 | ## Thanks for visiting this repository!
81 |
--------------------------------------------------------------------------------
/src/pages/Login.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useLogin } from "../hooks/useLogin";
3 | // import { useServerStatus } from "../hooks/useServerStatus";
4 |
5 | const Login = () => {
6 | const [email, setEmail] = useState("");
7 | const [password, setPassword] = useState("");
8 | const [serverReady, setServerReady] = useState(true);
9 | const [serverError, setServerError] = useState(false);
10 | const [isLoading, setIsLoading] = useState(false);
11 |
12 | const { login, error, loading } = useLogin();
13 | // const {
14 | // status,
15 | // error: serverError,
16 | // isChecking,
17 | // initialCheckDone,
18 | // checkServerStatus,
19 | // } = useServerStatus();
20 |
21 | const checkServerStatus = async () => {
22 | try {
23 | setIsLoading(true);
24 |
25 | const response = await fetch(
26 | `${process.env.REACT_APP_BASE_URL}/api/health`
27 | );
28 |
29 | if (response.ok) {
30 | setIsLoading(false);
31 | setServerReady(true);
32 | } else {
33 | setIsLoading(false);
34 | setServerReady(false);
35 | throw new Error("Server is not responding");
36 | }
37 | } catch (error) {
38 | setIsLoading(false);
39 | setServerError(true);
40 | console.error("Server health check failed:", error);
41 | }
42 | };
43 |
44 | const handleLogin = async (e) => {
45 | e.preventDefault();
46 | checkServerStatus();
47 |
48 | if (isLoading !== true && serverError !== true && serverReady === true) {
49 | await login(email, password);
50 | }
51 | };
52 |
53 | return (
54 |
120 | );
121 | };
122 |
123 | export default Login;
124 |
--------------------------------------------------------------------------------
/src/components/ProjectForm.js:
--------------------------------------------------------------------------------
1 | import { useState } from "react";
2 | import { useAuthContext } from "../hooks/useAuthContext";
3 | import { useProjectsContext } from "../hooks/useProjectsContext";
4 |
5 | const ProjectForm = ({ project, setIsModalOpen, setIsOverlayOpen }) => {
6 | const [title, setTitle] = useState(project ? project.title : "");
7 | const [tech, setTech] = useState(project ? project.tech : "");
8 | const [budget, setBudget] = useState(project ? project.budget : "");
9 | const [duration, setDuration] = useState(project ? project.duration : "");
10 | const [manager, setManager] = useState(project ? project.manager : "");
11 | const [dev, setDev] = useState(project ? project.dev : "");
12 | const [error, setError] = useState(null);
13 | const [emptyFields, setEmptyFields] = useState([]);
14 |
15 | const { dispatch } = useProjectsContext();
16 | const { user } = useAuthContext();
17 |
18 | const handleSubmit = async (e) => {
19 | e.preventDefault();
20 |
21 | if (!user) {
22 | setError("Please log in to access this feature.");
23 | return;
24 | }
25 |
26 | //Data
27 | const projectObj = {
28 | title,
29 | tech,
30 | budget,
31 | duration,
32 | manager,
33 | dev,
34 | };
35 |
36 | //Is there is no project, sent post req
37 | if (!project) {
38 | //Post req
39 | const res = await fetch(
40 | `${process.env.REACT_APP_BASE_URL}/api/projects`,
41 | {
42 | method: "POST",
43 | headers: {
44 | "Content-Type": "application/json",
45 | Authorization: `Bearer ${user.token}`,
46 | },
47 | body: JSON.stringify(projectObj),
48 | }
49 | );
50 | const json = await res.json();
51 |
52 | //res.ok is false set error
53 | if (!res.ok) {
54 | setError(json.error);
55 | setEmptyFields(json.emptyFields);
56 | }
57 |
58 | //res.ok is true, reset
59 | if (res.ok) {
60 | setTitle("");
61 | setTech("");
62 | setBudget("");
63 | setDuration("");
64 | setManager("");
65 | setDev("");
66 | setError(null);
67 | setEmptyFields([]);
68 |
69 | //Dispath
70 | dispatch({ type: "CREATE_PROJECT", payload: json });
71 | }
72 | return;
73 | }
74 |
75 | //Is there is a project, sent patch req
76 | if (project) {
77 | //Sent patch req
78 | const res = await fetch(
79 | `${process.env.REACT_APP_BASE_URL}/api/projects/${project._id}`,
80 | {
81 | method: "PATCH",
82 | headers: {
83 | "Content-Type": "application/json",
84 | Authorization: `Bearer ${user.token}`,
85 | },
86 | body: JSON.stringify(projectObj),
87 | }
88 | );
89 |
90 | const json = await res.json();
91 |
92 | //!res.ok
93 | if (!res.ok) {
94 | setError(json.error);
95 | setEmptyFields(json.emptyFields);
96 | }
97 |
98 | //res.ok
99 | if (res.ok) {
100 | setError(null);
101 | setEmptyFields([]);
102 |
103 | //Dispatch
104 | dispatch({ type: "UPDATE_PROJECT", payload: json });
105 |
106 | //Close overlay & modal
107 | setIsModalOpen(false);
108 | setIsOverlayOpen(false);
109 | }
110 | return;
111 | }
112 | };
113 |
114 | return (
115 |
257 | );
258 | };
259 |
260 | export default ProjectForm;
261 |
--------------------------------------------------------------------------------