S62 Added

master
Ron Reciproco 10 months ago
parent d69f0aeaba
commit d52fa7f45e

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

@ -12,10 +12,13 @@
"@testing-library/react": "^13.4.0", "@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"dotenv": "^16.3.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^1.5.2", "react-bootstrap": "^1.5.2",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sweetalert2": "^11.10.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
} }
}, },
@ -3272,6 +3275,14 @@
"url": "https://opencollective.com/popperjs" "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": { "node_modules/@restart/context": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz", "resolved": "https://registry.npmjs.org/@restart/context/-/context-2.1.4.tgz",
@ -7120,11 +7131,14 @@
} }
}, },
"node_modules/dotenv": { "node_modules/dotenv": {
"version": "10.0.0", "version": "16.3.1",
"resolved": "https://registry.npmjs.org/dotenv/-/dotenv-10.0.0.tgz", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz",
"integrity": "sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q==", "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==",
"engines": { "engines": {
"node": ">=10" "node": ">=12"
},
"funding": {
"url": "https://github.com/motdotla/dotenv?sponsor=1"
} }
}, },
"node_modules/dotenv-expand": { "node_modules/dotenv-expand": {
@ -14929,6 +14943,36 @@
"node": ">=0.10.0" "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": { "node_modules/react-scripts": {
"version": "5.0.1", "version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-5.0.1.tgz", "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": { "node_modules/react-transition-group": {
"version": "4.4.5", "version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
@ -16488,6 +16540,15 @@
"boolbase": "~1.0.0" "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": { "node_modules/symbol-tree": {
"version": "3.2.4", "version": "3.2.4",
"resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "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/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0", "@testing-library/user-event": "^13.5.0",
"bootstrap": "^4.6.0", "bootstrap": "^4.6.0",
"dotenv": "^16.3.1",
"react": "^18.2.0", "react": "^18.2.0",
"react-bootstrap": "^1.5.2", "react-bootstrap": "^1.5.2",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-router-dom": "^6.20.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
"sweetalert2": "^11.10.1",
"web-vitals": "^2.1.4" "web-vitals": "^2.1.4"
}, },
"scripts": { "scripts": {

@ -1,26 +1,102 @@
import AppNavBar from "./components/AppNavBar"; import { useState, useEffect } from 'react';
import { Container } from "react-bootstrap"; import AppNavBar from './components/AppNavBar';
import "./App.css"; 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 // Pages
// import Home from "./pages/Home"; import Home from './pages/Home';
// import Courses from "./pages/Courses"; import Courses from './pages/Courses';
import Register from "./pages/Register"; import Register from './pages/Register';
import Login from "./pages/Login"; 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() { 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 ( return (
// JSX - JavaScript XML Code // JSX - JavaScript XML code
// Fragments // <></> - Fragments
<> <UserProvider value={{ user, setUser, unsetUser }}>
<Router>
<AppNavBar /> <AppNavBar />
<Container> <Container>
{/* <Register/> */} <Routes>
<Login/> <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> </Container>
</> </Router>
</UserProvider>
); );
} }
export default App; 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>
)}
</>
);
}

@ -3,23 +3,57 @@
-Syntax -Syntax
import moduleName from 'filePath'; import moduleName from 'filePath';
*/ */
import { useContext } from 'react';
import Container from 'react-bootstrap/Container'; 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 Nav from 'react-bootstrap/Nav';
import { NavLink } from 'react-router-dom';
import UserContext from '../UserContext';
export default function AppNavBar() { export default function AppNavBar() {
const { user } = useContext(UserContext);
return ( return (
<NavBar bg="light" expand="lg"> <Navbar bg="light" expand="lg">
<Container fluid> <Container fluid>
<NavBar.Brand href="#home">Zuitt Booking</NavBar.Brand> <Navbar.Brand href="#home">Zuitt Booking</Navbar.Brand>
<NavBar.Toggle aria-controls="basic-NavBar-nav"/> <Navbar.Toggle aria-controls="basic-navbar-nav" />
<NavBar.Collapse id="basic-NavBar-nav"> <Navbar.Collapse id="basic-navbar-nav">
<Nav className="ms-auto"> <Nav className="ml-auto">
<Nav.Link href="#home">Home</Nav.Link> <Nav.Link as={NavLink} to="/" exact>
<Nav.Link href="#link">Courses</Nav.Link> 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> </Nav>
</NavBar.Collapse> </Navbar.Collapse>
</Container> </Container>
</NavBar> </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,12 +1,18 @@
import { Button, Row, Col } from 'react-bootstrap'; import { Button, Row, Col } from 'react-bootstrap';
import { Link } from 'react-router-dom';
export default function Banner({data}) {
console.log(data);
const {title, content, destination, label} = data;
export default function Banner() {
return ( return (
<Row> <Row>
<Col> <Col className="p-5 text-center">
<h1>Zuitt Coding Bootcamp</h1> <h1>{title}</h1>
<p>Opportunites for everyone, everywhere.</p> <p>{content}</p>
<Button variant='primary'>Enroll Now</Button> <Link className="btn btn-primary" to={destination}>{label}</Link>
</Col> </Col>
</Row> </Row>
) )

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

@ -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,6 +1,7 @@
import { Row, Col, Card } from "react-bootstrap"; import { Row, Col, Card } from 'react-bootstrap';
export default function Highlights() { export default function Highlights() {
return ( return (
<Row className="mt-3 mb-3"> <Row className="mt-3 mb-3">
<Col xs={12} md={4}> <Col xs={12} md={4}>

@ -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 onOffer: true
}, },
{ {
id: "wdc003", id: "wdc004",
name: "Java - React", 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.", 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 onOffer: true
}, },
{ {
id: "wdc003", id: "wdc005",
name: "Java - Express", 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.", 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 onOffer: true
} }
] ]

@ -12,19 +12,3 @@ root.render(
</React.StrictMode> </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() { export default function Courses() {
console.log(coursesData); // console.log(coursesData);
console.log(coursesData[0]); // console.log(coursesData[0])
const courses = coursesData.map(course => { const { user } = useContext(UserContext);
return (
<CourseCard courseProp={course} />
) 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 // Component Body
return ( return (
<> <>
{courses} {
(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 Banner from '../components/Banner';
import Highlights from "../components/Highlights"; import FeaturedCourses from '../components/FeaturedCourses';
import Highlights from '../components/Highlights';
export default function Home() { export default function Home() {
const data = {
title: "Zuitt Coding Bootcamp",
content: "Opportunities for everyone, everywhere",
destination: "/courses",
label: "Enroll now!"
}
return ( return (
<> <>
<Banner /> <Banner data={data}/>
<FeaturedCourses/>
<Highlights /> <Highlights />
</> </>
); )
} }

@ -1,97 +1,140 @@
import React, { useState } from "react"; import { useState, useEffect, useContext } from 'react';
import { Form, Button } from "react-bootstrap"; import { Form, Button } from 'react-bootstrap';
import Swal from 'sweetalert2';
const Login = () => { import UserContext from '../UserContext';
const [email, setEmail] = useState(""); import { Navigate } from 'react-router-dom';
const [password, setPassword] = useState("");
const [isValid, setIsValid] = useState(false); export default function Login() {
const checkFormValidity = () => { // Allows us to consume the User context and it's properties to use for validation
// Simple validation for demonstration purposes const { user, setUser } = useContext(UserContext);
const isValid = email !== "" && password !== "";
setIsValid(isValid); const [email, setEmail] = useState('');
}; const [password, setPassword] = useState('');
const handleInputChange = (e) => { const [isActive, setIsActive] = useState(false);
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) => { function authenticate(e) {
e.preventDefault();
try { // Prevent page redirection via form submission
const response = await fetch("http://localhost:4000/users/login", { e.preventDefault()
fetch(`${process.env.REACT_APP_API_URL}/users/login`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json", "Content-Type": "application/json"
}, },
body: JSON.stringify({ body: JSON.stringify({
email, email: email,
password, 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"
}); });
if (response.ok) {
alert("Thank you for logging in");
console.log("Login success")
// Handle successful login, e.g., redirect to another page
} else { } else {
alert("Unsucessful login"); Swal.fire({
// Handle invalid credentials, e.g., show an error message title: "Authentication failed",
icon: "error",
text: "Check your login details and try again"
});
} }
} catch (error) { })
console.error("Error during login:", error);
// Handle other errors, e.g., network issues // 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 ( return (
<Form onSubmit={handleSubmit}> (user.id !== null) ?
<Navigate to='/courses' />
:
<Form onSubmit={(e) => authenticate(e)} >
<h1 className="my-5 text-center">Login</h1> <h1 className="my-5 text-center">Login</h1>
<Form.Group> <Form.Group controlId="userEmail">
<Form.Label>Email Address:</Form.Label> <Form.Label>Email address</Form.Label>
<Form.Control <Form.Control
type="email" type="email"
placeholder="Enter Email" placeholder="Enter email"
required
name="email"
value={email} value={email}
onChange={(e) => { onChange={(e) => setEmail(e.target.value)}
handleInputChange(e); required
checkFormValidity();
}}
/> />
</Form.Group> </Form.Group>
<Form.Group>
<Form.Label>Password:</Form.Label> <Form.Group controlId="password">
<Form.Label>Password</Form.Label>
<Form.Control <Form.Control
type="password" type="password"
placeholder="Enter Password" placeholder="Password"
required
name="password"
value={password} value={password}
onChange={(e) => { onChange={(e) => setPassword(e.target.value)}
handleInputChange(e); required
checkFormValidity();
}}
/> />
</Form.Group> </Form.Group>
<Button variant="success" type="submit" disabled={!isValid}>
Login
{ isActive ?
<Button variant="primary" type="submit" id="submitBtn">
Submit
</Button> </Button>
</Form> :
);
};
export default Login; <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 { Form, Button } from 'react-bootstrap';
import { Navigate } from 'react-router-dom';
import UserContext from '../UserContext';
export default function Register() { export default function Register() {
const { user } = useContext(UserContext);
// State hooks to store the values of the input fields // State hooks to store the values of the input fields
const [ firstName, setFirstName ] = useState(""); const [ firstName, setFirstName ] = useState("");
const [ lastName, setLastName ] = useState(""); const [ lastName, setLastName ] = useState("");
@ -25,7 +29,7 @@ export default function Register() {
e.preventDefault(); e.preventDefault();
fetch("http://localhost:4000/users/register", { fetch(`${process.env.REACT_APP_API_URL}/users/register`, {
method: "POST", method: "POST",
headers: { headers: {
"Content-Type": "application/json" "Content-Type": "application/json"
@ -40,6 +44,7 @@ export default function Register() {
}) })
.then(res => res.json()) .then(res => res.json())
.then(data => { .then(data => {
console.log(data) console.log(data)
if (data) { if (data) {
@ -72,6 +77,9 @@ export default function Register() {
return ( return (
(user.id !== null) ?
<Navigate to='/courses' />
:
<Form onSubmit={(e) => registerUser(e)}> <Form onSubmit={(e) => registerUser(e)}>
<h1 className="my-5 text-center">Register</h1> <h1 className="my-5 text-center">Register</h1>
<Form.Group> <Form.Group>

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