Skip to content

Commit

Permalink
Feat/login2 (#190)
Browse files Browse the repository at this point in the history
* Cherry-pick feat/login changes

* Fix JS errors

* Missing from merge

* Missed in the merge

* Adding delay logic to load the delayed part of the form

* Added more utility functions around login/logout and session state tracking for logged in user

* treat invalid login as a logout as well

* More login handling of errors, fetching profile, and showing user name in navigation

* Remove external ID from initial login

* Handle logged-in user on page load

* Fix lint issue

* Remove console logging

---------

Co-authored-by: Brendan Robert <[email protected]>
  • Loading branch information
2 people authored and bstopp committed Mar 6, 2024
1 parent 68a6e36 commit 850fc1f
Show file tree
Hide file tree
Showing 8 changed files with 735 additions and 1 deletion.
14 changes: 14 additions & 0 deletions blocks/header/header.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { BREAKPOINTS } from '../../scripts/scripts.js';
import { getMetadata, decorateIcons, decorateSections } from '../../scripts/aem.js';
import { open as openSignIn, close as closeSignIn } from '../login/login.js';
import { logout } from '../../scripts/apis/user.js';

// media query match that indicates mobile/tablet width
const isDesktop = BREAKPOINTS.large;
Expand Down Expand Up @@ -63,6 +65,10 @@ function closeNavDrop(e) {
*/
function toggleMenu(nav, navSections, forceExpanded = null) {
const expanded = forceExpanded !== null ? !forceExpanded : nav.getAttribute('aria-expanded') === 'true';
const closing = (expanded || isDesktop.matches);
if (closing) {
closeSignIn();
}
document.body.style.overflowY = (expanded || isDesktop.matches) ? '' : 'hidden';
nav.setAttribute('aria-expanded', expanded ? 'false' : 'true');
toggleAllNavSections(navSections, expanded || isDesktop.matches ? 'false' : 'true');
Expand Down Expand Up @@ -112,6 +118,12 @@ function buildLogo() {
return logo;
}

function doLogout() {
const userDetailsLink = document.body.querySelector('.username a');
userDetailsLink.textContent = 'Sign In';
logout();
}

/**
* Adds the Profile submenu to the Nav.
* @param {HTMLDivElement} nav
Expand All @@ -137,6 +149,8 @@ function addProfileLogin(nav) {
</li>
`;
profileList.prepend(...profileMenu.childNodes);
profileList.querySelector('.login a').addEventListener('click', openSignIn);
profileList.querySelector('.user-menu .logout a').addEventListener('click', doLogout);
}

/**
Expand Down
123 changes: 123 additions & 0 deletions blocks/login/login-delayed.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { close, displayError, reset } from './login.js';
import { login, isLoggedIn, getUserDetails } from '../../scripts/apis/user.js';

const block = document.querySelector('.login.block');

function isValid(form) {
const errors = [];
const user = form.querySelector('input[name="username"]');
if (!user.value || user.value.trim().length === 0) {
errors.push('Email address is required.');
block.querySelector('input[name="username"]').classList.add('error');
}

const password = form.querySelector('input[name="password"]');
if (!password.value || password.value.trim().length === 0) {
block.querySelector('input[name="password"]').classList.add('error');
errors.push('Password is required.');
}

if (errors.length > 0) {
displayError(errors);
return false;
}
return true;
}

function loginError(response) {
if (response.status === 401) {
displayError(['Invalid username or password.']);
} else {
displayError([`There was an error logging in (${response.body})`]);
}
}

/**
* Checks if the user is logged in and updates the header accordingly.
*/
function checkForLoggedInUser() {
if (isLoggedIn()) {
const userDetailsLink = document.body.querySelector('.nav-profile .username a');
document.body.querySelector('.nav-profile .login').style.display = 'none';
document.body.querySelector('.nav-profile .username').style.display = 'block';
const userDetails = getUserDetails();
userDetailsLink.textContent = userDetails?.profile?.firstName || 'Valued Customer';
}
}

/**
* Submits the form.
*
* @param {HTMLFormElement} form
*/
async function submit(form) {
reset();

if (isValid(form)) {
const credentials = {
username: form.querySelector('input[name="username"]').value,
password: form.querySelector('input[name="password"]').value,
};
const userDetails = await login(credentials, loginError);
if (userDetails) {
close();
checkForLoggedInUser();
}
}
}

[...block.querySelectorAll('input[name="username"], input[name="password"]')].forEach((el) => {
el.addEventListener('blur', (e) => {
const { value } = e.currentTarget;
if (!value || value.trim().length === 0) {
e.currentTarget.classList.add('error');
} else {
e.currentTarget.classList.remove('error');
}
});
});

const rememberMe = block.querySelector('.help .remember input[type="checkbox"]');
const pseudoRemember = block.querySelector('.remember .checkbox');
const warning = block.querySelector('.help .warning');

pseudoRemember.addEventListener('click', () => {
rememberMe.click();
});

rememberMe.addEventListener('change', () => {
pseudoRemember.classList.toggle('checked');
warning.classList.toggle('visible');
});

block.querySelector('a.forgot-password').addEventListener('click', (e) => {
e.currentTarget.closest('.login-form').classList.remove('open');
const event = new Event('open-overlay');
event.overlay = 'forgot-password'; // TODO: Change this to a constant exported from block.
document.body.dispatchEvent(event);
});

block.querySelector('.create-account .create-button').addEventListener('click', (e) => {
e.currentTarget.closest('.login-form').classList.remove('open');
const event = new Event('open-overlay');
event.overlay = 'create-account'; // TODO: Change this to a constant exported from block.
document.body.dispatchEvent(event);
});

block.querySelector('form').addEventListener('submit', (e) => {
e.preventDefault();
});

block.querySelector('.cta a.submit').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
submit(block.querySelector('form'));
});

block.querySelector('.cta a.cancel').addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
close();
});

checkForLoggedInUser();
Loading

0 comments on commit 850fc1f

Please sign in to comment.