Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

DBC22-2869: FE integration in saved route and account pages #796

Merged
merged 1 commit into from
Jan 6, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/backend/apps/authentication/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.core.exceptions import ObjectDoesNotExist
from django.core.mail import EmailMultiAlternatives
from django.db.utils import IntegrityError
from django.http import HttpResponseRedirect
from django.shortcuts import reverse
from django.template.loader import render_to_string
from django.utils.encoding import force_bytes, force_str
Expand Down Expand Up @@ -128,7 +129,7 @@ def get(self, request, uidb64, token):
if user is not None and EmailVerificationTokenGenerator().check_token(user, token):
user.verified = True
user.save()
return Response({'message': 'Email verified successfully'}, status=status.HTTP_200_OK)
return HttpResponseRedirect(env("FRONTEND_BASE_URL") + 'account') # Redirect to the account page

else:
return Response({'error': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST)
Expand Down
3 changes: 2 additions & 1 deletion src/backend/apps/shared/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,8 @@ def get(self, request, format=None):
if request.user.is_authenticated:
response = JsonResponse({
"username": request.user.username,
"email": request.user.email
"email": request.user.email,
"verified": request.user.verified
})

else:
Expand Down
4 changes: 4 additions & 0 deletions src/frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import ScrollToTop from './Components/shared/ScrollToTop';
// FontAwesome Stylesheet
import { config } from '@fortawesome/fontawesome-svg-core'
import '@fortawesome/fontawesome-svg-core/styles.css'
import VerifyEmailPage from "./pages/VerifyEmailPage";
config.autoAddCss = false

// Variables
Expand Down Expand Up @@ -148,11 +149,13 @@ function App() {
if (data.username) {
ret.username = data.username;
ret.email = data.email;
ret.verified = data.verified;
}
setAuthContext((prior) => {
if (ret.loginStateKnown != prior.loginStateKnown) { return ret; }
if (ret.username != prior.username) { return ret; }
if (ret.email != prior.email) { return ret; }
if (ret.verified != prior.verified) { return ret; }
return prior;
});
})
Expand Down Expand Up @@ -199,6 +202,7 @@ function App() {
<Route path="/bulletins" element={<BulletinsListPage />} />
<Route path="/bulletins/:id" element={<BulletinDetailsPage />} />
<Route path="/account" element={<AccountPage />} />
<Route path="/verify-email" element={<VerifyEmailPage />} />
{/* Catch-all route for 404 errors */}
<Route path="*" element={<NotFoundPage />} />
<Route path="/problems" element={<ProblemsPage />} />
Expand Down
14 changes: 12 additions & 2 deletions src/frontend/src/pages/AccountPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import Container from 'react-bootstrap/Container';

// Internal imports
import { AuthContext } from '../App';
import { sendVerificationEmail } from "../Components/data/user";
import Footer from '../Footer.js';
import PageHeader from '../PageHeader';

Expand All @@ -33,7 +32,18 @@ export default function AccountPage() {
</PageHeader>

<Container>
<button onClick={sendVerificationEmail}>Send Verification Email</button>
<div className='email-address'>
<p className='header'>Email Address</p>
<p className='email'>{authContext.email}</p>

{!authContext.verified &&
<p className='not-verified'>
This email address has not been verified. Email notifications for
saved routes will be disabled until verification
is complete.
</p>
}
</div>
</Container>

<Footer/>
Expand Down
21 changes: 21 additions & 0 deletions src/frontend/src/pages/AccountPage.scss
Original file line number Diff line number Diff line change
@@ -1 +1,22 @@
@import "../styles/variables.scss";

.account-page {
.email-address {
.email, .header {
font-size: 1.1rem;
font-weight: 700;
color: $MyAccountHeader;
}

.email {
font-weight: 400;
}

.not-verified {
background-color: $MyAccountWarning;
color: $MyAccountWarningText;
padding: 0.5rem 1rem;
font-size: 0.875rem;
}
}
}
22 changes: 22 additions & 0 deletions src/frontend/src/pages/SavedRoutesPage.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// React
import React, { useCallback, useContext, useEffect, useRef, useState } from 'react';

// Navigation
import { useNavigate } from 'react-router-dom';

// Redux
import { useSelector } from 'react-redux';
import { memoize } from 'proxy-memoize';
Expand All @@ -24,6 +27,9 @@ export default function SavedRoutesPage() {
/* Setup */
document.title = 'DriveBC - My Routes';

// Navigation
const navigate = useNavigate();

// Context
const { authContext, setAuthContext } = useContext(AuthContext);

Expand Down Expand Up @@ -71,6 +77,22 @@ export default function SavedRoutesPage() {
description="Manage and view your saved routes here.">
</PageHeader>

{authContext.loginStateKnown && authContext.username && !authContext.verified &&
<div className='not-verified'>
{authContext.email} has not been verified. Email notifications for saved routes will be disabled until
verification is complete.

<div
className='verify-link'
tabIndex={0}
onClick={() => navigate('/verify-email')}
onKeyPress={() => navigate('/verify-email')}>

<b>Verify email address</b>
</div>
</div>
}

{authContext.loginStateKnown && authContext.username &&
<Container className="content-container">
{!(favRoutes && favRoutes.length) &&
Expand Down
45 changes: 35 additions & 10 deletions src/frontend/src/pages/SavedRoutesPage.scss
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,31 @@
.saved-routes-page {
position: relative;

.not-verified {
background-color: $MyAccountWarning;
color: $MyAccountWarningText;
padding: 1rem 4rem;
font-size: 0.875rem;

@media (max-width: 992px) {
padding: 1rem 0.75rem;
}

.verify-link {
text-decoration: underline;
cursor: pointer;

@media (min-width: 992px) {
display: inline;
margin-left: 1rem;
}

@media (max-width: 992px) {
margin-top: 1rem;
}
}
}

.container {
@media (min-width: 576px) {
max-width: 1828px;
Expand Down Expand Up @@ -32,15 +57,15 @@
opacity: 1;
overflow: auto;
transition: all 0.3s ease;

@media (min-width: 576px) {
grid-template-columns: repeat(2, 1fr );
}

@media (min-width: 1240px) {
grid-template-columns: repeat(3, 1fr );
}

@media (min-width: 1480px) {
grid-template-columns: repeat(4, 1fr );
}
Expand All @@ -49,7 +74,7 @@
opacity: 0;
overflow: hidden;
}

.route-card {
border-radius: 4px;
border: none;
Expand All @@ -60,7 +85,7 @@
display: flex;
flex-direction: column;
justify-content: space-between;

@media (min-width: 576px) {
width: unset;
margin: unset;
Expand Down Expand Up @@ -98,7 +123,7 @@
border-top-right-radius: 0;
}
}

.route-alt-name {
margin-bottom: 0;
}
Expand All @@ -118,7 +143,7 @@
border: none;
background: none;
padding: 0;

svg {
margin-right: 8px;
}
Expand All @@ -132,7 +157,7 @@
}
}
}

.fav-cams-on-route {
background: white;
transition: all 0.3s ease;
Expand All @@ -147,13 +172,13 @@
display: flex;
justify-content: space-between;
margin-bottom: 1rem;

.close-btn {
cursor: pointer;
font-size: 20px;
margin-top: 4px;
}

.caption {
font-size: 1.125rem;
color: $Grey80;
Expand Down
59 changes: 59 additions & 0 deletions src/frontend/src/pages/VerifyEmailPage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// React
import React, { useContext, useEffect } from 'react';

// External imports
import Container from 'react-bootstrap/Container';

// Internal imports
import { AuthContext } from '../App';
import { sendVerificationEmail } from "../Components/data/user";
import Footer from '../Footer.js';
import PageHeader from '../PageHeader';

// Styling
import './VerifyEmailPage.scss';

export default function VerifyEmailPage() {
const { authContext } = useContext(AuthContext);

useEffect(() => {
if (authContext.loginStateKnown && !authContext.verified) {
sendVerificationEmail();
}
}, [authContext]);

return (
<div className="verify-email-page">
<PageHeader
title="Please verify your email"
description="">
</PageHeader>

<Container>
<p className='base-instructions'>
To enable notifications your email account needs to be
verified. We’ve sent an email to <b>{authContext.email}</b> with
a link to verify that the account is yours.
</p>

<p>
Once you click on the link, you will be able to receive
notifications by email for your favourite routes.
</p>

<p>
<b>If you can’t find the email</b>, please check the following:
<ul>
<li>double check the spelling of your email address</li>
<li>check your junk mail folders, and add</li>
<li>add [email protected] to your whitelist</li>
</ul>
</p>

<button className='btn btn-outline-primary' onClick={sendVerificationEmail} onKeyPress={sendVerificationEmail}>Send another verification Email</button>
</Container>

<Footer/>
</div>
);
}
12 changes: 12 additions & 0 deletions src/frontend/src/pages/VerifyEmailPage.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@import "../styles/variables.scss";

.verify-email-page {
p {
font-size: 1rem;
margin-bottom: 1.5rem;
}

.instructions {
font-size: 1.1rem;
}
}
9 changes: 7 additions & 2 deletions src/frontend/src/styles/variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,14 @@ $BtnHover-Tertiary: $Grey30;
$BtnActive-Secondary: $White;


//Animations
//Animations
$Anim-gentle: cubic-bezier(0.175, 0.885, 0.32, 1.275);

$Border-dark: #353433;

$GreyOnBlue: #5C6B78;
$GreyOnBlue: #5C6B78;

// My Account
$MyAccountHeader: #323130;
$MyAccountWarning: #FEF8E8;
$MyAccountWarningText: #584215;
Loading