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

Feat/profile #194

Merged
merged 22 commits into from
Jan 30, 2024
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
62 changes: 51 additions & 11 deletions blocks/header/header.css
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,57 @@ body.light-nav {
display: none;
}

.header.block nav .nav-profile .level-2 {
filter: drop-shadow(0 0 6px rgba(0 0 0 / 25%));
background-color: var(--white);
padding: 0.25em 0;
}

.header.block nav .nav-sections > ul > li.nav-drop > a::after {
content: '';
height: 32px;
width: 32px;
background: url('/icons/chevron-right-white.svg') center center no-repeat;
}

.header.block nav .nav-profile .level-1:has(.level-2)>a::after {
content: '\f0d7';
font-family: var(--font-family-fontawesome);
height: var(--body-font-size-s);
width: var(--body-font-size-s);
margin-left: 10px;
color: var(--body-color);
transition: transform 0.2s ease-in-out;
}

.header.block nav .nav-profile .level-1 .level-2 {
display:none;
}

.header.block nav .nav-profile .level-1:hover .level-2 {
display: block;
}

.header.block nav .nav-profile .level-2::before {
content: '';
position: absolute;
top: -0.5em;
right: 1em;
height: 1em;
width: 1em;
background-color: var(--white);
transform: rotate(45deg);
}


.header.block nav .nav-sections > ul > li {
border-bottom: 1px solid var(--black);
}

.header.block nav .nav-profile .level-2 li {
padding: 0.4em 0;
}

.header.block nav .nav-sections {
display: none;
grid-area: sections;
Expand All @@ -143,10 +194,6 @@ body.light-nav {
margin: 10px 0;
}

.header.block nav .nav-sections > ul > li {
border-bottom: 1px solid var(--black);
}

/* stylelint-disable-next-line no-descending-specificity */
.header.block nav .nav-sections > ul > li > a {
display: flex;
Expand All @@ -162,13 +209,6 @@ body.light-nav {
justify-content: space-between;
}

.header.block nav .nav-sections > ul > li.nav-drop > a::after {
content: '';
height: 32px;
width: 32px;
background: url('/icons/chevron-right-white.svg') center center no-repeat;
}

.header.block nav .nav-sections > ul > li > ul {
padding-bottom: 15px;
}
Expand Down
59 changes: 41 additions & 18 deletions blocks/header/header.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
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';
import {
logout,
isLoggedIn,
onProfileUpdate,
getUserDetails,
} from '../../scripts/apis/user.js';
import { i18nLookup } from '../../scripts/util.js';

// media query match that indicates mobile/tablet width
const isDesktop = BREAKPOINTS.large;
let i18n;

function closeOnEscape(e) {
if (e.code === 'Escape') {
Expand Down Expand Up @@ -119,9 +126,26 @@ function buildLogo() {
}

function doLogout() {
const userDetailsLink = document.body.querySelector('.username a');
userDetailsLink.textContent = 'Sign In';
logout();
document.body.querySelector('.nav-profile .login').style.display = 'block';
document.body.querySelector('.nav-profile .username').style.display = 'none';
}

function showHideNavProfile() {
const profileList = document.querySelector('.nav-profile ul');
if (!profileList) {
return;
}
if (isLoggedIn()) {
profileList.querySelector('.login').style.display = 'none';
profileList.querySelector('.username').style.display = 'block';
const userDetails = getUserDetails();
const userDetailsLink = document.body.querySelector('.nav-profile .username a');
userDetailsLink.textContent = userDetails?.profile?.firstName || i18n('Valued Customer');
} else {
profileList.querySelector('.login').style.display = 'block';
profileList.querySelector('.username').style.display = 'none';
}
}

/**
Expand All @@ -133,24 +157,19 @@ function addProfileLogin(nav) {

const profileMenu = document.createElement('ul');
profileMenu.innerHTML = `
<li class="login">
<a href="#">Sign In</a>
</li>
<li class="username">
<li class="level-1 login"><a href="#">${i18n('Sign In')}</a></li>
<li class="level-1 username">
<a href="#">{Username}</a>
</li>

<li class="user-menu">
<a href="#">Back</a>
<ul>
<li class="profile"><a href="#">Profile</a></li>
<li class="logout"><a href="#">Sign out</a></li>
<ul class="level-2">
<li class="profile"><a href="/account/profile">${i18n('Profile')}</a></li>
<li class="logout"><a href="#">${i18n('Sign out')}</a></li>
</ul>
</li>
`;
profileList.prepend(...profileMenu.childNodes);
profileList.append(...profileMenu.childNodes);
profileList.querySelector('.login a').addEventListener('click', openSignIn);
profileList.querySelector('.user-menu .logout a').addEventListener('click', doLogout);
profileList.querySelector('.username .logout a').addEventListener('click', doLogout);
onProfileUpdate(showHideNavProfile);
}

/**
Expand All @@ -163,10 +182,10 @@ function buildHamburger() {
const icon = document.createElement('div');
icon.classList.add('nav-hamburger-icon');
icon.innerHTML = `
<svg class="open" role="img" aria-hidden="true" tabindex="-1" aria-label="Open Navigation">
<svg class="open" role="img" aria-hidden="true" tabindex="-1" aria-label="${i18n('Open Navigation')}">
<use id="hamburger-icon" xlink:href="/icons/icons.svg#hamburger-white"></use>
</svg>
<svg class="close" role="img" aria-hidden="true" tabindex="-1" aria-label="Close Navigation">
<svg class="close" role="img" aria-hidden="true" tabindex="-1" aria-label="${i18n('Close Navigation')}">
<use id="close-hamburger-icon" xlink:href="/icons/icons.svg#close-x"></use>
</svg>
`;
Expand All @@ -179,6 +198,8 @@ function buildHamburger() {
* @param {Element} block The header block element
*/
export default async function decorate(block) {
i18n = await i18nLookup();

// fetch nav content
const navMeta = getMetadata('nav');
const navPath = navMeta ? new URL(navMeta).pathname : '/nav';
Expand Down Expand Up @@ -253,5 +274,7 @@ export default async function decorate(block) {
navWrapper.className = 'nav-wrapper';
navWrapper.append(nav);
block.querySelector(':scope > div').replaceWith(navWrapper);

showHideNavProfile();
}
}
36 changes: 13 additions & 23 deletions blocks/login/login-delayed.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,22 @@
import { close, displayError, reset } from './login.js';
import { login, isLoggedIn, getUserDetails } from '../../scripts/apis/user.js';
import { login } from '../../scripts/apis/user.js';
import { i18nLookup } from '../../scripts/util.js';

const i18n = await i18nLookup();
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.');
errors.push(i18n('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.');
errors.push(i18n('Password is required.'));
}

if (errors.length > 0) {
Expand All @@ -24,24 +26,15 @@ function isValid(form) {
return true;
}

function loginError(response) {
if (response.status === 401) {
displayError(['Invalid username or password.']);
async function loginError(response) {
if (response.status) {
if (response.status === 401) {
displayError([i18n('Invalid username or password.')]);
} else {
displayError([`${i18n('There was an error logging in')}: (${i18n(await response.text())})`]);
}
} 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';
displayError([`${i18n('There was an error logging in')}: ${i18n(response)}`]);
}
}

Expand All @@ -61,7 +54,6 @@ async function submit(form) {
const userDetails = await login(credentials, loginError);
if (userDetails) {
close();
checkForLoggedInUser();
}
}
}
Expand Down Expand Up @@ -119,5 +111,3 @@ block.querySelector('.cta a.cancel').addEventListener('click', (e) => {
e.stopPropagation();
close();
});

checkForLoggedInUser();
42 changes: 24 additions & 18 deletions blocks/login/login.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { i18nLookup } from '../../scripts/util.js';

let i18n;

const LOGIN_ERROR = 'There was a problem processing your request.';

export function reset() {
Expand Down Expand Up @@ -66,7 +70,9 @@ function initLogin() {
document.head.append(script);
}

export default function decorate(block) {
export default async function decorate(block) {
i18n = await i18nLookup();

block.innerHTML = `
<div class="login-overlay"></div>
<div class="login-form">
Expand All @@ -80,55 +86,55 @@ export default function decorate(block) {
</div>
</div>
<div class="inputs">
<input name="username" aria-label="email address" aria-required="true"
type="text" placeholder="Email Address*" autocomplete="email" >
<input name="password" aria-label="password" aria-required="true"
type="password" placeholder="Password*" autocomplete="current-password">
<input name="username" aria-label="${i18n('email address')}" aria-required="true"
type="text" placeholder="${i18n('Email Address')}*" autocomplete="email" >
<input name="password" aria-label="${i18n('password')}" aria-required="true"
type="password" placeholder="${i18n('Password')}*" autocomplete="current-password">
</div>
<div class="help">
<div class="remember">
<input type="checkbox" name="rememberMe" aria-label="remember me" id="rememberMe">
<div class="checkbox"></div>
<label for="rememberMe">Remember me</label>
<label for="rememberMe">${i18n('Remember me')}</label>
</div>
<a href="#" class="forgot-password" role="button">I forgot my password</a>
<a href="#" class="forgot-password" role="button">${i18n('I forgot my password')}</a>
<div class="warning" role="alert">
Don’t check this box if you are using a public computer or shared device.
${i18n('Don’t check this box if you are using a public computer or shared device.')}
</div>
</div>
<div class="cta">
<div class="button-container">
<a href="" class="button primary submit" role="button">Sign In</a>
<a href="" class="button primary submit" role="button">${i18n('Sign In')}</a>
</div>
<div class="button-container">
<a href="" class="button secondary cancel" role="button">Cancel</a>
<a href="" class="button secondary cancel" role="button">${i18n('Cancel')}</a>
</div>
</div>
<div class="divider">OR</div>
<div class="divider">${i18n('OR')}</div>
<div class="social-sign-in">
<a class="fb button" role="button">
<img src="https://facebookbrand.com/wp-content/uploads/2019/10/Copy-of-facebook-app.svg" alt="facebook icon #1" class="facebook">
<span>Continue with Facebook</span>
<span>${i18n('Continue with Facebook')}</span>
</a>
<a class="google button" role="button">
<img src="data:image/png;base64,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" alt="Google icon #1" loading="lazy" class="google_logo">
<span>Continue with Google</span>
<span>${i18n('Continue with Google')}</span>
</a>
<a class="apple button" role="button">
<span>Continue with Apple</span>
<span>${i18n('Continue with Apple')}</span>
</a>
</div>
<div class="terms">
By clicking 'SIGN IN' or registering using any of the above third-party logins, I agree to the
<a href="/terms-of-use">Terms of Use</a> and <a href="/privacy-policy">Privacy Policy </a> for this website.
${i18n('By clicking \'SIGN IN\' or registering using any of the above third-party logins, I agree to the')}
<a href="/terms-of-use">${i18n('Terms of Use')}</a> ${i18n('and')} <a href="/privacy-policy">${i18n('Privacy Policy')} </a> ${i18n('for this website.')}
</div>
<button type="submit" aria-label="Submit" title="Submit" class="sr-only"></button>
</form>
<div class="create-account">
<div class="container">
Not a member yet?
${i18n('Not a member yet?')}
<br>
<div class="create-button" role="button" tabindex="0">Create an account</div>
<div class="create-button" role="button" tabindex="0">${i18n('Create an account')}</div>
</div>
</div>
</div>
Expand Down
Loading
Loading