Please manage your email preferences by using "Unsubscribe" option at the bottom of emails you receive.
+
+
+
+ By providing your telephone number, you are giving permission to Berkshire Hathaway HomeServices and a franchisee
+ member of the Berkshire Hathaway HomeServices real estate network to communicate with you by phone or text,
+ including automated means, even if your telephone number appears on any "Do Not Call" list. A phone number is not
+ required in order to receive real estate brokerage services. Message/data rates may apply. For more about how we will
+ use your contact information, please review our Terms of Use
+ and Privacy Policy.
+
+
+
+ Change Password
+
+
Click reset, and we will send you a email containing a reset password link.
Please manage your email preferences by using "Unsubscribe" option at the bottom of emails you receive.
-
-
-
- By providing your telephone number, you are giving permission to Berkshire Hathaway HomeServices and a franchisee
- member of the Berkshire Hathaway HomeServices real estate network to communicate with you by phone or text,
- including automated means, even if your telephone number appears on any "Do Not Call" list. A phone number is not
- required in order to receive real estate brokerage services. Message/data rates may apply. For more about how we will
- use your contact information, please review our Terms of Use
- and Privacy Policy.
-
-
+
+
+
+
+
Please manage your email preferences by using "Unsubscribe" option at the bottom of emails you receive.
+
+
+
+ By providing your telephone number, you are giving permission to Berkshire Hathaway HomeServices and a franchisee
+ member of the Berkshire Hathaway HomeServices real estate network to communicate with you by phone or text,
+ including automated means, even if your telephone number appears on any "Do Not Call" list. A phone number is not
+ required in order to receive real estate brokerage services. Message/data rates may apply. For more about how we will
+ use your contact information, please review our Terms of Use
+ and Privacy Policy.
+
- Change Password
-
-
Click reset, and we will send you a email containing a reset password link.
-
-
+
+
Click reset, and we will send you a email containing a reset password link.
`;
- 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);
}
/**
diff --git a/blocks/login/login-delayed.js b/blocks/login/login-delayed.js
index f9933533..b0e50350 100644
--- a/blocks/login/login-delayed.js
+++ b/blocks/login/login-delayed.js
@@ -42,6 +42,9 @@ function checkForLoggedInUser() {
document.body.querySelector('.nav-profile .username').style.display = 'block';
const userDetails = getUserDetails();
userDetailsLink.textContent = userDetails?.profile?.firstName || 'Valued Customer';
+ } else {
+ document.body.querySelector('.nav-profile .login').style.display = 'block';
+ document.body.querySelector('.nav-profile .username').style.display = 'none';
}
}
From 354520ffd4ab980f70047d13972a4444c33541bd Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Tue, 23 Jan 2024 15:13:54 -0600
Subject: [PATCH 14/21] Small cleanup
---
blocks/login/login-delayed.js | 17 ++++++++++++-----
1 file changed, 12 insertions(+), 5 deletions(-)
diff --git a/blocks/login/login-delayed.js b/blocks/login/login-delayed.js
index b0e50350..4615bcb1 100644
--- a/blocks/login/login-delayed.js
+++ b/blocks/login/login-delayed.js
@@ -32,19 +32,26 @@ function loginError(response) {
}
}
+function setVisible(selector, visible = true) {
+ const elem = document.querySelector(selector);
+ if (elem) {
+ elem.style.display = visible ? 'block' : 'none';
+ }
+}
+
/**
* 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';
+ setVisible('.nav-profile .login', false);
+ setVisible('.nav-profile .username');
const userDetails = getUserDetails();
+ const userDetailsLink = document.body.querySelector('.nav-profile .username a');
userDetailsLink.textContent = userDetails?.profile?.firstName || 'Valued Customer';
} else {
- document.body.querySelector('.nav-profile .login').style.display = 'block';
- document.body.querySelector('.nav-profile .username').style.display = 'none';
+ setVisible('.nav-profile .login');
+ setVisible('.nav-profile .username', false);
}
}
From 90406a30d11dafe6f0e34f92d40597592d26fce1 Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Tue, 23 Jan 2024 15:21:37 -0600
Subject: [PATCH 15/21] Fix linting gripes
---
blocks/header/header.css | 23 ++++++++++++-----------
1 file changed, 12 insertions(+), 11 deletions(-)
diff --git a/blocks/header/header.css b/blocks/header/header.css
index a57e1261..cbeefda1 100644
--- a/blocks/header/header.css
+++ b/blocks/header/header.css
@@ -133,6 +133,13 @@ body.light-nav {
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);
@@ -162,6 +169,11 @@ body.light-nav {
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;
}
@@ -182,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;
@@ -201,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;
}
From 5272eebecefd714ddba131e987d13f0fbb2dd73b Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Wed, 24 Jan 2024 11:42:13 -0600
Subject: [PATCH 16/21] Better session handling and also moved header
visibility changes into header block (and out of login-delayed)
---
blocks/header/header.js | 27 +++++++-
blocks/login/login-delayed.js | 40 +++---------
blocks/profile/profile.js | 8 ++-
scripts/apis/user.js | 119 +++++++++++++++++++++++-----------
4 files changed, 121 insertions(+), 73 deletions(-)
diff --git a/blocks/header/header.js b/blocks/header/header.js
index 19846ddc..2937eb73 100644
--- a/blocks/header/header.js
+++ b/blocks/header/header.js
@@ -1,7 +1,12 @@
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';
// media query match that indicates mobile/tablet width
const isDesktop = BREAKPOINTS.large;
@@ -124,6 +129,23 @@ function doLogout() {
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 || 'Valued Customer';
+ } else {
+ profileList.querySelector('.login').style.display = 'block';
+ profileList.querySelector('.username').style.display = 'none';
+ }
+}
+
/**
* Adds the Profile submenu to the Nav.
* @param {HTMLDivElement} nav
@@ -148,6 +170,7 @@ function addProfileLogin(nav) {
profileList.append(...profileMenu.childNodes);
profileList.querySelector('.login a').addEventListener('click', openSignIn);
profileList.querySelector('.username .logout a').addEventListener('click', doLogout);
+ onProfileUpdate(showHideNavProfile);
}
/**
@@ -250,5 +273,7 @@ export default async function decorate(block) {
navWrapper.className = 'nav-wrapper';
navWrapper.append(nav);
block.querySelector(':scope > div').replaceWith(navWrapper);
+
+ showHideNavProfile();
}
}
diff --git a/blocks/login/login-delayed.js b/blocks/login/login-delayed.js
index 4615bcb1..c9fca743 100644
--- a/blocks/login/login-delayed.js
+++ b/blocks/login/login-delayed.js
@@ -1,5 +1,5 @@
import { close, displayError, reset } from './login.js';
-import { login, isLoggedIn, getUserDetails } from '../../scripts/apis/user.js';
+import { login } from '../../scripts/apis/user.js';
const block = document.querySelector('.login.block');
@@ -24,34 +24,15 @@ function isValid(form) {
return true;
}
-function loginError(response) {
- if (response.status === 401) {
- displayError(['Invalid username or password.']);
- } else {
- displayError([`There was an error logging in (${response.body})`]);
- }
-}
-
-function setVisible(selector, visible = true) {
- const elem = document.querySelector(selector);
- if (elem) {
- elem.style.display = visible ? 'block' : 'none';
- }
-}
-
-/**
- * Checks if the user is logged in and updates the header accordingly.
- */
-function checkForLoggedInUser() {
- if (isLoggedIn()) {
- setVisible('.nav-profile .login', false);
- setVisible('.nav-profile .username');
- const userDetails = getUserDetails();
- const userDetailsLink = document.body.querySelector('.nav-profile .username a');
- userDetailsLink.textContent = userDetails?.profile?.firstName || 'Valued Customer';
+async function loginError(response) {
+ if (response.status) {
+ if (response.status === 401) {
+ displayError(['Invalid username or password.']);
+ } else {
+ displayError([`There was an error logging in: (${await response.text()})`]);
+ }
} else {
- setVisible('.nav-profile .login');
- setVisible('.nav-profile .username', false);
+ displayError([`There was an error logging in: ${response}`]);
}
}
@@ -71,7 +52,6 @@ async function submit(form) {
const userDetails = await login(credentials, loginError);
if (userDetails) {
close();
- checkForLoggedInUser();
}
}
}
@@ -129,5 +109,3 @@ block.querySelector('.cta a.cancel').addEventListener('click', (e) => {
e.stopPropagation();
close();
});
-
-checkForLoggedInUser();
diff --git a/blocks/profile/profile.js b/blocks/profile/profile.js
index a1c3e588..c4ce4151 100644
--- a/blocks/profile/profile.js
+++ b/blocks/profile/profile.js
@@ -1,8 +1,9 @@
import {
getUserDetails,
requestPasswordReset,
- updateProfile,
+ saveProfile,
isLoggedIn,
+ onProfileUpdate,
} from '../../scripts/apis/user.js';
let form = {};
@@ -79,7 +80,7 @@ function populateForm(block) {
Object.keys(form).forEach((key) => {
form[key].value = profile[key] || '';
// If field is required, append asterisk to placeholder
- if (form[key].required) {
+ if (form[key].required && !form[key].placeholder.endsWith('*')) {
form[key].placeholder += '*';
}
});
@@ -172,7 +173,7 @@ async function performSave() {
Object.keys(form).forEach((key) => {
data[key] = form[key].value;
});
- const response = await updateProfile(data);
+ const response = await saveProfile(data);
if (response.status === 200) {
return response;
}
@@ -260,4 +261,5 @@ export default async function decorate(block) {
populateForm(block);
setupPasswordReset(block);
setupSaveHandlers(block);
+ onProfileUpdate(() => populateForm(block));
}
diff --git a/scripts/apis/user.js b/scripts/apis/user.js
index 8e29552b..550d13a7 100644
--- a/scripts/apis/user.js
+++ b/scripts/apis/user.js
@@ -32,11 +32,12 @@ export function isLoggedIn() {
* @returns {object} user details
*/
export function getUserDetails() {
- if (!isLoggedIn()) {
- return null;
- }
-
const userDetails = sessionStorage.getItem('userDetails');
+ if (!userDetails) {
+ return {
+ profile: {},
+ };
+ }
return JSON.parse(userDetails);
}
@@ -47,13 +48,42 @@ async function fetchUserProfile(username) {
return json;
}
+const profileListeners = [];
+
+/**
+ * Register a callback handler that is fired any time a profile is modified
+ * by either login, logout, updateProfile, or saveProfile
+ *
+ * @param {Function} listener
+ */
+export function onProfileUpdate(listener) {
+ profileListeners.push(listener);
+}
+
+/** Make changes to the user profile in session (does not save to the servlet)
+ * This also triggers any listeners that are registered for profile updates
+ *
+ * @param {Object} Updated user profile
+*/
+export function updateProfile(profile) {
+ const userDetails = getUserDetails();
+
+ // Update profile in session storage using a merge
+ const existingProfile = userDetails.profile;
+ userDetails.profile = { ...existingProfile, ...profile };
+ sessionStorage.setItem('userDetails', JSON.stringify(userDetails));
+ profileListeners.forEach((listener) => {
+ listener(userDetails.profile);
+ });
+}
+
/**
* Attempt to update the user profile. If successful, also update session copy.
* Caller must look at response to see if it was successful, etc.
* @param {Object} Updated user profile
* @returns response object with status, null if user not logged in
*/
-export async function updateProfile(profile) {
+export async function saveProfile(profile) {
const userDetails = getUserDetails();
if (userDetails === null) {
return null;
@@ -92,9 +122,8 @@ export async function updateProfile(profile) {
});
if (response.ok) {
- // Update profile in session storage using a merge
- userDetails.profile = { ...existingProfile, ...profile };
- sessionStorage.setItem('userDetails', JSON.stringify(userDetails));
+ // Update profile in session
+ updateProfile(profile);
}
return response;
@@ -142,6 +171,9 @@ export function logout() {
document.cookie = `${cookie}; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;`;
}
});
+ profileListeners.forEach((listener) => {
+ listener({});
+ });
}
/**
@@ -155,39 +187,50 @@ export function logout() {
*/
export async function login(credentials, failureCallback = null) {
const url = `${API_URL}/cregLoginServlet`;
- const resp = await fetch(url, {
- method: 'POST',
- credentials: 'include',
- mode: 'cors',
- headers: {
- 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
- },
- body: new URLSearchParams({
- Username: credentials.username,
- Password: credentials.password,
- }).toString(),
- });
- if (resp.ok) {
- // Extract contactKey and externalID from response JSON. Store in session
- const responseJson = await resp.json();
- const { contactKey } = responseJson;
- // const { hsfconsumerid } = JSON.parse(externalID);
-
- const profile = await fetchUserProfile(credentials.username);
-
- const sessionData = {
- contactKey,
- // externalID,
- // hsfconsumerid,
- profile,
- username: credentials.username,
- };
- sessionStorage.setItem('userDetails', JSON.stringify(sessionData));
- return sessionData;
+ let error;
+ try {
+ const resp = await fetch(url, {
+ method: 'POST',
+ credentials: 'include',
+ mode: 'cors',
+ headers: {
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
+ },
+ body: new URLSearchParams({
+ Username: credentials.username,
+ Password: credentials.password,
+ }).toString(),
+ });
+ if (resp.ok) {
+ // Extract contactKey and externalID from response JSON. Store in session
+ const responseJson = await resp.json();
+ const { contactKey } = responseJson;
+ // const { hsfconsumerid } = JSON.parse(externalID);
+
+ const profile = await fetchUserProfile(credentials.username);
+
+ const sessionData = {
+ contactKey,
+ // externalID,
+ // hsfconsumerid,
+ profile,
+ username: credentials.username,
+ };
+ sessionStorage.setItem('userDetails', JSON.stringify(sessionData));
+ profileListeners.forEach((listener) => {
+ listener(profile);
+ });
+ return sessionData;
+ }
+ error = resp;
+ } catch (e) {
+ // eslint-disable-next-line no-console
+ console.error(e.message);
+ error = e.message;
}
logout();
if (failureCallback) {
- failureCallback(resp);
+ await failureCallback(error);
}
return null;
}
From 2b69194ce6eda3037cea0c0c9d47f9833f6ef3d5 Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Wed, 24 Jan 2024 15:32:51 -0600
Subject: [PATCH 17/21] Refactored i18n to be in utils
---
blocks/profile/profile.js | 73 ++++++++++++++++++++-------------------
scripts/util.js | 34 ++++++++++++++++++
2 files changed, 72 insertions(+), 35 deletions(-)
diff --git a/blocks/profile/profile.js b/blocks/profile/profile.js
index c4ce4151..087c423f 100644
--- a/blocks/profile/profile.js
+++ b/blocks/profile/profile.js
@@ -5,8 +5,10 @@ import {
isLoggedIn,
onProfileUpdate,
} from '../../scripts/apis/user.js';
+import { i18nLookup } from '../../scripts/util.js';
let form = {};
+let i18n;
function asHtml(string) {
const div = document.createElement('div');
@@ -117,11 +119,11 @@ function showNotification(type, iconHtml, message, message2) {
}
function showError(err) {
- showNotification('error', '', 'There was a problem processing your request.', err);
+ showNotification('error', '', i18n('There was a problem processing your request.'), i18n(err));
}
function showSuccess(message) {
- showNotification('success', '', message);
+ showNotification('success', '', i18n(message));
}
async function getErrorResponseText(errResponse) {
@@ -137,7 +139,7 @@ function setupPasswordReset(block) {
try {
const response = await requestPasswordReset();
if (response.status === 200) {
- showSuccess('Your password has been reset. Check your email for a link to create a new password.');
+ showSuccess(`${i18n('Your password has been reset.')} ${i18n('Check your email for a link to create a new password.')}`);
} else {
throw new Error(await response.text());
}
@@ -149,7 +151,7 @@ function setupPasswordReset(block) {
function validateForm() {
if (!isLoggedIn()) {
- throw new Error('You must be logged in to update your profile.');
+ throw new Error(i18n('You must be logged in to update your profile.'));
}
const errors = [];
@@ -159,7 +161,7 @@ function validateForm() {
if (fieldName.endsWith('*')) {
fieldName = fieldName.slice(0, -1);
}
- errors.push(`${fieldName} is required.`);
+ errors.push(`${fieldName} ${i18n('is required')}.`);
}
});
if (errors.length > 0) {
@@ -208,50 +210,51 @@ function setupSaveHandlers(block) {
}
export default async function decorate(block) {
+ i18n = await i18nLookup();
block.innerHTML = `
-
-
-
-
-
Please manage your email preferences by using "Unsubscribe" option at the bottom of emails you receive.
-
-
+
+
+
+
+
${i18n('Please manage your email preferences by using "Unsubscribe" option at the bottom of emails you receive.')}
+
+
- By providing your telephone number, you are giving permission to Berkshire Hathaway HomeServices and a franchisee
+ ${i18n(`By providing your telephone number, you are giving permission to Berkshire Hathaway HomeServices and a franchisee
member of the Berkshire Hathaway HomeServices real estate network to communicate with you by phone or text,
including automated means, even if your telephone number appears on any "Do Not Call" list. A phone number is not
- required in order to receive real estate brokerage services. Message/data rates may apply. For more about how we will
- use your contact information, please review our Terms of Use
- and Privacy Policy.
+ required in order to receive real estate brokerage services. Message/data rates may apply.`)}
+ ${i18n('For more about how we will use your contact information, please review our')}
+ ${i18n('Terms of Use')} ${i18n('and')} ${i18n('Privacy Policy')}.
-
+
-
-
Click reset, and we will send you a email containing a reset password link.
-
+
+
${i18n('Click reset, and we will send you a email containing a reset password link.')}
+
-
+
-
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
Set the language for emails and this site.
-
Language
+
+
${i18n('Set the language for emails and this site.')}
+
${i18n('Language')}
-
Set the currency and unit of measurement for this site.
-
Currency
+
${i18n('Set the currency and unit of measurement for this site.')}
+
${i18n('Currency')}
-
Unit of Measurement
+
${i18n('Unit of Measurement')}
-
+
`;
diff --git a/scripts/util.js b/scripts/util.js
index ffd1b819..f89c8c4b 100644
--- a/scripts/util.js
+++ b/scripts/util.js
@@ -1,3 +1,5 @@
+import { fetchPlaceholders } from './aem.js';
+
/**
* Creates the standard Spinner Div.
*
@@ -47,9 +49,41 @@ export function showModal(content) {
document.body.append(modal);
}
+function createTextKey(text) {
+ // create a key that can be used to look up the text in the placeholders
+ const words = text.toLowerCase().replace(/[^a-z0-9\s]/g, '').split(/\s+/);
+ if (words.length > 5) {
+ words.splice(5);
+ }
+ words.forEach((word, i) => {
+ if (i > 0) {
+ words[i] = word.charAt(0).toUpperCase() + word.slice(1);
+ }
+ });
+ return words.join('');
+}
+
+export async function i18nLookup(prefix) {
+ const placeholders = await fetchPlaceholders(prefix);
+ return (msg) => {
+ if (placeholders[msg]) {
+ return placeholders[msg];
+ }
+ if (placeholders[msg.toLowerCase()]) {
+ return placeholders[msg.toLowerCase()];
+ }
+ const key = createTextKey(msg);
+ if (placeholders[key]) {
+ return placeholders[key];
+ }
+ return msg;
+ };
+}
+
const Util = {
getSpinner,
showModal,
+ i18nLookup,
};
export default Util;
From 624cd18bd909d59a1b70b686ebe6aeb9d5e543e9 Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Wed, 24 Jan 2024 16:00:11 -0600
Subject: [PATCH 18/21] Add i18n support to header nav
---
blocks/header/header.js | 19 ++++++++++---------
1 file changed, 10 insertions(+), 9 deletions(-)
diff --git a/blocks/header/header.js b/blocks/header/header.js
index 2937eb73..73430699 100644
--- a/blocks/header/header.js
+++ b/blocks/header/header.js
@@ -7,9 +7,11 @@ import {
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') {
@@ -139,7 +141,7 @@ function showHideNavProfile() {
profileList.querySelector('.username').style.display = 'block';
const userDetails = getUserDetails();
const userDetailsLink = document.body.querySelector('.nav-profile .username a');
- userDetailsLink.textContent = userDetails?.profile?.firstName || 'Valued Customer';
+ userDetailsLink.textContent = userDetails?.profile?.firstName || i18n('Valued Customer');
} else {
profileList.querySelector('.login').style.display = 'block';
profileList.querySelector('.username').style.display = 'none';
@@ -155,15 +157,12 @@ function addProfileLogin(nav) {
const profileMenu = document.createElement('ul');
profileMenu.innerHTML = `
-
- 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.')}
- By clicking 'SIGN IN' or registering using any of the above third-party logins, I agree to the
- Terms of Use and Privacy Policy for this website.
+ ${i18n('By clicking \'SIGN IN\' or registering using any of the above third-party logins, I agree to the')}
+ ${i18n('Terms of Use')} ${i18n('and')} ${i18n('Privacy Policy')} ${i18n('for this website.')}
- Not a member yet?
+ ${i18n('Not a member yet?')}
-
Create an account
+
${i18n('Create an account')}
From 307bd7571843c2e1bd991bdc11acc11ca7e58fdb Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Wed, 24 Jan 2024 16:11:11 -0600
Subject: [PATCH 20/21] Add i18n to login form
---
blocks/login/login-delayed.js | 8 +++++---
1 file changed, 5 insertions(+), 3 deletions(-)
diff --git a/blocks/login/login-delayed.js b/blocks/login/login-delayed.js
index c9fca743..e9379979 100644
--- a/blocks/login/login-delayed.js
+++ b/blocks/login/login-delayed.js
@@ -1,6 +1,8 @@
import { close, displayError, reset } from './login.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) {
@@ -27,12 +29,12 @@ function isValid(form) {
async function loginError(response) {
if (response.status) {
if (response.status === 401) {
- displayError(['Invalid username or password.']);
+ displayError([i18n('Invalid username or password.')]);
} else {
- displayError([`There was an error logging in: (${await response.text()})`]);
+ displayError([`${i18n('There was an error logging in')}: (${i18n(await response.text())})`]);
}
} else {
- displayError([`There was an error logging in: ${response}`]);
+ displayError([`${i18n('There was an error logging in')}: ${i18n(response)}`]);
}
}
From 15ac38d8a7d6cfc8a44688f5a4529f9be0b97623 Mon Sep 17 00:00:00 2001
From: Brendan Robert
Date: Wed, 24 Jan 2024 16:12:08 -0600
Subject: [PATCH 21/21] Missing two text strings in i18n for login
---
blocks/login/login-delayed.js | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/blocks/login/login-delayed.js b/blocks/login/login-delayed.js
index e9379979..504253e3 100644
--- a/blocks/login/login-delayed.js
+++ b/blocks/login/login-delayed.js
@@ -9,14 +9,14 @@ 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) {