diff --git a/backend-lambda/OAuthProcessing/index.mjs b/backend-lambda/OAuthProcessing/index.mjs index 9c0935d..cbf6b64 100644 --- a/backend-lambda/OAuthProcessing/index.mjs +++ b/backend-lambda/OAuthProcessing/index.mjs @@ -11,12 +11,12 @@ export const handler = async (event, context) => { process.env.CLIENT_SECRET, process.env.OAUTH_CALLBACK_URL ); - const { code } = event['queryStringParameters']; - const { tokens } = await oauthClient.getToken(code); + const {code} = event['queryStringParameters']; + const {tokens} = await oauthClient.getToken(code); const url = getAccessAndBearerTokenUrl(tokens.access_token); const myHeaders = new Headers(); - const bearerToken = "Bearer "+tokens.id_token; + const bearerToken = "Bearer " + tokens.id_token; myHeaders.append("Authorization", bearerToken); const reqOptions = { @@ -24,33 +24,37 @@ export const handler = async (event, context) => { headers: myHeaders, redirect: 'follow' }; - return await fetch(url, reqOptions) - .then(response => response.json()) - .then(res => { - let user = updateOrCreateUserFromOAuth(res); - console.log("RES " + JSON.stringify(res)); - const payload = { - name: res.name, - email: res.email + + try { + let response = await fetch(url, reqOptions); + console.log(response) + let res = await response.json(); + console.log(res) + await updateOrCreateUserFromOAuth(res); + const payload = { + name: res.name, + email: res.email + } + console.log("PAYLOAD " + JSON.stringify(payload)); + const token = jwt.sign( payload, JWTSecret, {expiresIn: '2d'} ); + console.log("TOKEN " + token); + console.log("VERIFY " + jwt.verify(token, JWTSecret)); + console.log("DECODE " + jwt.decode(token, JWTSecret).name + " " + jwt.decode(token, JWTSecret).email); + + return { + statusCode: 302, + headers: { + "Location": `${process.env.APP_URL}/login?token=${token}` } - console.log("PAYLOAD " + JSON.stringify(payload)); - const token = jwt.sign( payload, JWTSecret, {expiresIn: '2d'} ); - console.log("TOKEN " + token); - console.log("VERIFY " + jwt.verify(token, JWTSecret)); - console.log("DECODE " + jwt.decode(token, JWTSecret).name + " " + jwt.decode(token, JWTSecret).email); - return { - statusCode: 302, - headers: { - "Location": `${process.env.APP_URL}/login?token=${token}` - } - }; - }).catch(err => { - return { - statusCode: 500, - body: JSON.stringify({ message: `Internal Server Error: ${err.message}` }) - }; - }); -}; + }; + } catch (err) { + return { + statusCode: 500, + body: err.message + }; + + } +} const updateOrCreateUserFromOAuth = async (user) => { const uri = process.env.MONGO_URI; @@ -65,42 +69,36 @@ const updateOrCreateUserFromOAuth = async (user) => { const { name, email } = user; console.log("UPDATE OR CREATE FROM OAUTH -- NAME & EMAIL " + name + " " + email) - return client.connect() - .then(async () => { - const database = client.db("vehicleDB"); - const users = database.collection("users"); - const existingUser = await users.findOne({email}) - if( existingUser ) { - console.log("User exists") - const result = await users.findOneAndUpdate({email}, - { $set: {name, email}}, - { returnDocument: "after"} - ); - console.log("Result: ", result.value.name + " " + result.value.email) - return { - statusCode: 200, - body: JSON.stringify(result.value) - }; - } - else { - console.log("User does not exist") - const result = await users.insertOne( {email, name}); - console.log("Result: ", result.value.name + " " + result.value.email) - return { - statusCode: 200, - body: JSON.stringify(result.value) - }; - } - }).catch(err => { - return { - statusCode: 500, - body: JSON.stringify({ message: `Internal Server Error: ${err.message}` }) - }; - }).finally(() => client.close()); -}; + try { + await client.connect(); + const database = client.db("vehicleDB"); + const users = database.collection("users"); + const existingUser = await users.findOne({email}); + if (existingUser) { + console.log("User exists") + await users.findOneAndUpdate({email}, + {$set: {name, email}}, + {returnDocument: "after"} + ); + console.log("UPDATED USER") + } else { + console.log("User does not exist") + await users.insertOne({email, name}); + console.log("NEW USER ADDED") + + } + } + catch (e) { + console.log(e.message); + } + finally{ + console.log("FINISHED") + await client.close(); + } +} const getAccessAndBearerTokenUrl = (access_token) => { return `https://www.googleapis.com/oauth2/v1/userinfo?alt=json&access_token=${access_token}`; }; \ No newline at end of file diff --git a/frontend/src/App.css b/frontend/src/App.css index 6801192..df150ad 100644 --- a/frontend/src/App.css +++ b/frontend/src/App.css @@ -175,15 +175,11 @@ body, .navbar-nav .nav-link, .navbar-text { height: 100%; } -button, .btn { - background-color: #8c7498 !important; - border-color: #8c7498 !important; - color: white !important; -} + .custom-button-primary, .custom-button-success { - background-color: #2872a7 !important; - border-color: #2872a7 !important; + background-color: #8c7498 !important; + border-color: #8c7498 !important; color: white !important; } diff --git a/frontend/src/pages/App.js b/frontend/src/pages/App.js index 90fbf7d..5359179 100644 --- a/frontend/src/pages/App.js +++ b/frontend/src/pages/App.js @@ -9,6 +9,7 @@ import ManualVehicleHistory from "./maintenance-history/ManualVehicleHistory"; import AddVehicle from "./garage/AddVehicle"; import {Settings} from "./Settings"; import {useState} from "react"; +import Footer from "./Footer"; const ProtectedRoute = () => { const authenticated = localStorage.getItem('token') !== null; @@ -21,24 +22,27 @@ function App() { return ( -
+
- } /> + } /> } /> }> } /> } /> - } /> - } /> + } /> + } /> } /> } /> } /> +
+
+
- ) + ); } export default App; \ No newline at end of file diff --git a/frontend/src/pages/Footer.js b/frontend/src/pages/Footer.js new file mode 100644 index 0000000..f463b12 --- /dev/null +++ b/frontend/src/pages/Footer.js @@ -0,0 +1,15 @@ +import React from 'react'; +import { Container } from 'react-bootstrap'; + +function Footer() { + return ( + + ); +} + +export default Footer; diff --git a/frontend/src/pages/Login.js b/frontend/src/pages/Login.js index c5a0947..56aacfa 100644 --- a/frontend/src/pages/Login.js +++ b/frontend/src/pages/Login.js @@ -48,8 +48,7 @@ export const Login = () => {

Welcome to Driveline

Your personalized tool to help keep your vehicle in top condition.

- - {error &&
{error}
} + {error &&
{error}
}
); diff --git a/frontend/src/pages/NavBar.js b/frontend/src/pages/NavBar.js index 7df7c0f..dc6ae64 100644 --- a/frontend/src/pages/NavBar.js +++ b/frontend/src/pages/NavBar.js @@ -47,7 +47,7 @@ function NavBar() { diff --git a/frontend/src/pages/Settings.js b/frontend/src/pages/Settings.js index c3bcd12..9973700 100644 --- a/frontend/src/pages/Settings.js +++ b/frontend/src/pages/Settings.js @@ -1,8 +1,9 @@ import React, { useState } from 'react'; import Button from 'react-bootstrap/Button'; import Offcanvas from 'react-bootstrap/Offcanvas'; -import {Nav} from "react-bootstrap"; +import {Col, Nav, Row} from "react-bootstrap"; import { useNavigate } from 'react-router-dom'; +import {GearFill} from "react-bootstrap-icons"; export const Settings = (props) => { const [darkMode, setDarkMode] = useState(false); @@ -26,7 +27,17 @@ export const Settings = (props) => { return (
{/**/} - {props.greeting} + + + + + {props.greeting} + + + + + + Settings diff --git a/frontend/src/pages/garage/AddVehicle.js b/frontend/src/pages/garage/AddVehicle.js index d87b8da..f64eb4c 100644 --- a/frontend/src/pages/garage/AddVehicle.js +++ b/frontend/src/pages/garage/AddVehicle.js @@ -3,6 +3,7 @@ import React, { useEffect, useState } from "react"; import { Button, Form, Container, Row, Col } from 'react-bootstrap'; import '../../App.css'; import {useNavigate} from "react-router-dom"; + function AddVehicle() { const [refreshData, setRefreshData] = useState(false); @@ -61,7 +62,7 @@ function AddVehicle() { }, []); const handleChange = async (e) => { - const {name, value} = e.target; + const { name, value } = e.target; setSelectedVehicle(prevSelectedVehicle => ({ ...prevSelectedVehicle, [name]: value @@ -69,13 +70,13 @@ function AddVehicle() { if (name === 'year') { getDropdownValues('makes', value); - setDropdownValues(prev => ({...prev, models: [], engines: [], transmissions: []})); + setDropdownValues(prev => ({ ...prev, models: [], engines: [], transmissions: [] })); } else if (name === 'make') { getDropdownValues('models', selectedVehicle.year, value); - setDropdownValues(prev => ({...prev, engines: [], transmissions: []})); + setDropdownValues(prev => ({ ...prev, engines: [], transmissions: [] })); } else if (name === 'model') { getDropdownValues('engines', selectedVehicle.year, selectedVehicle.make, value); - setDropdownValues(prev => ({...prev, transmissions: []})); + setDropdownValues(prev => ({ ...prev, transmissions: [] })); } else if (name === 'engine') { getDropdownValues('transmissions', selectedVehicle.year, selectedVehicle.make, selectedVehicle.model, value); } @@ -250,9 +251,12 @@ function AddVehicle() { )}
- +
{vehicleAdded &&
Vehicle has been added successfully! diff --git a/frontend/src/pages/garage/Garage.js b/frontend/src/pages/garage/Garage.js index c8c5e21..04343d2 100644 --- a/frontend/src/pages/garage/Garage.js +++ b/frontend/src/pages/garage/Garage.js @@ -40,7 +40,6 @@ function Garage(props) { }, []); useEffect(() => { - const myHeaders = new Headers(); myHeaders.append("Authorization", "Bearer " + localStorage.getItem('token')); @@ -68,12 +67,17 @@ function Garage(props) { return ( - + -

navigate('/garage')} style={{ cursor: 'pointer', color: '#644A77', fontWeight: 'bold' }}> +

navigate('/garage')} + style={{cursor: 'pointer', color: '#644A77', fontWeight: 'bold'}}> {user} Garage

- + +
+ + + Add New Vehicle diff --git a/frontend/src/pages/garage/RemoveVehicle.js b/frontend/src/pages/garage/RemoveVehicle.js index b8007df..54742a3 100644 --- a/frontend/src/pages/garage/RemoveVehicle.js +++ b/frontend/src/pages/garage/RemoveVehicle.js @@ -39,18 +39,18 @@ function RemoveVehicle(props) { return ( <> - + Are you sure you want to delete? - Deletion is irreversible. + Removing this vehicle will also result in the loss of any maintenance history associated with it. - - + + @@ -58,4 +58,4 @@ function RemoveVehicle(props) { ) } -export default RemoveVehicle; \ No newline at end of file + export default RemoveVehicle; \ No newline at end of file diff --git a/frontend/src/pages/garage/Vehicle.js b/frontend/src/pages/garage/Vehicle.js index 4275515..7679af3 100644 --- a/frontend/src/pages/garage/Vehicle.js +++ b/frontend/src/pages/garage/Vehicle.js @@ -141,9 +141,9 @@ function Vehicle(props) { {v.configurations.year} {v.configurations.make} {v.configurations.model} - + - +
diff --git a/frontend/src/pages/maintenance-history/ManualVehicleHistory.js b/frontend/src/pages/maintenance-history/ManualVehicleHistory.js index 9884a8b..9796649 100644 --- a/frontend/src/pages/maintenance-history/ManualVehicleHistory.js +++ b/frontend/src/pages/maintenance-history/ManualVehicleHistory.js @@ -49,11 +49,11 @@ function ManualVehicleHistory(props) { return ( <> - + - Manual Maintenance History + Add Maintenance Information
@@ -63,10 +63,15 @@ function ManualVehicleHistory(props) { Type - - - - + + + + + + + + + @@ -82,7 +87,7 @@ function ManualVehicleHistory(props) { - Service + Part diff --git a/frontend/src/pages/vehicle/VehicleHistory.js b/frontend/src/pages/vehicle/VehicleHistory.js index ffa871b..cf77be1 100644 --- a/frontend/src/pages/vehicle/VehicleHistory.js +++ b/frontend/src/pages/vehicle/VehicleHistory.js @@ -1,12 +1,12 @@ -import {Routes, Route, useLocation} from "react-router-dom"; +import {Routes, Route, useLocation, useNavigate} from "react-router-dom"; import VehicleNavbar from "./VehicleNavbar"; import UploadNavbar from "../maintenance-history/UploadNavbar"; -import {useEffect, useRef, useState} from "react"; -import Button from "react-bootstrap/Button"; -import Modal from "react-bootstrap/Modal"; -import {Alert, Col, Form, InputGroup, Row} from "react-bootstrap"; +import { useEffect, useRef, useState } from "react"; +import { Button, Modal, Alert, Form, InputGroup, Container, Row, Col, Table, Card } from "react-bootstrap"; +import ManualVehicleHistory from "../maintenance-history/ManualVehicleHistory"; function VehicleHistory(props) { + const navigate = useNavigate(); const location = useLocation(); const { configId } = location.state; @@ -14,6 +14,35 @@ function VehicleHistory(props) { const [refreshData, setRefreshData] = useState(false); const [maintenance, setMaintenance] = useState(null); + const [vehicleInfo, setVehicleInfo] = useState(null); + const [user, setUser] = useState("Loading..."); + + useEffect(() => { + const fetchUser = async () => { + try { + const token = localStorage.getItem('token'); + if (token) { + const response = await fetch('/api/user', { + headers: { + 'Authorization': `Bearer ${token}` + } + }); + if (response.ok) { + const data = await response.json(); + const firstName = data.name.split(' ')[0]; + setUser(`${firstName}'s`); + } else { + console.error("User not found"); + setUser("User's"); + } + } + } catch (error) { + console.error(error); + setUser("User's"); + } + }; + fetchUser(); + }, []); useEffect(() => { const myHeaders = new Headers(); @@ -24,100 +53,129 @@ function VehicleHistory(props) { headers: myHeaders, redirect: 'follow' }; - fetch('/api/get-vehicle-history?config_id='+configId, reqOptions) - .then( (res) => { + fetch(`/api/get-vehicle-info?configId=${configId}`, reqOptions) + .then((res) => { + if (!res.ok) { + throw new Error('Network response was not ok'); + } + return res.json(); + }) + .then((info) => { + setVehicleInfo(info); + }) + .catch((error) => { + console.error('There has been a problem with your fetch operation:', error); + }); + fetch(`/api/get-vehicle-history?configId=${configId}`, reqOptions) + .then((res) => { if (!res.ok) { throw new Error('Network response was not ok'); } return res.json(); }) - .then( (maintenance) => { - setMaintenance(maintenance[0]); + .then((maintenanceData) => { + setMaintenance(maintenanceData[0]); setLoading(false); }) - .catch( (error) => { + .catch((error) => { console.error('There has been a problem with your fetch operation:', error); }); - }, []); + }, [refreshData]); return ( -
- - - - - - -
-
-
-
-
-
- - -

Maintenance History

- - - - - -
- -
-
-
-
-
-
- + + + +

+ navigate('/garage')} + style={{ cursor: 'pointer', color: '#644A77', fontWeight: 'bold' }}> + {user} Garage + + {' > '} + {vehicleInfo ? ( + navigate(`/garage/vehicle-info/${configId}`, {state: {configId}})} + style={{ cursor: 'pointer', color: '#644A77', fontWeight: 'bold' }}> + {vehicleInfo.year} {vehicleInfo.make} {vehicleInfo.model} + + ) : ( + Loading Vehicle Info... + )} + {' > '} Maintenance History +

+ + + + + + + + + + + + Detailed Maintenance Records + + +
- - - - - + + + + - { - maintenance != null ? - maintenance.completed_maintenance.map((service, index) => { - return ( - - - - - - - - - ) - }) : - - } + {maintenance && maintenance.completed_maintenance.map((service, index) => ( + + + + + + + + ))} -
- TypeDateServiceCost + ActionPartCostDate
{service.type}{service.date}{service.maintenance}{service.cost}
No maintenance history!
{service.type}{service.maintenance}${parseFloat(service.cost).toFixed(2)}{service.date} + + {/* Adding a span with horizontal padding */} + + +
-
-
-
-
-
-
-
+ + + + + + + ); } function UpdateMaintenanceHistory(props) { - - const [loading, setLoading] = useState(true); - const [refreshData, setRefreshData] = useState(false); - const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); + const [refreshData, setRefreshData] = useState(false); const typeText = useRef(); const dateText = useRef(); @@ -145,8 +203,8 @@ function UpdateMaintenanceHistory(props) { redirect: 'follow' }; - fetch("/api/update-maintenance-history?config_id="+props.configId, reqOptions) - .then((res) => res.text()) + fetch(`/api/update-maintenance-history?configId=${props.configId}`, reqOptions) + .then((res) => res.json()) .then((result) => { console.log(result); setRefreshData(!refreshData); @@ -158,7 +216,7 @@ function UpdateMaintenanceHistory(props) { return ( <> - + @@ -173,10 +231,15 @@ function UpdateMaintenanceHistory(props) { Type - - - - + + + + + + + + + @@ -205,7 +268,6 @@ function UpdateMaintenanceHistory(props) { - @@ -220,27 +282,21 @@ function UpdateMaintenanceHistory(props) { } function DeleteMaintenanceHistory(props) { - const [loading, setLoading] = useState(true); - const [refreshData, setRefreshData] = useState(false); - const [show, setShow] = useState(false); const handleClose = () => setShow(false); const handleShow = () => setShow(true); - const handleDelete = (e) => { e.preventDefault(); const myHeaders = new Headers(); myHeaders.append("Content-Type", "application/json"); myHeaders.append("Authorization", "Bearer " + localStorage.getItem('token')); - const raw = JSON.stringify({ "type": props.maintenanceInfo.type, "date": props.maintenanceInfo.date, "maintenance": props.maintenanceInfo.maintenance, "cost": props.maintenanceInfo.cost }); - const reqOptions = { method: "DELETE", headers: myHeaders, @@ -248,11 +304,11 @@ function DeleteMaintenanceHistory(props) { redirect: "follow" }; - fetch("/api/delete-maintenance-history?config_id="+props.configId, reqOptions) + fetch(`/api/delete-maintenance-history?configId=${props.configId}`, reqOptions) .then((response) => response.text()) .then((result) => { - setRefreshData(!refreshData); - window.location.reload(); + console.log(result); + props.setRefreshData(!props.refreshData); }) .catch((error) => console.error(error)); @@ -261,18 +317,18 @@ function DeleteMaintenanceHistory(props) { return ( <> - + Are you sure you want to delete? - Deletion is irreversible. + Once an entry is deleted it cannot be restored. - - + + diff --git a/frontend/src/pages/vehicle/VehicleInfo.js b/frontend/src/pages/vehicle/VehicleInfo.js index 78034b4..236846c 100644 --- a/frontend/src/pages/vehicle/VehicleInfo.js +++ b/frontend/src/pages/vehicle/VehicleInfo.js @@ -22,8 +22,6 @@ function VehicleInfo() { const [email, setEmail] = useState(""); const navigate = useNavigate(); - console.log("email: "+email); - useEffect(() => { const fetchUser = async () => { try { @@ -173,7 +171,6 @@ function VehicleInfo() { }); } - return ( @@ -190,7 +187,8 @@ function VehicleInfo() { @@ -229,7 +227,10 @@ function VehicleInfo() {
- +