S62 Added

master
Ron Reciproco 12 months ago
parent d69f0aeaba
commit d52fa7f45e

@ -16,7 +16,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"email\": \"renzo@email.com\",\r\n \"password\": \"renzo\"\r\n}\r\n",
"raw": "{\r\n \"email\": \"ronron@email.com\",\r\n \"password\": \"ronron\"\r\n}\r\n",
"options": {
"raw": {
"language": "json"
@ -24,13 +24,17 @@
}
},
"url": {
"raw": "http://localhost:3000/user/register",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/register",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"register"
]
@ -55,7 +59,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"email\": \"wapatu@email.com\",\r\n \"password\": \"wapatu\"\r\n}\r\n",
"raw": "{\r\n \"email\": \"ronron@email.com\",\r\n \"password\": \"ronron\"\r\n}\r\n",
"options": {
"raw": {
"language": "json"
@ -63,13 +67,17 @@
}
},
"url": {
"raw": "http://localhost:3000/user/authenticate",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/authenticate",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"authenticate"
]
@ -105,13 +113,17 @@
}
},
"url": {
"raw": "http://localhost:3000/user/update",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/update",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"update"
]
@ -125,14 +137,17 @@
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3000/product/active",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/active",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"product",
"b5",
"active"
]
}
@ -145,13 +160,17 @@
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3000/product/products/65545619b88d0a48f00ae778",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/product/products/65545619b88d0a48f00ae778",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"product",
"products",
"65545619b88d0a48f00ae778"
@ -167,7 +186,7 @@
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"userId\": \"65544d9be5c01f6c0ca79200\", // Replace with a valid user ID from your database\r\n \"products\": [\r\n {\r\n \"productId\": \"6553a4e897ac8ac9462f96c4\", // Replace with a valid product ID from your database\r\n \"productName\": \"Mastering Card\",\r\n \"quantity\": 1\r\n }\r\n ],\r\n \"totalAmount\": 500\r\n}\r\n",
"raw": "{\r\n \"userId\": \"65535cb526b586a3e2fd56cc\", // Replace with a valid user ID from your database\r\n \"products\": [\r\n {\r\n \"productId\": \"6553a4e897ac8ac9462f96c4\", // Replace with a valid product ID from your database\r\n \"productName\": \"Mastering Card\",\r\n \"quantity\": 1\r\n }\r\n ],\r\n \"totalAmount\": 500\r\n}\r\n",
"options": {
"raw": {
"language": "json"
@ -175,13 +194,17 @@
}
},
"url": {
"raw": "http://localhost:3000/user/order",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/order",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"order"
]
@ -217,13 +240,17 @@
}
},
"url": {
"raw": "http://localhost:3000/user/retrieveUser",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/retrieveUser",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"retrieveUser"
]
@ -239,16 +266,30 @@
{
"name": "Retrieve All Products",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTUzNWNiNTI2YjU4NmEzZTJmZDU2Y2MiLCJlbWFpbCI6ImFkbWluQGVtYWlsLmNvbSIsImlzQWRtaW4iOnRydWUsImlhdCI6MTcwMDIxNTgwNSwiZXhwIjoxNzAwMjE5NDA1fQ.tYjbNBbBsg71V7fPXk68ccEJ3svDiyAL_8O0CPZDKYo",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3000/product/all",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/product/all",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"product",
"all"
]
@ -281,13 +322,17 @@
}
},
"url": {
"raw": "http://localhost:3000/product/create",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/product/create",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"product",
"create"
]
@ -320,13 +365,17 @@
}
},
"url": {
"raw": "http://localhost:3000/product/products/65545a1e6fa9d841e1518d1d",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/product/products/65545a1e6fa9d841e1518d1d",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"product",
"products",
"65545a1e6fa9d841e1518d1d"
@ -351,13 +400,17 @@
"method": "PUT",
"header": [],
"url": {
"raw": "http://localhost:3000/product/products/6554634e5cac4bcd6f2394ed/activate",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/product/products/6554634e5cac4bcd6f2394ed/activate",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"product",
"products",
"6554634e5cac4bcd6f2394ed",
@ -373,13 +426,17 @@
"method": "PUT",
"header": [],
"url": {
"raw": "http://localhost:3000/product/products/6554634e5cac4bcd6f2394ed/archive",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/product/products/6554634e5cac4bcd6f2394ed/archive",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"product",
"products",
"6554634e5cac4bcd6f2394ed",
@ -392,16 +449,39 @@
{
"name": "Set User to Admin",
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTUzNWNiNTI2YjU4NmEzZTJmZDU2Y2MiLCJlbWFpbCI6ImFkbWluQGVtYWlsLmNvbSIsImlzQWRtaW4iOnRydWUsImlhdCI6MTcwMDA0MzgyOSwiZXhwIjoxNzAwMDQ3NDI5fQ.Mwkp161yHtw11mWL6oNnSDjyobOLHCb8qrFqgvF4bqI",
"type": "string"
}
]
},
"method": "POST",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"userId\": \"65535cb526b586a3e2fd56cc\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:3000/user/set-admin/",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/set-admin/",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"set-admin",
""
@ -418,7 +498,7 @@
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTU0YWM4ZGQ3ZmJmOWVlOTAyMTdlNzciLCJlbWFpbCI6Im1hc3RlckBlbWFpbC5jb20iLCJpc0FkbWluIjpmYWxzZSwiaWF0IjoxNzAwMjEyNjkzLCJleHAiOjE3MDAyMTYyOTN9.J908JWFjN5dKRw0-XJPHa4kD6QAW4M7tv1LOVmbtM_E",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTUzNWNiNTI2YjU4NmEzZTJmZDU2Y2MiLCJlbWFpbCI6ImFkbWluQGVtYWlsLmNvbSIsImlzQWRtaW4iOnRydWUsImlhdCI6MTcwMDIxNTgwNSwiZXhwIjoxNzAwMjE5NDA1fQ.tYjbNBbBsg71V7fPXk68ccEJ3svDiyAL_8O0CPZDKYo",
"type": "string"
}
]
@ -426,13 +506,17 @@
"method": "GET",
"header": [],
"url": {
"raw": "http://localhost:3000/user/orders-all",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/user/orders-all",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"user",
"orders-all"
]
@ -470,13 +554,17 @@
}
},
"url": {
"raw": "http://localhost:3000/cart/add-to-cart",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/cart/add-to-cart",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"cart",
"add-to-cart"
]
@ -509,13 +597,17 @@
}
},
"url": {
"raw": "http://localhost:3000/cart/remove-from-cart",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/cart/remove-from-cart",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"cart",
"remove-from-cart"
]
@ -548,15 +640,18 @@
}
},
"url": {
"raw": "http://localhost:3000/cart//update-quantity",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/cart/update-quantity",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"cart",
"",
"update-quantity"
]
}
@ -565,20 +660,45 @@
},
{
"name": "Cart Total Price",
"protocolProfileBehavior": {
"disableBodyPruning": true
},
"request": {
"auth": {
"type": "bearer",
"bearer": [
{
"key": "token",
"value": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VySWQiOiI2NTU0YWM4ZGQ3ZmJmOWVlOTAyMTdlNzciLCJlbWFpbCI6Im1hc3RlckBlbWFpbC5jb20iLCJpc0FkbWluIjpmYWxzZSwiaWF0IjoxNzAwMDU0NDk2LCJleHAiOjE3MDAwNTgwOTZ9.ytA5zJ3gqEefw7lRAgvVXlWD3-iANaEjjkYgBqMWc4I",
"type": "string"
}
]
},
"method": "GET",
"header": [],
"body": {
"mode": "raw",
"raw": "{\r\n \"userId\": \"655396dcc8ea29f42422e214\"\r\n}",
"options": {
"raw": {
"language": "json"
}
}
},
"url": {
"raw": "http://localhost:3000/cart/6554cc5a4446b63dcf2d7fc0/655396dcc8ea29f42422e214",
"raw": "http://ec2-3-15-94-47.us-east-2.compute.amazonaws.com/b5/cart/cart-details",
"protocol": "http",
"host": [
"localhost"
"ec2-3-15-94-47",
"us-east-2",
"compute",
"amazonaws",
"com"
],
"port": "3000",
"path": [
"b5",
"cart",
"6554cc5a4446b63dcf2d7fc0",
"655396dcc8ea29f42422e214"
"cart-details"
]
}
},

@ -12,10 +12,13 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^4.6.0",
"dotenv": "^16.3.1",
"react": "^18.2.0",
"react-bootstrap": "^1.5.2",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"react-scripts": "5.0.1",
"sweetalert2": "^11.10.1",
"web-vitals": "^2.1.4"
}
},
@ -3272,6 +3275,14 @@
"url": "https://opencollective.com/popperjs"
}
},
"node_modules/@remix-run/router": {
"version": "1.13.0",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.13.0.tgz",
"integrity": "sha512-5dMOnVnefRsl4uRnAdoWjtVTdh8e6aZqgM4puy9nmEADH72ck+uXwzpJLEKE9Q6F8ZljNewLgmTfkxUrBdv4WA==",
"engines": {
"node": ">=14.0.0"
}
},
"node_modules/@restart/context": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
@ -7120,11 +7131,14 @@
}
},
"node_modules/dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": {
"node": ">=10"
"node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
}
},
"node_modules/dotenv-expand": {
@ -14929,6 +14943,36 @@
"node": ">=0.10.0"
}
},
"node_modules/react-router": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.20.0.tgz",
"integrity": "sha512-pVvzsSsgUxxtuNfTHC4IxjATs10UaAtvLGVSA1tbUE4GDaOSU1Esu2xF5nWLz7KPiMuW8BJWuPFdlGYJ7/rW0w==",
"dependencies": {
"@remix-run/router": "1.13.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8"
}
},
"node_modules/react-router-dom": {
"version": "6.20.0",
"resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.20.0.tgz",
"integrity": "sha512-CbcKjEyiSVpA6UtCHOIYLUYn/UJfwzp55va4yEfpk7JBN3GPqWfHrdLkAvNCcpXr8QoihcDMuk0dzWZxtlB/mQ==",
"dependencies": {
"@remix-run/router": "1.13.0",
"react-router": "6.20.0"
},
"engines": {
"node": ">=14.0.0"
},
"peerDependencies": {
"react": ">=16.8",
"react-dom": ">=16.8"
}
},
"node_modules/react-scripts": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz",
@ -15001,6 +15045,14 @@
}
}
},
"node_modules/react-scripts/node_modules/dotenv": {
"version": "10.0.0",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==",
"engines": {
"node": ">=10"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -16488,6 +16540,15 @@
"boolbase": "~1.0.0"
}
},
"node_modules/sweetalert2": {
"version": "11.10.1",
"resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.10.1.tgz",
"integrity": "sha512-qu145oBuFfjYr5yZW9OSdG6YmRxDf8CnkgT/sXMfrXGe+asFy2imC2vlaLQ/L/naZ/JZna1MPAY56G4qYM0VUQ==",
"funding": {
"type": "individual",
"url": "https://github.com/sponsors/limonte"
}
},
"node_modules/symbol-tree": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz",

@ -7,10 +7,13 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"bootstrap": "^4.6.0",
"dotenv": "^16.3.1",
"react": "^18.2.0",
"react-bootstrap": "^1.5.2",
"react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"react-scripts": "5.0.1",
"sweetalert2": "^11.10.1",
"web-vitals": "^2.1.4"
},
"scripts": {

@ -1,26 +1,102 @@
import AppNavBar from "./components/AppNavBar";
import { Container } from "react-bootstrap";
import "./App.css";
import { useState, useEffect } from 'react';
import AppNavBar from './components/AppNavBar';
import { Container } from 'react-bootstrap';
import { BrowserRouter as Router } from 'react-router-dom';
import { Route, Routes } from 'react-router-dom';
import './App.css';
import { UserProvider } from './UserContext';
// Pages
// import Home from "./pages/Home";
// import Courses from "./pages/Courses";
import Register from "./pages/Register";
import Login from "./pages/Login";
import Home from './pages/Home';
import Courses from './pages/Courses';
import Register from './pages/Register';
import Login from './pages/Login';
import Logout from './pages/Logout';
import Profile from './pages/Profile';
import CourseView from './pages/CourseView';
import AddCourse from './pages/AddCourse';
import NotFound from './pages/Error';
function App() {
// let user = { token: localStorage.getItem('token') }
const [user, setUser] = useState({
id: null,
isAdmin: null
})
const unsetUser = () => {
localStorage.clear()
}
useEffect(() => {
// console.log(localStorage);
fetch(`${process.env.REACT_APP_API_URL}/users/details`, {
headers: {
Authorization: `Bearer ${ localStorage.getItem('token') }`
}
})
.then(res => res.json())
.then(data => {
// console.log(data);
if(typeof data._id !== "undefined"){
setUser({
id: data._id,
isAdmin: data.isAdmin
});
} else {
setUser({
id: null,
isAdmin: null
});
}
})
}, [])
console.log(user);
return (
// JSX - JavaScript XML Code
// Fragments
<>
<AppNavBar />
<Container>
{/* <Register/> */}
<Login/>
</Container>
</>
// JSX - JavaScript XML code
// <></> - Fragments
<UserProvider value={{ user, setUser, unsetUser }}>
<Router>
<AppNavBar />
<Container>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/courses" element={<Courses />} />
{/*MINI ACTIVITY - continue completing the routes*/}
<Route path="/register" element={<Register />} />
<Route path="/login" element={<Login />} />
<Route path="/logout" element={<Logout />} />
<Route path="/profile" element={<Profile />} />
<Route path="/courses/:courseId" element={<CourseView />} />
<Route path='/add-course' element={<AddCourse/>} />
<Route path="*" element={<NotFound />} />
</Routes>
</Container>
</Router>
</UserProvider>
);
}
export default App;
/**
* Mini Activity 2
* Import the Highlights component
* Add the Highlights component in the JSX inside the Container and after the Banner Component
* 2 mins
*/

@ -0,0 +1,11 @@
import React from 'react';
// Creates a Context object
// A context object as the name states is a data type of an object that can be used to store information that can be shared to other components within the app
// The context object is a different approach to passing information between components and allows easier access by avoiding the use of prop-drilling
const UserContext = React.createContext();
// The "Provider" component allows other components to consume/use the context object and supply the necessary information needed to the context object
export const UserProvider = UserContext.Provider;
export default UserContext;

@ -0,0 +1,84 @@
// AdminView.js
import { useState, useEffect } from 'react';
import { Table } from 'react-bootstrap';
import EditCourse from './EditCourse';
import ArchiveCourse from './ArchiveCourse';
const fetchDataFromDatabase = async () => {
try {
const response = await fetch(`${process.env.REACT_APP_API_URL}/courses/all`);
const data = await response.json();
return data;
} catch (error) {
throw new Error('Error fetching data from the database');
}
};
export default function AdminView() {
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
const [courses, setCourses] = useState([]);
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
const data = await fetchDataFromDatabase();
setCourses(data.map(course => (
<tr key={course._id}>
<td>{course._id}</td>
<td>{course.name}</td>
<td>{course.description}</td>
<td>{course.price}</td>
<td className={course.isActive ? 'text-success' : 'text-danger'}>
{course.isActive ? 'Available' : 'Unavailable'}
</td>
<td><EditCourse course={course._id} fetchData={fetchData} /></td>
<td>
<ArchiveCourse
course={course._id}
fetchData={fetchData}
isActive={course.isActive}
/>
</td>
</tr>
)));
} catch (error) {
setError(error.message || 'An error occurred');
} finally {
setLoading(false);
}
};
fetchData();
}, []);
return (
<>
<h1 className="text-center my-4"> Admin Dashboard</h1>
{loading && <p>Loading...</p>}
{error && <p style={{ color: 'red' }}>{error}</p>}
{!loading && !error && (
<Table striped bordered hover responsive>
<thead>
<tr className="text-center">
<th>ID</th>
<th>Name</th>
<th>Description</th>
<th>Price</th>
<th>Availability</th>
<th colSpan="2">Actions</th>
</tr>
</thead>
<tbody>
{courses}
</tbody>
</Table>
)}
</>
);
}

@ -1,25 +1,59 @@
// React Bootstrap Component
/*
-Syntax
import moduleName from 'filePath';
-Syntax
import moduleName from 'filePath';
*/
import { useContext } from 'react';
import Container from 'react-bootstrap/Container';
import NavBar from 'react-bootstrap/NavBar';
import Navbar from 'react-bootstrap/Navbar';
import Nav from 'react-bootstrap/Nav';
import { NavLink } from 'react-router-dom';
import UserContext from '../UserContext';
export default function AppNavBar() {
return (
<NavBar bg="light" expand="lg">
<Container fluid>
<NavBar.Brand href="#home">Zuitt Booking</NavBar.Brand>
<NavBar.Toggle aria-controls="basic-NavBar-nav"/>
<NavBar.Collapse id="basic-NavBar-nav">
<Nav className="ms-auto">
<Nav.Link href="#home">Home</Nav.Link>
<Nav.Link href="#link">Courses</Nav.Link>
</Nav>
</NavBar.Collapse>
</Container>
</NavBar>
)
}
const { user } = useContext(UserContext);
return (
<Navbar bg="light" expand="lg">
<Container fluid>
<Navbar.Brand href="#home">Zuitt Booking</Navbar.Brand>
<Navbar.Toggle aria-controls="basic-navbar-nav" />
<Navbar.Collapse id="basic-navbar-nav">
<Nav className="ml-auto">
<Nav.Link as={NavLink} to="/" exact>
Home
</Nav.Link>
<Nav.Link as={NavLink} to="/courses" exact>
Courses
</Nav.Link>
{(user && user.isAdmin) && ( // Show the link only for admin users
<Nav.Link as={NavLink} to="/add-course" exact>
Add Course
</Nav.Link>
)}
{user && user.id !== null ? (
<>
<Nav.Link as={NavLink} to="/profile" exact>
Profile
</Nav.Link>
<Nav.Link as={NavLink} to="/logout" exact>
Logout
</Nav.Link>
</>
) : (
<>
<Nav.Link as={NavLink} to="/login" exact>
Login
</Nav.Link>
<Nav.Link as={NavLink} to="/register" exact>
Register
</Nav.Link>
</>
)}
</Nav>
</Navbar.Collapse>
</Container>
</Navbar>
);
}

@ -0,0 +1,82 @@
import React from 'react';
import { Button } from 'react-bootstrap';
import Swal from 'sweetalert2';
export default function ArchiveCourse({ course, fetchData, isActive }) {
const archiveToggle = (courseId) => {
fetch(`${process.env.REACT_APP_API_URL}/courses/${courseId}/archive`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(data => {
console.log(data);
if (!data.success) {
Swal.fire({
title: 'Success!',
icon: 'success',
text: 'Course Successfully Archived'
});
fetchData(); // Ensure it fetches the updated data
} else {
Swal.fire({
title: 'Error!',
icon: 'error',
text: 'Please try again'
});
fetchData();
}
});
};
const activateToggle = (courseId) => {
fetch(`${ process.env.REACT_APP_API_URL}/courses/${courseId}/activate`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`
}
})
.then(res => res.json())
.then(data => {
console.log(data);
if (!data.success) {
Swal.fire({
title: 'Success!',
icon: 'success',
text: 'Course Successfully Activated'
});
fetchData();
} else {
Swal.fire({
title: 'Error!',
icon: 'error',
text: ' Please try again'
});
fetchData();
}
});
};
return (
<>
{isActive ? (
<Button variant="danger" size="sm" onClick={() => archiveToggle(course)}>
Archive
</Button>
) : (
<Button variant="success" size="sm" onClick={() => activateToggle(course)}>
Activate
</Button>
)}
</>
);
}

@ -1,13 +1,19 @@
import { Button, Row, Col } from 'react-bootstrap';
import { Link } from 'react-router-dom';
export default function Banner() {
return (
<Row>
<Col>
<h1>Zuitt Coding Bootcamp</h1>
<p>Opportunites for everyone, everywhere.</p>
<Button variant='primary'>Enroll Now</Button>
</Col>
</Row>
)
export default function Banner({data}) {
console.log(data);
const {title, content, destination, label} = data;
return (
<Row>
<Col className="p-5 text-center">
<h1>{title}</h1>
<p>{content}</p>
<Link className="btn btn-primary" to={destination}>{label}</Link>
</Col>
</Row>
)
}

@ -1,54 +1,55 @@
import { Button, Card } from "react-bootstrap";
import PropTypes from 'prop-types';
import { Card, Button } from 'react-bootstrap';
import { useState } from 'react'
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
export default function CourseCard({ courseProp }) {
const { name, description, price } = courseProp;
// Use the state hook to be able to store state (data) in this component
// States are used to keep track of information related to individual component
// Syntax:
/**
* const [ getter, setterFunction ] = useState(initialGetterValue)
*/
// Deconstruct the course properties into their own variables
const { _id, name, description, price } = courseProp;
const [ count, setCount ] = useState(0);
const enroll = () => {
if (count < 30) {
setCount(count + 1);
} else {
alert('No More Seats');
}
};
// Use the state hook to be able to store state (data) in this component
// States are used to keep track of information related to individual component
// Syntax:
/*
const [getter, setterFunction] = useState(initialGetterValue)
*/
// const [count, setCount] = useState(0);
// const [seats, setSeats] = useState(10);
// function enroll(){
// if(seats > 0) {
// setCount(count + 1)
// setSeats(seats - 1);
// } else {
// alert("No more seats available")
// }
// }
return (
<Card>
<Card.Body>
<Card.Title>{name}</Card.Title>
<Card.Title>Description</Card.Title>
<Card.Text>{description}</Card.Text>
<Card.Title>Price:</Card.Title>
<Card.Text>Php {price}</Card.Text>
<Card.Text>Enrollees: {count}</Card.Text>
<Button variant="primary" onClick={enroll}>
Enroll
</Button>
</Card.Body>
</Card>
);
return (
<Card>
<Card.Body>
<Card.Title>{name}</Card.Title>
<Card.Subtitle>Description:</Card.Subtitle>
<Card.Text>{description}</Card.Text>
<Card.Subtitle>Price:</Card.Subtitle>
<Card.Text>Php {price}</Card.Text>
<Link className="btn btn-primary" to={`/courses/${_id}`} >Details</Link>
</Card.Body>
</Card>
)
}
// Check if the CourseCard component is getting the correct prop types
// PropTypes are used for validating information passed to a component.
CourseCard.propTypes = {
// The 'shape' method is used to check if a prop object conforms to a specific "shape"
courseProp: PropTypes.shape({
// Define the properties and their expected types
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
price: PropTypes.number.isRequired
}).isRequired // Make sure the entire courseProp object is required
};
// The 'shape' method is used to check if a prop object comforms to a specific "shape"
courseProp: PropTypes.shape({
// Define the properties and their expected types
name: PropTypes.string.isRequired,
description: PropTypes.string.isRequired,
price: PropTypes.number.isRequired
})
}

@ -0,0 +1,50 @@
import React, { useState } from 'react';
import CourseCard from './CourseCard';
const CourseSearch = () => {
const [searchQuery, setSearchQuery] = useState('');
const [searchResults, setSearchResults] = useState([]);
const handleSearch = async () => {
try {
const response = await fetch(`${process.env.REACT_APP_API_URL}/courses/search`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ courseName: searchQuery })
});
const data = await response.json();
setSearchResults(data);
} catch (error) {
console.error('Error searching for courses:', error);
}
};
return (
<div>
<h2>Course Search</h2>
<div className="form-group">
<label htmlFor="courseName">Course Name:</label>
<input
type="text"
id="courseName"
className="form-control"
value={searchQuery}
onChange={event => setSearchQuery(event.target.value)}
/>
</div>
<button className="btn btn-primary" onClick={handleSearch}>
Search
</button>
<h3>Search Results:</h3>
<ul>
{searchResults.map(course => (
<CourseCard courseProp={course} key={course._id}/>
))}
</ul>
</div>
);
};
export default CourseSearch;

@ -0,0 +1,127 @@
import React, { useState } from 'react';
import { Button, Modal, Form } from 'react-bootstrap';
import Swal from 'sweetalert2';
// Add the props course to get the specific id of the course
export default function EditCourse({ course, fetchData }){
// State for courseId for the fetch URL
const [courseId, setCourseId] = useState('')
// Forms state
// Add state for the forms of course
const [name, setName] = useState('');
const [description, setDescription] = useState('');
const [price, setPrice] = useState('');
// state for editCourse Modals to open/close
const [showEdit, setShowEdit] = useState(false)
// Function for opening the modal
const openEdit = (courseId) => {
fetch(`http://localhost:4000/courses/${courseId}`)
.then(res => res.json())
.then(data => {
// Populate all the input values with course info that we fetched
setCourseId(data._id);
setName(data.name);
setDescription(data.description);
setPrice(data.price)
})
// Open the modal
setShowEdit(true)
}
const closeEdit = () =>{
setShowEdit(false);
setName('');
setDescription('');
setPrice(0);
}
// Function to update the course
const editCourse = (e, courseId) => {
e.preventDefault();
fetch(`http://localhost:4000/courses/${courseId}`,{
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
name: name,
description: description,
price: price
})
})
.then(res => res.json())
.then(data => {
console.log(data)
if(data === true){
Swal.fire({
title: 'Success!',
icon: 'success',
text: 'Course Successfully Updated'
})
closeEdit();
fetchData();
} else {
Swal.fire({
title: 'Error!',
icon: 'error',
text: ' Please try again'
})
closeEdit();
fetchData();
}
})
}
return(
<>
<Button variant="primary" size="sm" onClick={() => openEdit(course)}> Edit </Button>
{/*Edit Modal*/}
<Modal show={showEdit} onHide={closeEdit}>
<Form onSubmit={e => editCourse(e, courseId)}>
<Modal.Header closeButton>
<Modal.Title>Edit Course</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Group>
<Form.Label>Name</Form.Label>
<Form.Control
type="text"
value={name}
onChange={e => setName(e.target.value)}
required/>
</Form.Group>
<Form.Group>
<Form.Label>Description</Form.Label>
<Form.Control
type="text"
value={description}
onChange={e => setDescription(e.target.value)}
required/>
</Form.Group>
<Form.Group>
<Form.Label>Price</Form.Label>
<Form.Control
type="number"
value={price}
onChange={e => setPrice(e.target.value)}
required/>
</Form.Group>
</Modal.Body>
<Modal.Footer>
<Button variant="secondary" onClick={closeEdit}>Close</Button>
<Button variant="success" type="submit">Submit</Button>
</Modal.Footer>
</Form>
</Modal>
</>
)
}

@ -0,0 +1,60 @@
import { useState, useEffect } from 'react'
import { CardGroup } from 'react-bootstrap'
import PreviewCourses from './PreviewCourses'
export default function FeaturedCourses(){
const [previews, setPreviews] = useState([])
useEffect(() => {
fetch(`${ process.env.REACT_APP_API_URL}/courses/`)
.then(res => res.json())
.then(data => {
console.log(data)
// Create two empty arrays to be used to store random numbers and featured course data.
const numbers = []
const featured = []
// This function generates a random number between 0 and the length of the data array (the fetched course data). It checks if the random number has already been added to the numbers array. If not, it adds the random number to the numbers array. If the random number already exists in the numbers array, it recursively calls itself to generate a new random number.
const generateRandomNums = () => {
let randomNum = Math.floor(Math.random() * data.length)
if(numbers.indexOf(randomNum) === -1){
numbers.push(randomNum)
}else{
generateRandomNums()
}
}
// A loop is used to iterate five times (from 0 to 4). Inside the loop, the generateRandomNums function is called to generate a random number and push it into the numbers array.
for(let i = 0; i < 5; i++){
generateRandomNums()
// For each iteration of the loop, the PreviewCourses component is rendered with the corresponding course data from the data array based on the random number. The key prop is set to the _id of the course for React's reconciliation to track components efficiently. The resulting JSX is pushed into the featured array.
featured.push(
// the breakPoint here is for columns
<PreviewCourses data={data[numbers[i]]} key={data[numbers[i]]._id} breakPoint={2} />
)
}
// After the loop finishes, the setPreviews function is called to update the state of the component with the featured array. This state update triggers a re-render, and the PreviewCourses components are displayed on the page.
setPreviews(featured)
})
}, [])
return(
<>
<h2 className="text-center">Featured Courses</h2>
<CardGroup className="justify-content-center">
{/*add the state here*/}
{previews}
</CardGroup>
</>
)
}

@ -1,44 +1,45 @@
import { Row, Col, Card } from "react-bootstrap";
import { Row, Col, Card } from 'react-bootstrap';
export default function Highlights() {
return (
<Row className="mt-3 mb-3">
<Col xs={12} md={4}>
<Card className="cardHighlight p-3">
<Card.Body>
<Card.Title>
<h2>Learn from Home</h2>
</Card.Title>
<Card.Text>
Pariatur adipisicing aute do amet dolore cupidatat. Eu labore aliqua eiusmod commodo occaecat mollit ullamco labore minim. Minim irure fugiat anim ea sint consequat fugiat laboris id. Lorem elit irure mollit officia incididunt ea ullamco laboris excepteur amet. Cillum pariatur consequat adipisicing aute ex.
</Card.Text>
</Card.Body>
</Card>
</Col>
<Col xs={12} md={4}>
<Card className="cardHighlight p-3">
<Card.Body>
<Card.Title>
<h2>Study Now, Pay Later</h2>
</Card.Title>
<Card.Text>
Ex Lorem cillum consequat ad. Consectetur enim sunt amet sit nulla dolor exercitation est pariatur aliquip minim. Commodo velit est in id anim deserunt ullamco sint aute amet. Adipisicing est Lorem aliquip anim occaecat consequat in magna nisi occaecat consequat et. Reprehenderit elit dolore sunt labore qui.
</Card.Text>
</Card.Body>
</Card>
</Col>
<Col xs={12} md={4}>
<Card className="cardHighlight p-3">
<Card.Body>
<Card.Title>
<h2>Be Part of Our Community</h2>
</Card.Title>
<Card.Text>
Minim nostrud dolore consequat ullamco minim aliqua tempor velit amet. Officia occaecat non cillum sit incididunt id pariatur. Mollit tempor laboris commodo anim mollit magna ea reprehenderit fugiat et reprehenderit tempor. Qui ea Lorem dolor in ad nisi anim. Culpa adipisicing enim et officia exercitation adipisicing.
</Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
)
}
return (
<Row className="mt-3 mb-3">
<Col xs={12} md={4}>
<Card className="cardHighlight p-3">
<Card.Body>
<Card.Title>
<h2>Learn from Home</h2>
</Card.Title>
<Card.Text>
Pariatur adipisicing aute do amet dolore cupidatat. Eu labore aliqua eiusmod commodo occaecat mollit ullamco labore minim. Minim irure fugiat anim ea sint consequat fugiat laboris id. Lorem elit irure mollit officia incididunt ea ullamco laboris excepteur amet. Cillum pariatur consequat adipisicing aute ex.
</Card.Text>
</Card.Body>
</Card>
</Col>
<Col xs={12} md={4}>
<Card className="cardHighlight p-3">
<Card.Body>
<Card.Title>
<h2>Study Now, Pay Later</h2>
</Card.Title>
<Card.Text>
Ex Lorem cillum consequat ad. Consectetur enim sunt amet sit nulla dolor exercitation est pariatur aliquip minim. Commodo velit est in id anim deserunt ullamco sint aute amet. Adipisicing est Lorem aliquip anim occaecat consequat in magna nisi occaecat consequat et. Reprehenderit elit dolore sunt labore qui.
</Card.Text>
</Card.Body>
</Card>
</Col>
<Col xs={12} md={4}>
<Card className="cardHighlight p-3">
<Card.Body>
<Card.Title>
<h2>Be Part of Our Community</h2>
</Card.Title>
<Card.Text>
Minim nostrud dolore consequat ullamco minim aliqua tempor velit amet. Officia occaecat non cillum sit incididunt id pariatur. Mollit tempor laboris commodo anim mollit magna ea reprehenderit fugiat et reprehenderit tempor. Qui ea Lorem dolor in ad nisi anim. Culpa adipisicing enim et officia exercitation adipisicing.
</Card.Text>
</Card.Body>
</Card>
</Col>
</Row>
)
}

@ -0,0 +1,34 @@
import { Col, Card } from 'react-bootstrap'
import { Link } from 'react-router-dom'
export default function Product(props){
// props is used here to get the data and breakPoint from the FeaturedCourses.js
const { breakPoint, data } = props
// Destructure the courses data here
const { _id, name, description, price } = data
return(
<Col xs={12} md={ breakPoint }>
{/*add spacing for x-axis*/}
<Card className="cardHighlight mx-2">
<Card.Body>
<Card.Title className="text-center">
{/*Add the specific details of course link*/}
<Link to={`/courses/${ _id}`}>{ name }</Link>
</Card.Title>
<Card.Text>{ description }</Card.Text>
</Card.Body>
<Card.Footer>
<h5 className="text-center">{ price }</h5>
<Link className="btn btn-primary d-block" to={`/courses/${ _id}`}>Details</Link>
</Card.Footer>
</Card>
</Col>
)
}

@ -0,0 +1,80 @@
import React, { useState } from 'react';
const ResetPassword = () => {
const [password, setPassword] = useState('');
const [confirmPassword, setConfirmPassword] = useState('');
const [message, setMessage] = useState('');
const handleResetPassword = async (e) => {
e.preventDefault();
if (password !== confirmPassword) {
setMessage('Passwords do not match');
return;
}
try {
const token = localStorage.getItem('token'); // Replace with your actual JWT token
const response = await fetch(`${process.env.REACT_APP_API_URL}/users/reset-password`, {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
},
body: JSON.stringify({ newPassword: password }),
});
if (response.ok) {
setMessage('Password reset successfully');
setPassword('');
setConfirmPassword('');
} else {
const errorData = await response.json();
setMessage(errorData.message);
}
} catch (error) {
setMessage('An error occurred. Please try again.');
console.error(error);
}
};
return (
<div className="container">
<h2>Reset Password</h2>
<form onSubmit={handleResetPassword}>
<div className="mb-3">
<label htmlFor="password" className="form-label">
New Password
</label>
<input
type="password"
className="form-control"
id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</div>
<div className="mb-3">
<label htmlFor="confirmPassword" className="form-label">
Confirm Password
</label>
<input
type="password"
className="form-control"
id="confirmPassword"
value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)}
required
/>
</div>
{message && <div className="alert alert-danger">{message}</div>}
<button type="submit" className="btn btn-primary">
Reset Password
</button>
</form>
</div>
);
};
export default ResetPassword;

@ -0,0 +1,34 @@
import { useState, useEffect } from 'react';
import CourseCard from './CourseCard';
import CourseSearch from './CourseSearch';
export default function UserView({coursesData}) {
const [courses, setCourses] = useState([])
useEffect(() => {
const coursesArr = coursesData.map(course => {
//only render the active courses since the route used is /all from Course.js page
if(course.isActive === true) {
return (
<CourseCard courseProp={course} key={course._id}/>
)
} else {
return null;
}
})
//set the courses state to the result of our map function, to bring our returned course component outside of the scope of our useEffect where our return statement below can see.
setCourses(coursesArr)
}, [coursesData])
return(
<>
<CourseSearch />
{ courses }
</>
)
}

@ -21,17 +21,17 @@ const coursesData = [
onOffer: true
},
{
id: "wdc003",
name: "Java - React",
id: "wdc004",
name: "JavaScript - React",
description: "Proident est adipisicing est deserunt cillum dolore. Fugiat incididunt quis aliquip ut aliquip est mollit officia dolor ea cupidatat velit. Consectetur aute velit aute ipsum quis. Eiusmod dolor exercitation dolor mollit duis velit aliquip dolor proident ex exercitation labore cupidatat. Eu aliquip mollit labore do.",
price: 55000,
price: 60000,
onOffer: true
},
{
id: "wdc003",
name: "Java - Express",
id: "wdc005",
name: "JavaScript - Express",
description: "Proident est adipisicing est deserunt cillum dolore. Fugiat incididunt quis aliquip ut aliquip est mollit officia dolor ea cupidatat velit. Consectetur aute velit aute ipsum quis. Eiusmod dolor exercitation dolor mollit duis velit aliquip dolor proident ex exercitation labore cupidatat. Eu aliquip mollit labore do.",
price: 55000,
price: 50000,
onOffer: true
}
]

@ -12,19 +12,3 @@ root.render(
</React.StrictMode>
);
const name = 'John Smith';
const user = {
firstName: "Jane",
lastName: "Lovelace"
}
function formatName(user) {
return user.firstName + ' ' + user.lastName
}
const element = <h1>Hello there, {formatName(user)} </h1>; // JSX - Javascript XML
// const root = ReactDOM.createRoot(document.getElementById('root'));
// root.render(element);

@ -0,0 +1,121 @@
import React, { useContext, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import Container from 'react-bootstrap/Container';
import Form from 'react-bootstrap/Form';
import Button from 'react-bootstrap/Button';
import UserContext from '../UserContext';
import Swal from 'sweetalert2';
const AddCourse = () => {
const { user } = useContext(UserContext);
const navigate = useNavigate();
const [courseData, setCourseData] = useState({
name: '',
description: '',
price: 0,
// Add other course-related fields here
});
const [isCourseAdded, setCourseAdded] = useState(false);
const handleInputChange = (e) => {
const { name, value } = e.target;
setCourseData((prevData) => ({
...prevData,
[name]: value,
}));
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch(`${process.env.REACT_APP_API_URL}/courses/add-course/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${localStorage.getItem('token')}`, // Assuming you have a token-based authentication
},
body: JSON.stringify(courseData),
});
if (response.ok) {
setCourseAdded(true);
Swal.fire({
icon: "success",
title: "Courses added"
})
} else {
console.error('Failed to add the course');
// Optionally handle error response here
Swal.fire({
icon: "error",
title: "Failed to add the course"
})
}
} catch (error) {
console.error('Error adding course:', error);
// Optionally handle network or other errors here
}
};
if (!user || !user.isAdmin) {
navigate('/');
return null;
}
if (isCourseAdded) {
navigate('/courses');
return null;
}
return (
<Container>
<h2>Add New Course</h2>
<Form onSubmit={handleSubmit}>
<Form.Group controlId="name">
<Form.Label>Course Name</Form.Label>
<Form.Control
type="text"
placeholder="Enter the course name"
name="name"
value={courseData.name}
onChange={handleInputChange}
required
/>
</Form.Group>
<Form.Group controlId="description">
<Form.Label>Description</Form.Label>
<Form.Control
as="textarea"
placeholder="Enter the course description"
name="description"
value={courseData.description}
onChange={handleInputChange}
required
/>
</Form.Group>
<Form.Group controlId="price">
<Form.Label>Price</Form.Label>
<Form.Control
type="number"
placeholder="Enter the course price"
name="price"
value={courseData.price}
onChange={handleInputChange}
required
/>
</Form.Group>
{/* Add other form fields for additional course information */}
<Button variant="primary" type="submit">
Add Course
</Button>
</Form>
</Container>
);
};
export default AddCourse;

@ -0,0 +1,96 @@
import { useState, useEffect, useContext } from 'react';
import { Container, Card, Button, Row, Col } from 'react-bootstrap';
import { useParams, useNavigate, Link } from 'react-router-dom';
import Swal from 'sweetalert2';
import UserContext from '../UserContext'
export default function CourseView() {
// useParams hook allows us to retrieve the courseId passed via the URL
const { courseId } = useParams();
const { user } = useContext(UserContext);
// Allow us to redirect to different pages in our functions
const navigate = useNavigate();
const [name, setName] = useState("");
const [description, setDescription] = useState("");
const [price, setPrice] = useState(0);
const enroll = (coursId) => {
fetch(`${ process.env.REACT_APP_API_URL }/users/enroll`, {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${localStorage.getItem('token')}`
},
body: JSON.stringify({
courseId: courseId
})
})
.then(res => res.json())
.then(data => {
console.log(data)
if(data.message === 'Enrolled Successfully!') {
Swal.fire({
title: "Successfully Enrolled",
icon: "success",
text: "You have successfully enrolled for this course."
})
navigate("/courses");
} else {
Swal.fire({
title: "Something went wrong",
icon: "error",
text: "Please try again."
})
}
})
}
useEffect(() => {
fetch(`${ process.env.REACT_APP_API_URL }/courses/${courseId}`)
.then(res => res.json())
.then(data => {
setName(data.name);
setDescription(data.description);
setPrice(data.price)
})
}, [courseId]);
return (
<Container className="mt-5">
<Row>
<Col lg={{ span: 6, offset: 3 }}>
<Card>
<Card.Body className="text-center">
<Card.Title>{name}</Card.Title>
<Card.Subtitle>Description:</Card.Subtitle>
<Card.Text>{description}</Card.Text>
<Card.Subtitle>Price:</Card.Subtitle>
<Card.Text>PhP {price}</Card.Text>
<Card.Subtitle>Class Schedule</Card.Subtitle>
<Card.Text>8 am - 5 pm</Card.Text>
{(user.id !== null) ?
<Button variant="primary" block onClick={() => enroll(courseId)}>Enroll</Button>
:
<Link className="btn btn-danger btn-block" to="/login">Log in to Enroll</Link>
}
</Card.Body>
</Card>
</Col>
</Row>
</Container>
)
}

@ -1,21 +1,67 @@
import { useEffect, useState, useContext } from 'react';
import CourseCard from '../components/CourseCard';
// import coursesData from '../data/coursesData';
import UserContext from '../UserContext';
import UserView from '../components/UserView';
import AdminView from '../components/AdminView';
import CourseCard from "../components/CourseCard"
import coursesData from "../data/courseData"
export default function Courses() {
console.log(coursesData);
console.log(coursesData[0]);
const courses = coursesData.map(course => {
return (
<CourseCard courseProp={course} />
)
})
// Component Body
return (
<>
{courses}
</>
)
}
// console.log(coursesData);
// console.log(coursesData[0])
const { user } = useContext(UserContext);
const [courses, setCourses] = useState([]);
const fetchData = () => {
fetch(`${process.env.REACT_APP_API_URL}/courses/all`)
.then(res => res.json())
.then(data => {
console.log(data);
// Sets the "courses" state to map the data retrieved from the fetch request into several "CourseCard" components
setCourses(data);
});
}
// Retrieves the courses from the database upon initial render of the "Courses" component
useEffect(() => {
fetchData()
}, []);
// The "map" method loops through the individual course objects in our array and returns a component for each course
// Multiple components created through the map method must have a unique key that will help React JS identify which components/elements have been changed, added or removed
// Everytime the map method loops through the data, it creates a "CourseCard" component and then passes the current element in our coursesData array using the courseProp
// const courses = coursesData.map(course => {
// // The "courseProp" in the CourseCard component is called a "prop" which is a shorthand for "property" since components are considered as objects in React JS
// // The curly braces ({}) are used for props to signify that we are providing information using JavaScript expressions rather than hard coded values which use double quotes ("")
// // We can pass information from one component to another using props. This is referred to as "props drilling"
// return (
// <CourseCard courseProp={course} />
// )
// })
// Component Body
return (
<>
{
(user.isAdmin === true) ?
<AdminView coursesData={courses} />
:
<UserView coursesData={courses} />
}
</>
)
}

@ -0,0 +1,15 @@
import Banner from '../components/Banner';
export default function Error() {
const data = {
title: "404 - Not found",
content: "The page you are looking for cannot be found",
destination: "/",
label: "Back home"
}
return (
<Banner data={data}/>
)
}

@ -1,11 +1,20 @@
import Banner from "../components/Banner";
import Highlights from "../components/Highlights";
import Banner from '../components/Banner';
import FeaturedCourses from '../components/FeaturedCourses';
import Highlights from '../components/Highlights';
export default function Home() {
return (
<>
<Banner />
<Highlights />
</>
);
}
const data = {
title: "Zuitt Coding Bootcamp",
content: "Opportunities for everyone, everywhere",
destination: "/courses",
label: "Enroll now!"
}
return (
<>
<Banner data={data}/>
<FeaturedCourses/>
<Highlights />
</>
)
}

@ -1,97 +1,140 @@
import React, { useState } from "react";
import { Form, Button } from "react-bootstrap";
const Login = () => {
const [email, setEmail] = useState("");
const [password, setPassword] = useState("");
const [isValid, setIsValid] = useState(false);
const checkFormValidity = () => {
// Simple validation for demonstration purposes
const isValid = email !== "" && password !== "";
setIsValid(isValid);
};
const handleInputChange = (e) => {
const { name, value } = e.target;
// Update state based on the input field name
switch (name) {
case "email":
setEmail(value);
break;
case "password":
setPassword(value);
break;
default:
break;
}
};
const handleSubmit = async (e) => {
e.preventDefault();
try {
const response = await fetch("http://localhost:4000/users/login", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
email,
password,
}),
});
if (response.ok) {
alert("Thank you for logging in");
console.log("Login success")
// Handle successful login, e.g., redirect to another page
} else {
alert("Unsucessful login");
// Handle invalid credentials, e.g., show an error message
}
} catch (error) {
console.error("Error during login:", error);
// Handle other errors, e.g., network issues
}
};
return (
<Form onSubmit={handleSubmit}>
<h1 className="my-5 text-center">Login</h1>
<Form.Group>
<Form.Label>Email Address:</Form.Label>
<Form.Control
type="email"
placeholder="Enter Email"
required
name="email"
value={email}
onChange={(e) => {
handleInputChange(e);
checkFormValidity();
}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Password:</Form.Label>
<Form.Control
type="password"
placeholder="Enter Password"
required
name="password"
value={password}
onChange={(e) => {
handleInputChange(e);
checkFormValidity();
}}
/>
</Form.Group>
<Button variant="success" type="submit" disabled={!isValid}>
Login
</Button>
</Form>
);
};
export default Login;
import { useState, useEffect, useContext } from 'react';
import { Form, Button } from 'react-bootstrap';
import Swal from 'sweetalert2';
import UserContext from '../UserContext';
import { Navigate } from 'react-router-dom';
export default function Login() {
// Allows us to consume the User context and it's properties to use for validation
const { user, setUser } = useContext(UserContext);
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [isActive, setIsActive] = useState(false);
function authenticate(e) {
// Prevent page redirection via form submission
e.preventDefault()
fetch(`${process.env.REACT_APP_API_URL}/users/login`, {
method: "POST",
headers: {
"Content-Type": "application/json"
},
body: JSON.stringify({
email: email,
password: password
})
})
.then(res => res.json())
.then(data => {
if(data.access){
// Set the token of the authenticated user in the local storage
/*
Syntax:
localStorage.setItem('propertyName', value);
*/
localStorage.setItem("token", data.access);
// function for retrieving user details
retrieveUserDetails(data.access);
Swal.fire({
title: "Login Successful",
icon: "success",
text: "Welcome to Zuitt"
});
} else {
Swal.fire({
title: "Authentication failed",
icon: "error",
text: "Check your login details and try again"
});
}
})
// Clearing our input fields after submission
setEmail('');
setPassword('');
}
const retrieveUserDetails = (token) => {
fetch(`${process.env.REACT_APP_API_URL}/users/details`, {
headers: {
Authorization: `Bearer ${token}`
}
})
.then(res => res.json())
.then(data => {
console.log(data);
setUser({
id: data._id,
isAdmin: data.isAdmin
})
})
}
useEffect(() => {
if(email !== '' && password !== '') {
setIsActive(true);
} else {
setIsActive(false);
}
}, [email, password])
return (
(user.id !== null) ?
<Navigate to='/courses' />
:
<Form onSubmit={(e) => authenticate(e)} >
<h1 className="my-5 text-center">Login</h1>
<Form.Group controlId="userEmail">
<Form.Label>Email address</Form.Label>
<Form.Control
type="email"
placeholder="Enter email"
value={email}
onChange={(e) => setEmail(e.target.value)}
required
/>
</Form.Group>
<Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control
type="password"
placeholder="Password"
value={password}
onChange={(e) => setPassword(e.target.value)}
required
/>
</Form.Group>
{ isActive ?
<Button variant="primary" type="submit" id="submitBtn">
Submit
</Button>
:
<Button variant="danger" type="submit" id="submitBtn" disabled>
Submit
</Button>
}
</Form>
)
}

@ -0,0 +1,22 @@
import { useContext, useEffect } from 'react';
import { Navigate } from 'react-router-dom';
import UserContext from '../UserContext';
export default function Logout() {
const { unsetUser, setUser } = useContext(UserContext);
unsetUser();
useEffect(() => {
setUser({
id: null,
isAdmin: null
})
})
// Redirect back to login
return (
<Navigate to='/login' />
)
}

@ -0,0 +1,20 @@
import Banner from '../components/Banner';
export default function NotFound(){
const data = {
title: "404 - Not found",
content: "Sorry, the page you are looking for does not exist.",
destination: "/",
label: "Back home"
}
return(
<Banner data={data} />
)
}

@ -0,0 +1,83 @@
import { useEffect, useContext, useState } from 'react';
import { Row, Col } from 'react-bootstrap';
import UserContext from '../UserContext';
import { Navigate } from 'react-router-dom';
import ResetPassword from '../components/ResetPassword';
export default function Profile() {
const { user } = useContext(UserContext);
const [profileData, setProfileData] = useState(null);
useEffect(() => {
if (user && user.id) {
// Fetch user profile data when the component mounts
fetchUserProfile();
}
}, [user]);
const fetchUserProfile = async () => {
try {
const response = await fetch(
`${process.env.REACT_APP_API_URL}/users/details`,
{
headers: {
Authorization: `Bearer ${localStorage.getItem('token')}`,
},
}
);
if (response.ok) {
const data = await response.json();
console.log('Profile Data:', data);
if (data && data.firstName && data.lastName && data.email && data.mobileNo) {
setProfileData(data);
} else {
console.error('Invalid user profile data received from the server.');
}
} else {
console.error('Failed to fetch user profile');
}
} catch (error) {
console.error('Error fetching user profile:', error);
}
};
if (!user || user.id === null) {
return <Navigate to="/courses" />;
}
return (
<>
<Row>
<Col className="p-5 bg-primary text-white">
<h1 className="my-5">Profile</h1>
{profileData ? (
<>
<h2 className="mt-3">
{profileData.firstName} {profileData.lastName}
</h2>
<hr />
<h4>Contacts</h4>
<ul>
<li>
Name: {profileData.firstName} {profileData.lastName}
</li>
<li>Email: {profileData.email}</li>
<li>Mobile No: {profileData.mobileNo}</li>
</ul>
</>
) : (
<p>Loading profile...</p>
)}
</Col>
</Row>
<Row className="pt-4 mt-4">
<Col>
<ResetPassword />
</Col>
</Row>
</>
);
}

@ -1,8 +1,12 @@
import { useState, useEffect } from 'react';
import { useState, useEffect, useContext } from 'react';
import { Form, Button } from 'react-bootstrap';
import { Navigate } from 'react-router-dom';
import UserContext from '../UserContext';
export default function Register() {
const { user } = useContext(UserContext);
// State hooks to store the values of the input fields
const [ firstName, setFirstName ] = useState("");
const [ lastName, setLastName ] = useState("");
@ -25,7 +29,7 @@ export default function Register() {
e.preventDefault();
fetch("http://localhost:4000/users/register", {
fetch(`${process.env.REACT_APP_API_URL}/users/register`, {
method: "POST",
headers: {
"Content-Type": "application/json"
@ -40,6 +44,7 @@ export default function Register() {
})
.then(res => res.json())
.then(data => {
console.log(data)
if (data) {
@ -72,80 +77,83 @@ export default function Register() {
return (
<Form onSubmit={(e) => registerUser(e)}>
<h1 className="my-5 text-center">Register</h1>
<Form.Group>
<Form.Label>First Name:</Form.Label>
<Form.Control
type="text"
placeholder="Enter First Name"
required
value={firstName}
onChange={e => {setFirstName(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Last Name:</Form.Label>
<Form.Control
type="text"
placeholder="Enter Last Name"
required
value={lastName}
onChange={e => {setLastName(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Email:</Form.Label>
<Form.Control
type="email"
placeholder="Enter Email"
required
value={email}
onChange={e => {setEmail(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Mobile No:</Form.Label>
<Form.Control
type="number"
placeholder="Enter 11 Digit No."
required
value={mobileNo}
onChange={e => {setMobileNo(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Password:</Form.Label>
<Form.Control
type="password"
placeholder="Enter Password"
required
value={password}
onChange={e => {setPassword(e.target.value)}}
(user.id !== null) ?
<Navigate to='/courses' />
:
<Form onSubmit={(e) => registerUser(e)}>
<h1 className="my-5 text-center">Register</h1>
<Form.Group>
<Form.Label>First Name:</Form.Label>
<Form.Control
type="text"
placeholder="Enter First Name"
required
value={firstName}
onChange={e => {setFirstName(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Confirm Password:</Form.Label>
<Form.Control
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={e => {setConfirmPassword(e.target.value)}}
</Form.Group>
<Form.Group>
<Form.Label>Last Name:</Form.Label>
<Form.Control
type="text"
placeholder="Enter Last Name"
required
value={lastName}
onChange={e => {setLastName(e.target.value)}}
/>
</Form.Group>
{/*conditionally renders the submit button based on the isActive state*/}
{
isActive
?
<Button variant="primary" type="submit" id="submitBtn">Submit</Button>
:
<Button variant="danger" type="submit" id="submitBtn" disabled>Submit</Button>
}
</Form.Group>
<Form.Group>
<Form.Label>Email:</Form.Label>
<Form.Control
type="email"
placeholder="Enter Email"
required
value={email}
onChange={e => {setEmail(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Mobile No:</Form.Label>
<Form.Control
type="number"
placeholder="Enter 11 Digit No."
required
value={mobileNo}
onChange={e => {setMobileNo(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Password:</Form.Label>
<Form.Control
type="password"
placeholder="Enter Password"
required
value={password}
onChange={e => {setPassword(e.target.value)}}
/>
</Form.Group>
<Form.Group>
<Form.Label>Confirm Password:</Form.Label>
<Form.Control
type="password"
placeholder="Confirm Password"
required
value={confirmPassword}
onChange={e => {setConfirmPassword(e.target.value)}}
/>
</Form.Group>
{/*conditionally renders the submit button based on the isActive state*/}
{
isActive
?
<Button variant="primary" type="submit" id="submitBtn">Submit</Button>
:
<Button variant="danger" type="submit" id="submitBtn" disabled>Submit</Button>
}
</Form>
</Form>
)
}

@ -0,0 +1 @@
Subproject commit a9253e2e4f52925478c882822da78456d1526540
Loading…
Cancel
Save