From 1b8f1069b8c896414b8a0b3e4fd4dda842f64099 Mon Sep 17 00:00:00 2001 From: TyroneAEM <147942284+TyroneAEM@users.noreply.github.com> Date: Thu, 29 Aug 2024 09:50:03 -0500 Subject: [PATCH] Marketing Dashboard and Program Detail Optimization by adding the Content Fragment Path (#165) * Added performance logging code for queries getAllCampaigns for Program List executeQuery for Program Details * gmo-program-details.js : Added the path parameter to program-details page, which now is passed to the graphql query getProgramDetails which now has the path parameter query ($programName: String, $programID:String, $path:ID) * Fix programName extraction and conditionally add 'path' parameter to query string - Corrected the `extractQueryVars` function to properly handle and return the correct value for `programName`, ensuring that the `+` character is not mistakenly replaced with a space. - Updated the construction of `programQueryString` to include the `path` parameter only if `encodedPath` is not an empty string. - These changes improve the accuracy of parameter handling and optimize the query string construction process. * Updated logging to use console.debug * feat(calendar): implement caching mechanism for thumbnails to optimize performance - Replaced array-based caching with an object-based caching system using a combination of `programName`, `campaignName`, and `deliverableType` as the cache key. - Updated the `addThumbnailToItem` function to utilize the new caching mechanism, reducing redundant API calls. - Converted the outer loop from `forEach` to `for...of` to ensure proper handling of asynchronous operations and to support the sequential execution of `await` calls. * Removed commented out code * Update cache key generation to conditionally include campaignName - Modified the cache key generation logic to only include campaignName if it is not null or an empty string. --- .../gmo-program-details.js | 71 ++++++++++++------- blocks/gmo-program-list/gmo-program-list.js | 6 +- scripts/graphql.js | 14 ++++ 3 files changed, 64 insertions(+), 27 deletions(-) diff --git a/blocks/gmo-program-details/gmo-program-details.js b/blocks/gmo-program-details/gmo-program-details.js index f0cf4f69..232e12ae 100644 --- a/blocks/gmo-program-details/gmo-program-details.js +++ b/blocks/gmo-program-details/gmo-program-details.js @@ -15,13 +15,19 @@ const startDateProp = 'taskPlannedStartDate'; const endDateProp = 'taskPlannedEndDate'; let viewStart, viewEnd, calendarDeliverables; +// Thumbnail cache array object to store the image objects using cacheKey = `${programName}-${campaignName}-${deliverableType}`; +const thumbnailCache = {}; + export default async function decorate(block) { const encodedSemi = encodeURIComponent(';'); const encodedProgram = encodeURIComponent(programName); + const encodedPath = queryVars.path ? `${encodeURIComponent(queryVars.path)}` : ''; blockConfig = readBlockConfig(block); + // Including path in the query if present + const programQueryString = `getProgramDetails${encodedSemi}programName=${encodedProgram}${encodedSemi}programID=${encodeURIComponent(programID)}` + + (encodedPath ? `${encodedSemi}path=${encodedPath}` : ''); - const programQueryString = `getProgramDetails${encodedSemi}programName=${encodedProgram}${encodedSemi}programID=${encodeURIComponent(programID)}`; const deliverableQueryString = `getProgramDeliverables${encodedSemi}programName=${encodedProgram}${encodedSemi}programID=${encodeURIComponent(programID)}`; // Immediately render a placeholder header @@ -705,23 +711,26 @@ function attachListener(htmlElement) { function extractQueryVars() { const urlStr = window.location.href; - const pnRegex = /.*programName=(.*?)&programID=(.*)/; + const pnRegex = /[?&]programName=([^&]+)&programID=([^&]+)(&path=([^&]+))?/; const match = urlStr.match(pnRegex); if (match && match[1] && match[2]) { - const pName = decodeURIComponent(match[1]); - let pID = decodeURIComponent(match[2]) + const pName = decodeURIComponent(match[1]); // Removed the replace method + let pID = decodeURIComponent(match[2]); + let pPath = match[4] ? decodeURIComponent(match[4]) : null; if (pID.endsWith('#')) { pID = pID.slice(0, -1); } return { programName: pName, - programID: pID - } + programID: pID, + path: pPath + }; } else { return { programName: 'Program Name Not Available', - programID: 'Program ID Not Available' - } + programID: 'Program ID Not Available', + path: null + }; } } @@ -890,8 +899,9 @@ async function buildCalendar(dataObj, block, type, mappingArray, period) { `; itemEl.style.width = itemDurationPct + '%'; - // Call the new function to fetch and add the thumbnail - addThumbnailToItem(itemEl, item.programName, item.campaignName,item.deliverableType); + + // Call the new function to fetch and add the thumbnail, ensuring sequential execution + await addThumbnailToItem(itemEl, item.programName, item.campaignName,item.deliverableType); itemWrapper.appendChild(itemEl); }; @@ -1015,20 +1025,33 @@ async function getTaskStatusMapping(taskStatus) { } async function addThumbnailToItem(itemEl, programName, campaignName, deliverableType) { - try { - const imageObject = await searchAsset(programName, campaignName,deliverableType); - if (imageObject && imageObject.imageUrl) { - const thumbnailDiv = itemEl.querySelector('.thumbnail'); - const imgElement = document.createElement('img'); - imgElement.src = imageObject.imageUrl; - imgElement.alt = imageObject.imageAltText; - imgElement.loading = 'lazy'; - thumbnailDiv.appendChild(imgElement); - } else { - console.error("Image Object does not have a valid imageUrl"); + // Create a unique key for the cache based on the parameters, only add the campaignName in cacheKey when it is not null or empty + const cacheKey = campaignName ? `${programName}-${campaignName}-${deliverableType}` : `${programName}-${deliverableType}`; + + // Check if the imageObject is already cached + let imageObject = thumbnailCache[cacheKey]; + + // If not cached, make the API call and store the result in the cache + if (!imageObject) { + try { + imageObject = await searchAsset(programName, campaignName, deliverableType); + thumbnailCache[cacheKey] = imageObject; // Store the result in the cache + } catch (error) { + console.error("Failed to load thumbnail image:", error); + return; // Exit the function if the API call fails } - } catch (error) { - console.error("Failed to load thumbnail image:", error); + } + + // Use the cached or newly fetched imageObject + if (imageObject && imageObject.imageUrl) { + const thumbnailDiv = itemEl.querySelector('.thumbnail'); + const imgElement = document.createElement('img'); + imgElement.src = imageObject.imageUrl; + imgElement.alt = imageObject.imageAltText; + imgElement.loading = 'lazy'; + thumbnailDiv.appendChild(imgElement); + } else { + console.error("Image Object does not have a valid imageUrl"); } } @@ -1367,4 +1390,4 @@ function calculateScroll(type, viewStartYear, displayYear, displayQuarter, numYe function isValidDate(dateObj) { return dateObj instanceof Date && !isNaN(dateObj); -} \ No newline at end of file +} diff --git a/blocks/gmo-program-list/gmo-program-list.js b/blocks/gmo-program-list/gmo-program-list.js index 927093f4..7a9bd3cf 100644 --- a/blocks/gmo-program-list/gmo-program-list.js +++ b/blocks/gmo-program-list/gmo-program-list.js @@ -181,6 +181,7 @@ async function buildCampaignList(campaigns, numPerPage) { const programName = campaign.node.programName; const campaignName = campaign.node.campaignName; const programID = campaign.node.programID ? campaign.node.programID : ""; + const path = campaign.node._path; campaignRow.classList.add('campaign-row'); if ((index + 1) > numPerPage) campaignRow.classList.add('hidden'); @@ -190,9 +191,9 @@ async function buildCampaignList(campaigns, numPerPage) { const campaignIconLink = document.createElement('a'); let campaignDetailsLink = host + `/${detailsPage}?programName=${programName}&`; - campaignDetailsLink += `programID=${programID}` + campaignDetailsLink += `programID=${programID}`; + campaignDetailsLink += `&path=${path}`; campaignIconLink.href = campaignDetailsLink; - const campaignIcon = document.createElement('div'); campaignIcon.classList.add('campaign-icon'); campaignIcon.dataset.programname = programName; @@ -203,7 +204,6 @@ async function buildCampaignList(campaigns, numPerPage) { const campaignNameWrapper = document.createElement('div'); campaignNameWrapper.classList.add('campaign-name-wrapper', 'vertical-center'); - campaignNameWrapper.innerHTML = `