Skip to content

Commit

Permalink
Marketing Dashboard and Program Detail Optimization by adding the Con…
Browse files Browse the repository at this point in the history
…tent 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.
  • Loading branch information
TyroneAEM authored Aug 29, 2024
1 parent 4cc7e6c commit 1b8f106
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 27 deletions.
71 changes: 47 additions & 24 deletions blocks/gmo-program-details/gmo-program-details.js
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
};
}
}

Expand Down Expand Up @@ -890,8 +899,9 @@ async function buildCalendar(dataObj, block, type, mappingArray, period) {
</div>
`;
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);

};
Expand Down Expand Up @@ -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");
}
}

Expand Down Expand Up @@ -1367,4 +1390,4 @@ function calculateScroll(type, viewStartYear, displayYear, displayQuarter, numYe

function isValidDate(dateObj) {
return dateObj instanceof Date && !isNaN(dateObj);
}
}
6 changes: 3 additions & 3 deletions blocks/gmo-program-list/gmo-program-list.js
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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;
Expand All @@ -203,7 +204,6 @@ async function buildCampaignList(campaigns, numPerPage) {
const campaignNameWrapper = document.createElement('div');
campaignNameWrapper.classList.add('campaign-name-wrapper', 'vertical-center');


campaignNameWrapper.innerHTML = `
<div class='campaign-name-label' data-property='campaign'>
${checkBlankString(programName)}
Expand Down
14 changes: 14 additions & 0 deletions scripts/graphql.js
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ export async function graphqlAllCampaignsFilter(first,cursor,filter) {
const encodedCursor = encodeURIComponent(cursor);
const encodedFilter = encodeURIComponent(JSON.stringify(filter));
const graphqlEndpoint = `${baseApiUrl}/${projectId}/${queryName}${encodedSemiColon}first=${encodedFirst}${encodedSemiColon}cursor=${encodedCursor}${encodedSemiColon}filter=${encodedFilter}`;
//Performance logging
const startTime = performance.now();
const jwtToken = await getBearerToken();

try {
Expand All @@ -57,6 +59,11 @@ export async function graphqlAllCampaignsFilter(first,cursor,filter) {
},
};
const response = await fetch(`${graphqlEndpoint}`, options);
//Performance logging
const endTime = performance.now();
const executionTime = endTime - startTime;
console.debug(`getAllCampaigns Execution Time: ${executionTime} ms`);

// Handle response codes
if (response.status === 200) {
const responseBody = await response.json();
Expand Down Expand Up @@ -170,6 +177,8 @@ export async function executeQuery(queryString) {
const baseApiUrl = `${await getGraphqlEndpoint()}/graphql/execute.json`;
const projectId = 'gmo';
const queryEndpoint = `${baseApiUrl}/${projectId}/${queryString}`;
//Performance logging
const startTime = performance.now();
const jwtToken = await getBearerToken();

return fetch(queryEndpoint, {
Expand All @@ -181,6 +190,11 @@ export async function executeQuery(queryString) {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
//Performance logging
const endTime = performance.now();
const executionTime = endTime - startTime;
console.debug(`executeQuery for ${queryString} Execution Time: ${executionTime} ms`);

return response.json();
}).then(data => {
return data; // Make sure to return the data so that the promise resolves with it
Expand Down

0 comments on commit 1b8f106

Please sign in to comment.