0% found this document useful (0 votes)
34 views

Integrating Backend API - Summary Slide

Uploaded by

Pratik Nepal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
34 views

Integrating Backend API - Summary Slide

Uploaded by

Pratik Nepal
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PPTX, PDF, TXT or read online on Scribd
You are on page 1/ 104

Web Application Development

Integrating Backend API


 Users should be able to use the frontend views to fetch
and modify user data in the database based on
authentication and authorization.
 To implement these functionalities, the React
components will access the API endpoints that are
exposed by the backend using the Fetch API.
 The Fetch API is a newer standard that makes network
requests similar to XMLHttpRequest (XHR) but using
promises instead, enabling a simpler and cleaner API.
 To learn more about the Fetch API,
visit
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_
API

11/15/2024 Web Application Development 2


Fetch for user CRUD
 In the client/user/api-user.js file, we will add methods
for accessing each of the user CRUD API endpoints,
which the React components can use to exchange user
data with the server and database as required.

 In the following sections, we will look at the


implementation of these methods and how they
correspond to each CRUD endpoint.

11/15/2024 Web Application Development 3


CREATE – creating new user
 The create method will take user data from the view
component, which is where we will invoke this method.
Then, it will use fetch to make a POST call at the create
API route, '/api/users', to create a new user in the
backend with the provided data.

 Finally, in this method, we return the response from the


server as a promise. So, the component calling this
method can use this promise to handle the response
appropriately, depending on what is returned from the
server.

11/15/2024 Web Application Development 4


LIST – Listing users
 Similarly, we will implement the list method next.
 The list method will use fetch to make a GET call to
retrieve all the users in the database, and then return
the response from the server as a promise to the
component.

 The returned promise, if it resolves successfully, will


give the component an array containing the user
objects that were retrieved from the database.
 In the case of a single user read, we will deal with a
single user object instead, as demonstrated next.

11/15/2024 Web Application Development 5


Reading a user profile
 The read method will use fetch to make a GET call to
retrieve a specific user by ID.
 Since this is a protected route, besides passing the
user ID as a parameter, the requesting component must
also provide valid credentials, which, in this case, will
be a valid JWT received after a successful sign-in.
 mern-skeleton/client/user/api-user.js:

11/15/2024 Web Application Development 6


 The JWT is attached to the GET fetch call in the
Authorization header using the Bearer scheme, and
then the response from the server is returned to the
component in a promise.
 This promise, when it resolves, will either give the
component the user details for the specific user or
notify that access is restricted to authenticated users.
 Similarly, the updated user API method also needs to
be passed valid JWT credentials for the fetch call, as
shown in the next section.

11/15/2024 Web Application Development 7


UPDATE - Updating a user's data
 The update method will take changed user data from
the view component for a specific user, then use fetch
to make a PUT call to update the existing user in the
backend.
 This is also a protected route that will require a valid
JWT as the credential.

11/15/2024 Web Application Development 8


 As we have seen with the other fetch calls, this method
will also return a promise containing the server's
response to the user update request.
 In the final method, we will learn how to call the user
delete API.

11/15/2024 Web Application Development 9


Deleting a user
 The remove method will allow the view component to
delete a specific user from the database and use fetch
to make a DELETE call.
 This, again, is a protected route that will require a valid
JWT as a credential, similar to the read and update
methods.
 mern-skeleton/client/user/api-user.js:

11/15/2024 Web Application Development 10


 The response from the server to the delete request will
be returned to the component as a promise, as in the
other methods.
 In these five helper methods, we have covered calls to
all the user CRUD-related API endpoints that we
implemented on the backend.
 Finally, we can export these methods from the api-
user.js file as follows.
 mern-skeleton/client/user/api-user.js:
export { create, list, read, update, remove }

11/15/2024 Web Application Development 11


Updated
mern-skeleton/client/user/api-user.js:
 const create = async (user) => {
 try {
 let response = await fetch('/api/users/', {
 method: 'POST',
 headers: {
 'Accept': 'application/json',
 'Content-Type': 'application/json'
 },
 body: JSON.stringify(user)
 })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 const list = async (signal) => {
 try {
 let response = await fetch('/api/users/', {
 method: 'GET',
 signal: signal,
 })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 const read = async (params, credentials, signal) => {
 try {
 let response = await fetch('/api/users/' + params.userId, {
 method: 'GET',
 signal: signal,
 headers: {
 'Accept': 'application/json',
 'Content-Type': 'application/json',
 'Authorization': 'Bearer ' + credentials.t
 }
 })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 const update = async (params, credentials, user) => {
 try {
 let response = await fetch('/api/users/' + params.userId, {
 method: 'PUT',
 headers: {
 'Accept': 'application/json',
 'Content-Type': 'application/json',
 'Authorization': 'Bearer ' + credentials.t
 },
 body: JSON.stringify(user)
 })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 const remove = async (params, credentials) => {
 try {
 let response = await fetch('/api/users/' + params.userId, {
 method: 'DELETE',
 headers: {
 'Accept': 'application/json',
 'Content-Type': 'application/json',
 'Authorization': 'Bearer ' + credentials.t
 }
 })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 export { create, list, read, update, remove }

11/15/2024 Web Application Development 12


 These user CRUD methods can now be imported and
used by the React components as required.
 Next, we will implement similar helper methods to
integrate the auth-related API endpoints.

11/15/2024 Web Application Development 13


Fetch for the auth API
 In order to integrate the auth API endpoints from the
server with the frontend
 React components, we will add methods for fetching
sign-in and sign-out API
 endpoints in the client/lib/api-auth.js file. Let's take a
look at them.

11/15/2024 Web Application Development 14


Sign-in
 The signin method will take user sign-in data from the
view component, then use fetch to make a POST call to
verify the user with the backend.
 mern-skeleton/client/lib/api-auth.js:

11/15/2024 Web Application Development 15


 The response from the server will be returned to the
component in a promise, which may provide the JWT if
sign-in was successful.
 The component invoking this method needs to handle
the response appropriately, such as storing the received
JWT locally so it can be used when making calls to
other protected API routes from the frontend.
 We will look at the implementation for this when we
implement the Sign In view later.
 After the user is successfully signed in, we also want
the option to call the signout API when the user is
signing out.
 The call to the signout API is discussed next.
11/15/2024 Web Application Development 16
Sign-out
 We will add a signout method to api-auth.js, which will
use fetch to make a GET call to the signout API
endpoint on the server.
 mern-skeleton/client/lib/api-auth.js:

11/15/2024 Web Application Development 17


 This method will also return a promise to inform the
component about whether the API request was
successful.
 At the end of the api-auth.js file, we will export the
signin and signout methods.

 mern-skeleton/client/lib/api-auth.js:
export { signin, signout }

11/15/2024 Web Application Development 18


Updated mern-skeleton/client/lib/api-
 auth.js:
const signin = async (user) => {
 try {
 let response = await fetch('/auth/signin/', {
 method: 'POST',
 headers: {
 'Accept': 'application/json',
 'Content-Type': 'application/json'
 },
 credentials: 'include',
 body: JSON.stringify(user)
 })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 const signout = async () => {
 try {
 let response = await fetch('/auth/signout/', { method: 'GET' })
 return await response.json()
 } catch(err) {
 console.log(err)
 }
 }
 export { signin, signout }

11/15/2024 Web Application Development 19


 Now, these methods can be imported into the relevant
React components so that we can implement the user
sign-in and signout features.
 With these API fetch methods added, the React
frontend has complete access to the endpoints we
made available in the backend.
 Before we start putting these methods to use in our
React components, we will look into how user auth
state can be maintained across the frontend.

11/15/2024 Web Application Development 20


Adding auth in the frontend
 As we discussed earlier, implementing authentication
with JWT relinquishes responsibility to the client-side to
manage and store user auth state.

 To this end, we need to write code that will allow the


client-side to store the JWT that's received from the
server on successful sign-in, make it available when
accessing protected routes, delete or invalidate the
token when the user signs out, and also restrict access
to views and components on the frontend based on the
user auth state.

11/15/2024 Web Application Development 21


 Using examples of the auth workflow from the React
Router documentation, in the following sections, we will
write helper methods to manage the auth state across
the components, and also use a custom PrivateRoute
component to add protected routes to the frontend of
the MERN skeleton application.

11/15/2024 Web Application Development 22


Managing auth state
 To manage auth state in the frontend of the application,
the frontend needs to be able to store, retrieve, and
delete the auth credentials that are received from the
server on successful user sign in.
 In our MERN applications, we will use the browser's
sessionsStorage as the storage option to store the JWT
auth credentials.
 Alternatively, you can use localStorage instead of
sessionStorage to store the JWT credentials.
 With sessionStorage, the user auth state will only be
remembered in the current window tab.
 With localStorage, the user auth state will be
remembered across tabs in a browser.
11/15/2024 Web Application Development 23
 In client/auth/auth-helper.js, we will define the helper
methods discussed in the following sections to store
and retrieve JWT credentials from client-side
sessionStorage, and also clear out the sessionStorage
on user sign-out.

11/15/2024 Web Application Development 24


Saving credentials
 In order to save the JWT credentials that are received
from the server on successful sign-in, we use the
authenticate method, which is defined as follows.
 mern-skeleton/client/lib/auth-helper.js:

11/15/2024 Web Application Development 25


 The authenticate method takes the JWT credentials,
jwt, and a callback function, cb, as arguments.
 It stores the credentials in sessionStorage after
ensuring window is defined, in other words ensuring
this code is running in a browser and hence has access
to sessionStorage.
 Then, it executes the callback function that is passed in.
This callback will allow the component – in our case,
the component where sign-in is called – to define
actions that should take place after successfully signing
in and storing credentials.
 Next, we will discuss the method that lets us access
these stored credentials.

11/15/2024 Web Application Development 26


Retrieving credentials
 In our frontend components, we will need to retrieve the
stored credentials to check if the current user is signed
in.
 In the isAuthenticated() method, we can retrieve
these credentials from sessionStorage.
mern-skeleton/client/lib/auth-helper.js:

11/15/2024 Web Application Development 27


 A call to isAuthenticated() will return either the stored
credentials or false, depending on whether credentials
were found in sessionStorage.
 Finding credentials in storage will mean a user is signed
in, whereas not finding credentials will mean the user is
not signed in. We will also add a method that allows us
to delete the credentials from storage when a signed-in
user signs out from the application.

11/15/2024 Web Application Development 28


Deleting credentials
 When a user successfully signs out from the
application, we want to clear the stored JWT credentials
from sessionStorage.
 This can be accomplished by calling the clearJWT
method, which is defined in the following code.
mern-skeleton/client/lib/auth-helper.js:

11/15/2024 Web Application Development 29


Updated
mern-skeleton/client/lib/auth-


helper.js:
authenticate(jwt, cb) {
if(typeof window !== "undefined")
 sessionStorage.setItem('jwt', JSON.stringify(jwt))
 cb()
 }
 isAuthenticated() {
 if (typeof window == "undefined")
 return false
 if (sessionStorage.getItem('jwt'))
 return JSON.parse(sessionStorage.getItem('jwt'))
 else
 return false
 }
 clearJWT(cb) {
 if(typeof window !== "undefined")
 sessionStorage.removeItem('jwt')
 cb()
 signout().then((data) => {
 document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
 })
 }

11/15/2024 Web Application Development 30


Updated
mern-skeleton/client/lib/auth-


helper.js:
import { signout } from './api-auth.js'

const auth = {
 isAuthenticated() {
 if (typeof window == "undefined")
 return false

if (sessionStorage.getItem('jwt'))
 return JSON.parse(sessionStorage.getItem('jwt'))
 else
 return false
 },
 authenticate(jwt, cb) {
 if (typeof window !== "undefined")
 sessionStorage.setItem('jwt', JSON.stringify(jwt))
 cb()
 },
 clearJWT(cb) {
 if (typeof window !== "undefined")
 sessionStorage.removeItem('jwt')
 cb()
 //optional
 signout().then((data) => {
 document.cookie = "t=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;"
 })
 }
 }

export default auth

11/15/2024 Web Application Development 31


 This clearJWT method takes a callback function as an
argument, and it removes the JWT credential from
sessionStorage.
 The passed in cb() function allows the component
initiating the signout functionality to dictate what should
happen after a successful sign-out.
 The clearJWT method also uses the signout method we
defined earlier in api-auth.js to call the signout API in
the backend.
 If we had used cookies to store the credentials instead
of sessionStorage, the response to this API call would
be where we clear the cookie, as shown in the
preceding code.

11/15/2024 Web Application Development 32


 Using the signout API call is optional since this is
dependent on whether cookies are used as the
credential storage mechanism.

 With these three methods, we now have ways of


storing, retrieving, and deleting JWT credentials on the
client-side.

 Using these methods, the React components we build


for the frontend will be able to check and manage user
auth state to restrict access in the frontend, as
demonstrated in the following section with the custom
PrivateRoute component.
11/15/2024 Web Application Development 33
The PrivateRoute component
 The code in the file defines the PrivateRoute
component, as shown in the auth flow example at
https://reacttraining.com/react-router/web/example/auth
-workflow, which can be found in the React Router
documentation.
 It will allow us to declare protected routes for the
frontend to restrict view access based on user auth.

11/15/2024 Web Application Development 34


mern-skeleton/client/lib/
PrivateRoute.jsx:
 import React from 'react';
 import { Route, Navigate, useLocation } from 'react-router-dom';
 import auth from './auth-helper';

const PrivateRoute = ({ children, ...rest }) => {
 const location = useLocation();

 return auth.isAuthenticated() ? (
 children
 ) : (
 <Navigate
 to="/signin"
 state={{ from: location }}
 replace
 />
 );
 };

export default PrivateRoute;

11/15/2024 Web Application Development 35


 Components to be rendered in this PrivateRoute will only
load when the user is authenticated, which is determined
by a call to the isAuthenticated method; otherwise, the
user will be redirected to the Signin component.
 We load the components that should have restricted
access, such as the user profile component, in a
PrivateRoute.
 This will ensure that only authenticated users are able to
view the user profile page.
 With the backend APIs integrated and the auth
management helper methods ready for use in the
components, we can now start building the remaining
view components that utilize these methods and complete
the frontend.
11/15/2024 Web Application Development 36
Completing the User frontend
 The React components that will be described in this
section complete the interactive features we defined for
the skeleton by allowing users to view, create, and
modify user data stored in the database with respect to
auth restrictions.
 The components we will implement are as follows:
 Users: To fetch and list all users from the database to
the view
 Signup: To display a form that allows new users to sign
up
 Signin: To display a form that allows existing users to
sign in
 Profile: To display details for a specific user after
retrieving from the database
11/15/2024 Web Application Development 37
 EditProfile: To display details for a specific user and
allow authorized user to update these details
 DeleteUser: To allow an authorized user to delete their
account from the application
 Menu: To add a common navigation bar to each view in
the application.
 For each of these components, we will go over their
unique aspects, as well as how to add them to the
application in the MainRouter.

11/15/2024 Web Application Development 38


The Users component
 The Users component in client/user/Users.jsx shows
the names of all the users that have been fetched from
the database and links each name to the user profile.
 The following component can be viewed by any visitor
to the application and will render at the '/users' route:

11/15/2024 Web Application Development 39


Updated


mern-skeleton/client/user/Users.jsx:
import React from 'react'
import {useState} from 'react'
 import {useEffect} from 'react'
 import { makeStyles } from '@material-ui/core/styles'
 import Card from '@material-ui/core/Card'
 import Paper from '@material-ui/core/Paper'
 import List from '@material-ui/core/List'
 import {list} from './api-user.js'
 import { Link as RouterLink } from 'react-router-dom';
 import Link from '@material-ui/core/Link'
 import ListItem from '@material-ui/core/ListItem'
 import ListItemAvatar from '@material-ui/core/ListItemAvatar'
 import ListItemText from '@material-ui/core/ListItemText'
 import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'
 import IconButton from '@material-ui/core/IconButton'
 import Avatar from '@material-ui/core/Avatar'
 //import Person from '@material-ui/core/Person'
 //import ArrowForward from '@material-ui/core/ArrowForward'
 import CardContent from '@material-ui/core/CardContent'
 import CardMedia from '@material-ui/core/CardMedia'
 import Typography from '@material-ui/core/Typography'
 //import ArrowForward from '@material-ui/core/ArrowForward'
 import ArrowForward from '@material-ui/icons/ArrowForward';
 import unicornbikeImg from './../assets/images/unicornbikeImg.jpg'

const useStyles = makeStyles(theme => ({
 card: {
 // Define your card styles here
 },
 textField: {
 // Define your text field styles here
 },
 error: {
 // Define your error icon styles here
 },
 submit: {
 // Define your submit button styles here
 },
 title: {
 // Define your title styles here
 },
 root: {
 // Define your root styles here
 },
 }));

export default function Users() {

const [users, setUsers] = useState([])
 useEffect(() => {
 const abortController = new AbortController()
 const signal = abortController.signal
 list(signal).then((data) => {
 console.log(data)
 if (data && data.error) {
 console.log(data.error)
 } else {
 console.log(data)
 setUsers(data)
 }
 })
 return function cleanup(){
 abortController.abort()
 }
 }, [])

 const classes = useStyles()


 return (
 <Paper className={classes.root} elevation={4}>
 <Typography variant="h6" className={classes.title}>
 All Users
 </Typography>
 <List dense>
 {users.map((item, i) => {
 return <Link component={RouterLink} to={"/user/" + item._id} key={i}>

 <ListItem button>
 <ListItemAvatar>
 <Avatar>

</Avatar>
 </ListItemAvatar>
 <ListItemText primary={item.name}/>
 <ListItemSecondaryAction>
 <IconButton>
 <ArrowForward/>
 </IconButton>
 </ListItemSecondaryAction>
 </ListItem>
 </Link>
 })}
 </List>
 </Paper>
 )
 }

11/15/2024 Web Application Development 40


Explanation:
1. Imports:
o Import necessary modules from React and Material-UI.
o Import the list method from api-user.js.
2. Styles:
o Use makeStyles to define custom styles for the component.
3. Component:
o Define the Users component using a functional component.
o Use the useState hook to manage the users state.
o Use the useEffect hook to fetch user data when the component mounts.
4. Fetch User Data:
o Inside useEffect, define an AbortController to handle cleanup.
o Call the list method to fetch users and set the users state.
o Handle potential errors by logging them.
11/15/2024 Web Application Development 41
1. Render:
o Return a Paper component containing a Typography component for the
title and a List component to display users.
o Map over the users array to create a list of ListItem components.
o Use Link from Material-UI to wrap ListItem for navigation.
o Include Avatar with a default image and ArrowForward icon for better
UI.
Notes:
 Ensure the unicornbikeImg path is correct for the Avatar source.
 The styles in useStyles can be further customized based on your specific
design requirements.
 This implementation assumes the list function from api-user.js returns a
promise that resolves to the user data.

11/15/2024 Web Application Development 42


 To add this Users component to the React application,
we need to update the MainRouter component with a
Route that renders this component at the '/users' path.

 mern-skeleton/client/MainRouter.jsx:
<Route path="/users" element={Users}/>

11/15/2024 Web Application Development 43


The Signup component
 The Signup component in client/user/Signup.jsx
presents a form with name, email, and password fields
to the user for sign-up at the '/signup' path, as displayed
in the following screenshot:

11/15/2024 Web Application Development 44


Updated
mern-skeleton/client/user/Signup.jsx:

import React, { useState } from 'react';


 import { makeStyles } from '@material-ui/core/styles';
 import { Card, CardContent, Typography, TextField, CardActions, Button, Dialog, DialogTitle, DialogContent, DialogContentText, DialogActions } from '@material-ui/core';
 import { Link } from 'react-router-dom';
 import PropTypes from 'prop-types';
 import { create } from './api-user';

const useStyles = makeStyles(theme => ({
 card: {
 maxWidth: 400,
 margin: '0 auto',
 marginTop: theme.spacing(3),
 padding: theme.spacing(2),
 textAlign: 'center',
 },
 textField: {
 width: '100%',
 marginBottom: theme.spacing(2),
 },
 error: {
 color: 'red',
 },
 submit: {
 margin: '0 auto',
 marginBottom: theme.spacing(2),
 },
 title: {
 fontSize: 18,
 },
 }));

// const create = async (user) => {
 // return { error: null }; // Simulated API call
 // };

export default function Signup() {
 const classes = useStyles();

const [values, setValues] = useState({
 name: '',
 password: '',
 email: '',
 });

const [open, setOpen] = useState(false);

const handleChange = name => event => {
 setValues({ ...values, [name]: event.target.value });
 };

const handleClose = () => {
 setOpen(false);
 };

const clickSubmit = () => {
 const user = {
 name: values.name || undefined,
 email: values.email || undefined,
 password: values.password || undefined,
 };

create(user).then((data) => {
 if (data.error) {
 setValues({ ...values, error: data.error });
 } else {
 setOpen(true);
 }
 });
 };

Signup.propTypes = {
 open: PropTypes.bool.isRequired,
 handleClose: PropTypes.func.isRequired,
 };

return (
 <div>
 <Card className={classes.card}>
 <CardContent>
 <Typography variant="h6" className={classes.title}>
 Sign Up
 </Typography>

 <TextField
 id="name"
 label="Name"
 className={classes.textField}
 value={values.name}
 onChange={handleChange('name')}
 margin="normal"
 />
 <TextField
 id="email"
 label="Email"
 className={classes.textField}
 value={values.email}
 onChange={handleChange('email')}
 margin="normal"
 />
 <TextField
 id="password"
 label="Password"
 className={classes.textField}
 value={values.password}
 onChange={handleChange('password')}
 type="password"
 margin="normal"
 />
 </CardContent>
 <CardActions>
 <Button color="primary" variant="contained" onClick={clickSubmit}
 className={classes.submit}>
 Submit
 </Button>
 </CardActions>
 </Card>

<Dialog open={open} onClose={handleClose}>
 <DialogTitle>New Account</DialogTitle>
 <DialogContent>
 <DialogContentText>
 New account successfully created.
 </DialogContentText>
 </DialogContent>
 <DialogActions>
 <Link to="/Signin">
 <Button color="primary" autoFocus variant="contained" onClick={handleClose}>
 Sign In
 </Button>
 </Link>
 </DialogActions>
 </Dialog>
 </div>
 );
 }

11/15/2024 Web Application Development 45


 On successful account creation, the user is given
confirmation and asked to sign in using this Dialog
component, which links to the Signin component, as
shown in the following screenshot:

11/15/2024 Web Application Development 46


 To add the Signup component to the app, add the
following Route to MainRouter.

11/15/2024 Web Application Development 47


mern-skeleton/client/MainRouter.jsx:
 <Route path="/signup" element={Signup}/>

11/15/2024 Web Application Development 48


Updated
mern-skeleton/client/MainRouter.jsx:
 import React from 'react'
 import {Route, Routes} from 'react-router-dom'
 import Home from './core/Home'
 import Users from './user/Users.jsx'
 import Signup from './user/Signup.jsx'
 const MainRouter = () => {
 return ( <div>
 <Routes>
 <Route exact path="/" element={<Home />} />
 <Route path="/users" element={Users} />
 <Route path="/signup" element={Signup}/>



 </Routes>
 </div>
 )
 }
 export default MainRouter

11/15/2024 Web Application Development 49


 This will render the Signup view at '/signup'. Similarly,
we will implement the Signin component next.

11/15/2024 Web Application Development 50


The Signin component
 The Signin component in client/lib/Signin.jsx is also a
form with only email and password fields for signing in.
 This component is quite similar to the Signup
component and will render at the '/signin' path.
 The key difference is in the implementation of
redirection after a successful sign-in and storing the
received JWT credentials.
 The rendered Signin component can be seen in the
following screenshot:

11/15/2024 Web Application Development 51


11/15/2024 Web Application Development 52
Updated


mern-skeleton/client/lib/Signin.jsx:
import
import
React, {useState} from 'react'
Card from '@material-ui/core/Card'
 import CardActions from '@material-ui/core/CardActions'
 import CardContent from '@material-ui/core/CardContent'
 import Button from '@material-ui/core/Button'
 import TextField from '@material-ui/core/TextField'
 import Typography from '@material-ui/core/Typography'
 import Icon from '@material-ui/core/Icon'
 import { makeStyles } from '@material-ui/core/styles'
 import auth from './auth-helper.js'
 import {Navigate} from 'react-router-dom'
 import { useLocation } from 'react-router-dom';
 import {signin} from './api-auth.js'

const useStyles = makeStyles(theme => ({
 card: {
 maxWidth: 600,
 margin: 'auto',
 textAlign: 'center',
 marginTop: theme.spacing(5),
 paddingBottom: theme.spacing(2)
 },
 error: {
 verticalAlign: 'middle'
 },
 title: {
 marginTop: theme.spacing(2),
 color: theme.palette.openTitle
 },
 textField: {
 marginLeft: theme.spacing(1),
 marginRight: theme.spacing(1),
 width: 300
 },
 submit: {
 margin: 'auto',
 marginBottom: theme.spacing(2)
 }
 }))

export default function Signin(props) {
 const location = useLocation();
 console.log(location.state)
 const classes = useStyles()
 const [values, setValues] = useState({
 email: '',
 password: '',
 error: '',
 redirectToReferrer: false
 })

const clickSubmit = () => {
 const user = {
 email: values.email || undefined,
 password: values.password || undefined
 }
 console.log(user)
 signin(user).then((data) => {
 if (data.error) {
 setValues({ ...values, error: data.error})
 } else {
 console.log(data)
 auth.authenticate(data, () => {
 setValues({ ...values, error: '',redirectToReferrer: true})
 })
 }
 })
 }

const handleChange = name => event => {
 setValues({ ...values, [name]: event.target.value })
 }

const {from} = location.state || {
 from: {
 pathname: '/'
 }
 }
 const {redirectToReferrer} = values
 if (redirectToReferrer) {
 return <Navigate to={from}/>;

 }

return (
 <Card className={classes.card}>
 <CardContent>
 <Typography variant="h6" className={classes.title}>
 Sign In
 </Typography>
 <TextField id="email" type="email" label="Email" className={classes.textField} value={values.email} onChange={handleChange('email')} margin="normal"/><br/>
 <TextField id="password" type="password" label="Password" className={classes.textField} value={values.password} onChange={handleChange('password')} margin="normal"/>
 <br/> {
 values.error && (<Typography component="p" color="error">
 <Icon color="error" className={classes.error}>error</Icon>
 {values.error}
 </Typography>)
 }
 </CardContent>
 <CardActions>
 <Button color="primary" variant="contained" onClick={clickSubmit} className={classes.submit}>Submit</Button>
 </CardActions>
 </Card>
 )
 }

11/15/2024 Web Application Development 53


 To add the Signin component to the app, add the
following Route to MainRouter.

 mern-skeleton/client/MainRouter.jsx:
<Route path="/signin" element={Signin}/>

11/15/2024 Web Application Development 54


Updated
mern-skeleton/client/MainRouter.jsx:
 import React from 'react'
 import {Route, Routes} from 'react-router-dom'
 import Home from './core/Home'
 import Users from './user/Users.jsx'
 import Signup from './user/Signup.jsx'
 import Signin from ‘./lib/Signin.jsx'
 const MainRouter = () => {
 return ( <div>
 <Routes>
 <Route exact path="/" element={<Home />} />
 <Route path="/users" element={Users} />
 <Route path="/signup" element={Signup} />
 <Route path="/signin" element={Signin}/>



 </Routes>
 </div>
 )
 }
 export default MainRouter

11/15/2024 Web Application Development 55


The Profile component
 The Profile component in client/user/Profile.jsx shows
a single user's information in the view at the
'/user/:userId' path, where the userId parameter
represents the ID of the specific user.
 The completed Profile will display user details, and also
conditionally show edit/delete options.
 The following screenshot shows how the Profile renders
when the user currently browsing is viewing someone
else's profile and not their own profile:

11/15/2024 Web Application Development 56


 This profile information can be fetched from the server if
the user is signed in.
 To verify this, the component has to provide the JWT
credential to the read fetch call; otherwise, the user
should be redirected to the Sign In view.
 In the Profile component definition, we need to initialize
the state with an empty user and set redirectToSignin to
false.

11/15/2024 Web Application Development 57


Updated

mern-skeleton/client/user/Profile.jsx
//import React, { useState } from 'react';
 import auth from './auth/auth-helper.js';
 import React, { useState,useEffect } from 'react';
 import { Redirect } from 'react-router-dom';
 import { read } from './someApiModule'; // Replace with the actual module that contains the read function
 //import useState from 'react'
 import read from 'react';
 import match from 'react';
 import setUser from 'react';
 import setRedirectToSignin from 'react';

export default function Profile({ match }) {
 //...
 const [user, setUser] = useState({})
 const [redirectToSignin, setRedirectToSignin] = useState(false)
 //...
 }
 useEffect(() => {
 const abortController = new AbortController()
 const signal = abortController.signal
 const jwt = auth.isAuthenticated()
 read({
 userId: match.params.userId
 }, {t: jwt.token}, signal).then((data) => {
 if (data && data.error) {
 setRedirectToSignin(true)
 } else {
 setUser(data)
 }
 })
 return function cleanup(){
 abortController.abort()
 }
 }, [match.params.userId])

if (redirectToSignin) {
 return <Redirect to='/signin'/>
 }

return (
 <Paper className={classes.root} elevation={4}>
 <Typography variant="h6" className={classes.title}>
 Profile
 </Typography>
 <List dense>
 <ListItem>
 <ListItemAvatar>
 <Avatar>
 <Person/>
 </Avatar>
 </ListItemAvatar>
 <ListItemText primary={user.name} secondary={user.email}/>
 </ListItem>
 <Divider/>
 <ListItem>
 <ListItemText primary={"Joined: " + (
 new Date(user.created)).toDateString()}/>
 </ListItem>
 </List>
 </Paper>
 )

11/15/2024 Web Application Development 58


 However, if the user that's currently signed in is viewing
their own profile, they will be able to see edit and delete
options in the Profile component, as shown in the
following screen shot:

11/15/2024 Web Application Development 59


Updated


mern-skeleton/client/user/Profile.jsx:
/* eslint-disable react/prop-types */
import React, { useState, useEffect } from 'react'
 import { makeStyles } from '@material-ui/core/styles'
 import Paper from '@material-ui/core/Paper'
 import List from '@material-ui/core/List'
 import ListItem from '@material-ui/core/ListItem'
 import ListItemAvatar from '@material-ui/core/ListItemAvatar'
 import ListItemSecondaryAction from '@material-ui/core/ListItemSecondaryAction'
 import ListItemText from '@material-ui/core/ListItemText'
 import Avatar from '@material-ui/core/Avatar'
 import IconButton from '@material-ui/core/IconButton'
 import Typography from '@material-ui/core/Typography'
 import Edit from '@material-ui/icons/Edit'
 import Person from '@material-ui/icons/Person'
 import Divider from '@material-ui/core/Divider'
 import DeleteUser from './DeleteUser'
 import auth from '../lib/auth-helper.js'
 import {read} from './api-user.js'
 import {useLocation, Navigate, Link} from 'react-router-dom'
 import { useParams } from 'react-router-dom';
 const useStyles = makeStyles(theme => ({
 root: theme.mixins.gutters({
 maxWidth: 600,
 margin: 'auto',
 padding: theme.spacing(3),
 marginTop: theme.spacing(5)
 }),
 title: {
 marginTop: theme.spacing(3),
 color: theme.palette.protectedTitle
 }
 }))
 export default function Profile({ match }) {
 const location = useLocation();
 const classes = useStyles()
 const [user, setUser] = useState({})
 const [redirectToSignin, setRedirectToSignin] = useState(false)
 const jwt = auth.isAuthenticated()
 const { userId } = useParams();
 useEffect(() => {
 const abortController = new AbortController()
 const signal = abortController.signal
 read({
 userId: userId
 }, {t: jwt.token}, signal).then((data) => {
 if (data && data.error) {
 setRedirectToSignin(true)
 } else {
 setUser(data)
 }
 })
 return function cleanup(){
 abortController.abort()
 }
 }, [userId])

 if (redirectToSignin) {
 return <Navigate to="/signin" state={{ from: location.pathname }} replace />;

 }
 if (auth.isAuthenticated()) {
 console.log( auth.isAuthenticated().user._id)
 console.log(user._id)
 }
 return (
 <Paper className={classes.root} elevation={4}>
 <Typography variant="h6" className={classes.title}>
 Profile
 </Typography>
 <List dense>
 <ListItem>
 <ListItemAvatar>
 <Avatar>
 <Person/>
 </Avatar>
 </ListItemAvatar>
 <ListItemText primary={user.name} secondary={user.email}/> {
 auth.isAuthenticated().user && auth.isAuthenticated().user._id == user._id &&
 (<ListItemSecondaryAction>
 <Link to={"/user/edit/" + user._id}>
 <IconButton aria-label="Edit" color="primary">

<Edit/>
 </IconButton>
 </Link>
 <DeleteUser userId={user._id}/>
 </ListItemSecondaryAction>)
 }
 </ListItem>
 <Divider/>
 <ListItem>
 <ListItemText primary={"Joined: " + (
 new Date(user.created)).toDateString()}/>
 </ListItem>
 </List>
 </Paper>
 )
 }

11/15/2024 Web Application Development 60


 The Edit button will route to the EditProfile component,
while the custom DeleteUser component will handle the
delete operation with the userId passed to it as a prop.
 To add the Profile component to the app, add the Route
to MainRouter.

11/15/2024 Web Application Development 61


mern-skeleton/client/MainRouter.jsx:
<Route path="/user/:userId" element={Profile}/>

11/15/2024 Web Application Development 62


Updated
mern-skeleton/client/MainRouter.jsx:
 import React from 'react'
 import {Route, Routes} from 'react-router-dom'
 import Home from './core/Home'
 import Users from './user/Users.jsx'
 import Signup from './user/Signup.jsx'
 import Signin from ‘./lib/Signin.jsx'
 import Profile from './user/Profile.jsx'
 const MainRouter = () => {
 return ( <div>
 <Routes>
 <Route exact path="/" element={<Home />} />
 <Route path="/users" element={Users} />
 <Route path="/signup" element={Signup} />
 <Route path="/signin" element={Signin} />
 <Route path="/user/:userId" element={Profile}/>



 </Routes>
 </div>
 )
 }
 export default MainRouter

11/15/2024 Web Application Development 63


 To visit this route in the browser and render a Profile
with user details, the link should be composed with a
valid user ID in it. In the next section, we will use this
same approach of retrieving single user details and
rendering it in the component to implement the Edit
Profile view.

11/15/2024 Web Application Development 64


The EditProfile component
 The EditProfile component in client/user/EditProfile.jsx
has similarities in its implementation to both the Signup
and Profile components.
 It allows the authorized user to edit their own profile
information in a form similar to the signup form, as
shown in the following screenshot:

11/15/2024 Web Application Development 65


 Upon loading at '/user/edit/:userId', the component will
fetch the user's information with their ID after verifying
JWT for auth, and then load the form with the received
user information.
 The form will allow the user to edit and submit only the
changed information to the update fetch call, and, on
successful update, redirect the user to the Profile view
with updated information.
 EditProfile will load the user information the same way
as in the Profile component, that is, by fetching with
read in useEffect using the userId parameter from
match.params.
 It will gather credentials from auth.isAuthenticated.

11/15/2024 Web Application Development 66


 The form view will contain the same elements as the
Signup component, with the input values being updated
in the state when they change.
 On form submit, the component will call the update
fetch method with the userId, JWT and updated user
data.

11/15/2024 Web Application Development 67


Updated
mern-skeleton/client/user/EditProfile.j








sx:
import React, {useState, useEffect} from 'react'
import Card from '@material-ui/core/Card'
import CardActions from '@material-ui/core/CardActions'
import CardContent from '@material-ui/core/CardContent'
import Button from '@material-ui/core/Button'
import TextField from '@material-ui/core/TextField'
import Typography from '@material-ui/core/Typography'
import Icon from '@material-ui/core/Icon'
 import { makeStyles } from '@material-ui/core/styles'
 import auth from '../lib/auth-helper.js'
 import {read, update} from './api-user.js'
 import {Navigate} from 'react-router-dom'
 import { useParams } from 'react-router-dom';
 const useStyles = makeStyles(theme => ({
 card: {
 maxWidth: 600,
 margin: 'auto',
 textAlign: 'center',
 marginTop: theme.spacing(5),
 paddingBottom: theme.spacing(2)
 },
 title: {
 margin: theme.spacing(2),
 color: theme.palette.protectedTitle
 },
 error: {
 verticalAlign: 'middle'
 },
 textField: {
 marginLeft: theme.spacing(1),
 marginRight: theme.spacing(1),
 width: 300
 },
 submit: {
 margin: 'auto',
 marginBottom: theme.spacing(2)
 }
 }))
 export default function EditProfile() {
 const classes = useStyles()
 const { userId } = useParams();
 const [values, setValues] = useState({
 name: '',
 password: '',
 email: '',
 open: false,
 error: '',
 NavigateToProfile: false
 })
 const jwt = auth.isAuthenticated()
 useEffect(() => {
 const abortController = new AbortController()
 const signal = abortController.signal
 read({
 userId: userId
 }, {t: jwt.token}, signal).then((data) => {
 if (data && data.error) {
 setValues({...values, error: data.error})
 } else {
 setValues({...values, name: data.name, email: data.email})
 }
 })
 return function cleanup(){
 abortController.abort()
 }
 }, [userId])
 const clickSubmit = () => {
 const user = {
 name: values.name || undefined,
 email: values.email || undefined,
 password: values.password || undefined
 }
 update({
 userId: userId
 }, {
 t: jwt.token
 }, user).then((data) => {
 if (data && data.error) {
 setValues({...values, error: data.error})
 } else {
 setValues({...values, userId: data._id, NavigateToProfile: true})
 }
 })
 }
 const handleChange = name => event => {
 setValues({...values, [name]: event.target.value})
 }
 if (values.NavigateToProfile) {
 return (<Navigate to={'/user/' + values.userId}/>)
 }
 return (
 <Card className={classes.card}>
 <CardContent>
 <Typography variant="h6" className={classes.title}>
 Edit Profile
 </Typography>
 <TextField id="name" label="Name" className={classes.textField} value={values.name} onChange={handleChange('name')} margin="normal"/><br/>
 <TextField id="email" type="email" label="Email" className={classes.textField} value={values.email} onChange={handleChange('email')} margin="normal"/><br/>
 <TextField id="password" type="password" label="Password" className={classes.textField} value={values.password} onChange={handleChange('password')} margin="normal"/>
 <br/> {
 values.error && (<Typography component="p" color="error">
 <Icon color="error" className={classes.error}>error</Icon>
 {values.error}
 </Typography>)
 }
 </CardContent>
 <CardActions>
 <Button color="primary" variant="contained" onClick={clickSubmit} className={classes.submit}>Submit</Button>
 </CardActions>
 </Card>
 )
 }

11/15/2024 Web Application Development 68


 To add the EditProfile component to the app, we will
use a PrivateRoute, which will restrict the component
from loading at all if the user is not signed in.
 The order of placement in MainRouter will also be
important.

11/15/2024 Web Application Development 69


mern-skeleton/client/MainRouter.jsx:
 <Route
 path="/user/edit/:userId"
 element={
 <PrivateRoute>
 <EditProfile />
 </PrivateRoute>
 }
 />
 <Route path="/user/:userId" element={Profile}/>

11/15/2024 Web Application Development 70


Updated
mern-skeleton/client/MainRouter.jsx:
 import React from 'react'
 import {Route, Routes} from 'react-router-dom'
 import Home from './core/Home'
 import Users from './user/Users.jsx'
 import Signup from './user/Signup.jsx'
 import Signin from ‘./lib/Signin.js'
 import Profile from './user/Profile.js’
 //import Switch from 'react'
 import PrivateRoute from './lib/PrivateRoute.jsx'
 import EditProfile from './user/EditProfile.jsx'
 const MainRouter = () => {
 return ( <div>
 <Routes>
 <Route exact path="/" element={<Home />} />
 <Route path="/users" element={Users} />
 <Route path="/signup" element={Signup} />
 <Route path="/signin" element={Signin} />

 <PrivateRoute path="/user/edit/:userId" element={EditProfile}/>
 <Route path="/user/:userId" element={Profile}/>


 </Routes>
 </div>
 )
 }
 export default MainRouter

11/15/2024 Web Application Development 71


 The route with the '/user/edit/:userId' path needs to be
placed before the route with the '/user/:userId' path, so
that the edit path is matched first exclusively when this
route is requested, and not confused with the Profile
route.
 With this profile edit view added, we only have the user
delete UI implementation left to complete the user-
related frontend.

11/15/2024 Web Application Development 72


The DeleteUser component
 The DeleteUser component in
client/user/DeleteUser.jsx is basically a button that we
will add to the Profile view that, when clicked, opens a
Dialog component asking the user to confirm the delete
action, as shown in the following screenshot:

11/15/2024 Web Application Development 73


 This component initializes the state with open set to
false for the Dialog component, as well as redirect set
to false so that it isn't rendered first.

11/15/2024 Web Application Development 74


Updated
mern-skeleton/client/user/DeleteUser





import
import
import
import
import
.jsx:
React, {useState} from 'react'
PropTypes from 'prop-types'
IconButton from '@material-ui/core/IconButton'
Button from '@material-ui/core/Button'
DeleteIcon from '@material-ui/icons/Delete'
 import Dialog from '@material-ui/core/Dialog'
 import DialogActions from '@material-ui/core/DialogActions'
 import DialogContent from '@material-ui/core/DialogContent'
 import DialogContentText from '@material-ui/core/DialogContentText'
 import DialogTitle from '@material-ui/core/DialogTitle'
 import auth from ‘../lib/auth-helper.js'
 import {remove} from './api-user.js'
 import {Navigate} from 'react-router-dom'

export default function DeleteUser(props) {
 const [open, setOpen] = useState(false)
 const [redirect, setRedirect] = useState(false)

const jwt = auth.isAuthenticated()
 const clickButton = () => {
 setOpen(true)
 }
 const deleteAccount = () => {
 remove({
 userId: props.userId
 }, {t: jwt.token}).then((data) => {
 if (data && data.error) {
 console.log(data.error)
 } else {
 auth.clearJWT(() => console.log('deleted'))
 setRedirect(true)
 }
 })
 }
 const handleRequestClose = () => {
 setOpen(false)
 }

if (redirect) {
 return <Navigate to='/'/>
 }
 return (<span>
 <IconButton aria-label="Delete" onClick={clickButton} color="secondary">
 <DeleteIcon/>
 </IconButton>

<Dialog open={open} onClose={handleRequestClose}>
 <DialogTitle>{"Delete Account"}</DialogTitle>
 <DialogContent>
 <DialogContentText>
 Confirm to delete your account.
 </DialogContentText>
 </DialogContent>
 <DialogActions>
 <Button onClick={handleRequestClose} color="primary">
 Cancel
 </Button>
 <Button onClick={deleteAccount} color="secondary" autoFocus="autoFocus">
 Confirm
 </Button>
 </DialogActions>
 </Dialog>
 </span>)

}
 DeleteUser.propTypes = {
 userId: PropTypes.string.isRequired
 }

11/15/2024 Web Application Development 75


 Since we are using the DeleteUser component in the
Profile component, it gets added to the application view
when Profile is added in MainRouter.
 With the delete user UI added, we now have a frontend
that contains all the React component views in order to
complete the skeleton application features.
 But, we still need a common navigation UI to link all
these views together and make each view easy to
access for the frontend user.
 In the next section, we will implement this navigation
menu component.

11/15/2024 Web Application Development 76


The Menu component
 The Menu component will function as a navigation bar
across the frontend application by providing links to all
the available views, and also by indicating the user's
current location in the application.
 To implement these navigation bar functionalities, we
will use the HOC withRouter from React Router to get
access to the history object's properties.
 The following code in the Menu component adds just
the title, the Home icon linked to the root route, and the
Users button, which is linked to the '/users' route.

11/15/2024 Web Application Development 77


mern-skeleton/client/core/Menu.jsx:
 const Menu = withRouter(({history}) => (
 <AppBar position="static">
 <Toolbar>
 <Typography variant="h6" color="inherit">
 MERN Skeleton
 </Typography>
 <Link to="/">
 <IconButton aria-label="Home" style={isActive(history, "/")}>
 <HomeIcon/>
 </IconButton>
 </Link>
 <Link to="/users">
 <Button style={isActive(history, "/users")}>Users</Button>
 </Link>
 </Toolbar>
 </AppBar>))

11/15/2024 Web Application Development 78


Updated
mern-skeleton/client/core/Menu.jsx:
 import withRouter from 'react'
 import AppBar from 'react'
 import Toolbar from 'react'
 import Typography from 'react'
 import Link from 'react'
 import IconButton from 'react'
 import isActive from 'react'
 import HomeIcon from 'react'
 import Button from 'react'

const Menu = withRouter(({ history }) => (
 <AppBar position="static">
 <Toolbar>
 <Typography variant="h6" color="inherit">
 MERN Skeleton
 </Typography>
 <Link to="/">
 <IconButton aria-label="Home" style={isActive(history, "/")}>
 <HomeIcon/>
 </IconButton>
 </Link>
 <Link to="/users">
 <Button style={isActive(history, "/users")}>Users</Button>
 </Link>
 </Toolbar>
 </AppBar>))

11/15/2024 Web Application Development 79


 To indicate the current location of the application on the
Menu, we will highlight the link that matches the current
location path by changing the color conditionally.

11/15/2024 Web Application Development 80


mern-skeleton/client/core/Menu.jsx:
 const isActive = (history, path) => {
 if (history.location.pathname == path)
 return {color: '#ff4081'}
 else
 return {color: '#ffffff'}
}

11/15/2024 Web Application Development 81


Updated

mern-skeleton/client/core/Menu.jsx:
import withRouter from 'react'
 import AppBar from 'react'
 import Toolbar from 'react'
 import Typography from 'react'
 import Link from 'react'
 import IconButton from 'react'
 //import isActive from 'react'
 import HomeIcon from 'react'
 import Button from 'react'

const Menu = withRouter(({ history }) => (
 <AppBar position="static">
 <Toolbar>
 <Typography variant="h6" color="inherit">
 MERN Skeleton
 </Typography>
 <Link to="/">
 <IconButton aria-label="Home" style={isActive(history, "/")}>
 <HomeIcon/>
 </IconButton>
 </Link>
 <Link to="/users">
 <Button style={isActive(history, "/users")}>Users</Button>
 </Link>
 </Toolbar>
 </AppBar>))
 const isActive = (history, path) => {
 if (history.location.pathname == path)
 return {color: '#ff4081'}
 else
 return {color: '#ffffff'}
 }

11/15/2024 Web Application Development 82


 The isActive function is used to apply color to the
buttons in the Menu, as follows:
style={isActive(history, "/users")} – This line of script
already exist in the Menu.jsx

11/15/2024 Web Application Development 83


 The remaining links such as SIGN IN, SIGN UP, MY
PROFILE, and SIGN OUT will show up on the Menu
based on whether the user is signed in or not.
 The following screenshot shows how the Menu renders
when the user is not signed in:

 For example, the links to SIGN UP and SIGN IN should


only appear on the menu when the user is not signed
in.
 Therefore, we need to add it to the Menu component
after the Users button with a condition.
11/15/2024 Web Application Development 84
mern-skeleton/client/core/Menu.jsx:
{
 !auth.isAuthenticated() && (<span>
 <Link to="/signup">
 <Button style={isActive(history, "/signup")}> Sign Up
</Button>
 </Link>
 <Link to="/signin">
 <Button style={isActive(history, "/signin")}> Sign In
</Button>
 </Link>
 </span>)
}

11/15/2024 Web Application Development 85


Updated

mern-skeleton/client/core/Menu.jsx:
import withRouter from 'react'
 import AppBar from 'react'
 import Toolbar from 'react'
 import Typography from 'react'
 import Link from 'react'
 import IconButton from 'react'
 //import isActive from 'react'
 import HomeIcon from 'react'
 import Button from 'react'
 import auth from 'auth'

const Menu = withRouter(({ history }) => (
 <AppBar position="static">
 <Toolbar>
 <Typography variant="h6" color="inherit">
 MERN Skeleton
 </Typography>
 <Link to="/">
 <IconButton aria-label="Home" style={isActive(history, "/")}>
 <HomeIcon/>
 </IconButton>
 </Link>
 <Link to="/users">
 <Button style={isActive(history, "/users")}>Users</Button>
 </Link>
 </Toolbar>
 </AppBar>))
 const isActive = (history, path) => {
 if (history.location.pathname == path)
 return {color: '#ff4081'}
 else
 return {color: '#ffffff'}
 }
 {
 !auth.isAuthenticated() && (<span>
 <Link to="/signup">
 <Button style={isActive(history, "/signup")}> Sign Up </Button>
 </Link>
 <Link to="/signin">
 <Button style={isActive(history, "/signin")}> Sign In </Button>
 </Link>
 </span>)
 }

11/15/2024 Web Application Development 86


 Similarly, the link to MY PROFILE and the SIGN OUT
button should only appear on the menu when the user
is signed in, and should be added to the Menu
component with the following condition check.

11/15/2024 Web Application Development 87


mern-skeleton/client/core/Menu.jsx:
 {
 auth.isAuthenticated() && (<span>
 <Link to={"/user/" + auth.isAuthenticated().user._id}>
 <Button style={isActive(history, "/user/"
 + auth.isAuthenticated().user._id)}>
 My Profile
 </Button>
 </Link>
 <Button color="inherit"
 onClick={() => { auth.clearJWT(() => history.push('/')) }}>
 Sign out
 </Button>
 </span>)
 }

11/15/2024 Web Application Development 88


Updated
mern-skeleton/client/core/Menu.jsx:
 import React from 'react'
 import AppBar from '@material-ui/core/AppBar'
 import Toolbar from '@material-ui/core/Toolbar'
 import Typography from '@material-ui/core/Typography'
 import IconButton from '@material-ui/core/IconButton'
 import HomeIcon from '@material-ui/icons/Home'
 import Button from '@material-ui/core/Button'
 import auth from ‘../lib/auth-helper'
 import { Link, useNavigate, useLocation } from 'react-router-dom';

const isActive = (location, path) => {


 return location.pathname === path ? { color: '#ff4081' } : { color: '#ffffff' };
 };
 export default function Menu(){
 const navigate = useNavigate();
 const location = useLocation();

return (
 <AppBar position="static">
 <Toolbar>
 <Typography variant="h6" color="inherit">
 MERN Skeleton
 </Typography>
 <Link to="/">
 <IconButton aria-label="Home" style={isActive(location, "/")}>
 <HomeIcon/>
 </IconButton>
 </Link>
 <Link to="/users">
 <Button style={isActive(location, "/users")}>Users</Button>
 </Link>
 {
 !auth.isAuthenticated() && (<span>
 <Link to="/signup">
 <Button style={isActive(location, "/signup")}>Sign up
 </Button>
 </Link>
 <Link to="/signin">
 <Button style={isActive(location, "/signin")}>Sign In
 </Button>
 </Link>
 </span>)
 }
 {
 auth.isAuthenticated() && (<span>
 <Link to={"/user/" + auth.isAuthenticated().user._id}>
 <Button style={isActive(location, "/user/" + auth.isAuthenticated().user._id)}>My Profile</Button>
 </Link>
 <Button color="inherit" onClick={() => {
 auth.clearJWT(() => navigate('/'));
 }}>Sign out</Button>
 </span>)
 }
 </Toolbar>
 </AppBar>
 );
 }

11/15/2024 Web Application Development 89


 The MY PROFILE button uses the signed-in user's
information to link to the user's own profile, while the
SIGN OUT button calls the auth.clearJWT() method
when it's clicked.
 When the user is signed in, the Menu will look as
follows:

 To have the Menu navigation bar present in all the


views, we need to add it to the MainRouter before all
the other routes, and outside the Switch component.

11/15/2024 Web Application Development 90


mern-skeleton/client/MainRouter.jsx:
 <Menu/>

11/15/2024 Web Application Development 91


Updated
mern-skeleton/client/MainRouter.jsx:
 import React from 'react';
 import { Routes, Route } from 'react-router-dom';
 //import React from 'react'
 //import {Route, Routes} from 'react-router-dom'
 import Home from './core/Home'
 import Users from './user/Users.jsx'
 import Signup from './user/Signup.jsx'
 import Signin from './auth/Signin.jsx'
 import Profile from './user/Profile.jsx'
 import Switch from 'react'
 import PrivateRoute from './lib/PrivateRoute.jsx'
 import EditProfile from './user/EditProfile.jsx'
 import Menu from './core/Menu'
 function MainRouter() {
 return (
 <div>
 <Menu/>



 <Routes>
 <Route path="/" element={<Home />} />
 <Route path="/users" element={<Users />} />
 <Route path="/signup" element={<Signup />} />
 <Route path="/signin" element={<Signin />} />
 <Route
 path="/user/edit/:userId"
 element={
 <PrivateRoute>
 <EditProfile />
 </PrivateRoute>
 }
 />
 <Route path="/user/:userId" element={<Profile />} />

</Routes>
 </div>
 );
 }

export default MainRouter;

11/15/2024 Web Application Development 92


 This will make the Menu component render on top of all
the other components when these components are
accessed at their respective routes.
 The skeleton frontend is now complete and has all
necessary components to allow a user to sign up, view,
and modify user data on the backend while considering
authentication and authorization restrictions.
 However, it is still not possible to visit the frontend
routes directly in the browser address bar; these can
only be accessed when they're linked from within the
frontend view.
 To enable this functionality in the skeleton application,
we need to implement basic server-side rendering.

11/15/2024 Web Application Development 93


Implementing basic server-side
rendering
 Currently, when the React Router routes or pathnames
are directly entered in the browser address bar or when
a view that is not at the root path is refreshed, the URL
does not work.
 This happens because the server does not recognize
the React Router routes we defined in the frontend.
 We have to implement basic server-side rendering on
the backend so that the server is able to respond when
it receives a request to a frontend route.
 To render the relevant React components properly
when the server receives requests to the frontend
routes, we need to initially generate the React
components on the server-side with regard to the React
Router and Material-UI components, before the client-
side JS is ready to take
11/15/2024
over the rendering.
Web Application Development 94
 In the following sections, we will look at the
implementation of the steps outlined in the preceding
code block, and also discuss how to prepare the
frontend so that it accepts and handles this server-
rendered code.

11/15/2024 Web Application Development 95


 While rendering the React tree, the client app's root
component, MainRouter, is wrapped with the Material-
UI ThemeProvider to provide the styling props that are
needed by the MainRouter child components.
 The stateless StaticRouter is used here instead of the
BrowserRouter that's used on the client-side in order to
 wrap MainRouter and provide the routing props that are
used for implementing the client-side components.
 Based on these values, such as the requested location
route and theme that are passed in as props to the
wrapping components, renderToString will return the
 markup containing the relevant view.

11/15/2024 Web Application Development 96


 An example of a case where redirect is rendered in the
component is when we're trying to access a PrivateRoute
via a server-side render.
 As the server-side cannot access the auth token from the
browser's sessionStorage, the redirect in PrivateRoute will
render.
 The context.url value , in this case, will have the '/signin'
route, and hence, instead of trying to render the
PrivateRoute component, it will redirect to the '/signin'
route.
 This completes the code we need to add to the server-
side to enable the basic server-side rendering of the
React views.
 Next, we need to update the frontend so it is able to
integrate and render Web
11/15/2024 this server-generated
Application Development code. 97
CONNECTING THE FRONTEND
AND BACKEND
 To connect the frontend and Backend since the
backend(server) is running on port 3000, add the
following code to the vite.config.js file as follows:

11/15/2024 Web Application Development 98


Vite.config.js
 import { defineConfig } from "vite";
 import react from "@vitejs/plugin-react";
 const { PORT = 3000} = process.env;
 export default defineConfig({
 plugins: [react()],
 server:{
 proxy:{
 '/api':{
 target:`http://localhost:${PORT}`,
 changeOrigin: true,
 },
 '/auth': {
 target:`http://localhost:${PORT}`,
 changeOrigin: true,
 },
 },
 },
 build: {
 manifest: true,
 rollupOptions: {
 input: "./src/main.jsx",
 },
 },
 });

11/15/2024 Web Application Development 99


11/15/2024 Web Application Development 100
11/15/2024 Web Application Development 101
 When you click on a signed in user the screen below
is displayed, so you can edit or delete the profile.

11/15/2024 Web Application Development 102


 Edit Profile – Only signed in users can edit their profile.

11/15/2024 Web Application Development 103


 Delete Profile

11/15/2024 Web Application Development 104

You might also like