From f552a88bbbb5ec8317bc2420c3d420a80448849b Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Mon, 13 May 2024 16:10:21 -0400 Subject: [PATCH 01/16] initial grouping functionality --- .../gmo-campaign-details.js | 35 ++++++++++++++----- 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 598f9370..2593f372 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -182,7 +182,8 @@ export default async function decorate(block) { } async function buildDeliverablesTable(deliverables, block) { - const rows = buildTableNoGroups(deliverables); + //const rows = buildTableNoGroups(deliverables); + const rows = buildTable(deliverables) const tableRoot = block.querySelector('.table-content'); tableRoot.appendChild(rows); } @@ -308,23 +309,39 @@ function formatDate(dateString) { return formattedDate; } -function buildTable(data) { +function buildTable(jsonResponse) { + //console.log(jsonResponse); + const deliverableList = jsonResponse.data.deliverableList.items; + const programKpi = jsonResponse.data.programList.items.primaryKpi; + //console.log(deliverableList); const rows = document.createElement('div'); - const uniqueCategories = getUniqueValues(data, 'category'); + //const uniqueCategories = getUniqueValues(data, 'deliverableType'); + const uniqueCategories = getUniqueValues(deliverableList, 'deliverableType'); let isRowHidden = true; let emptyCategory = false; uniqueCategories.forEach((category) => { + console.log(category); // build header row let headerRow; - if (!((category == undefined) || (category === ''))) { + if (category == null || category == undefined || category === '') { + console.log('cat is null or empty'); + emptyCategory = true; + headerRow = rows; + } else { headerRow = buildHeaderRow(category, 'header', false); attachListener(headerRow); rows.appendChild(headerRow); - } else { - emptyCategory = true; - headerRow = rows; } - const matchingCampaigns = data.filter(campaign => campaign.category === category); + //const matchingCampaigns = data.filter(campaign => campaign.category === category); + const matchingCampaigns = deliverableList.filter(deliverable => deliverable.deliverableType === category); + matchingCampaigns.forEach((campaign) => { + //isRowHidden = emptyCategory ? false : true; + const tableRow = buildTableRow(campaign, programKpi, !emptyCategory); + headerRow.appendChild(tableRow); + //isRowHidden = true; + }) + emptyCategory = false; + /* // create subcategory headings const subCats = getUniqueValues(matchingCampaigns, 'subcategory'); subCats.forEach((subCat) => { @@ -349,7 +366,9 @@ function buildTable(data) { isRowHidden = true; }); }); + */ }) + //sort the rows return rows; } From 2eebb4a49708d896dda7285993c7b35bb8c600b2 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Tue, 14 May 2024 11:30:22 -0400 Subject: [PATCH 02/16] add sorting, label mappings --- .../gmo-campaign-details.js | 76 ++++++++++--------- scripts/shared-campaigns.js | 30 ++++++++ 2 files changed, 69 insertions(+), 37 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 2593f372..07d36976 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -2,7 +2,7 @@ import { decorateIcons, readBlockConfig } from '../../scripts/lib-franklin.js'; import { getQueryVariable } from '../../scripts/shared.js'; import { getProgramInfo } from '../../scripts/graphql.js'; import { checkBlankString } from '../gmo-campaign-list/gmo-campaign-list.js' -import { statusMappings, productMappings } from '../../scripts/shared-campaigns.js'; +import { statusMappings, productMappings, typeMappings } from '../../scripts/shared-campaigns.js'; import { getBaseConfigPath } from '../../scripts/site-config.js'; let blockConfig; @@ -310,21 +310,17 @@ function formatDate(dateString) { } function buildTable(jsonResponse) { - //console.log(jsonResponse); const deliverableList = jsonResponse.data.deliverableList.items; const programKpi = jsonResponse.data.programList.items.primaryKpi; - //console.log(deliverableList); const rows = document.createElement('div'); - //const uniqueCategories = getUniqueValues(data, 'deliverableType'); const uniqueCategories = getUniqueValues(deliverableList, 'deliverableType'); - let isRowHidden = true; + //let isRowHidden = true; let emptyCategory = false; uniqueCategories.forEach((category) => { console.log(category); // build header row let headerRow; if (category == null || category == undefined || category === '') { - console.log('cat is null or empty'); emptyCategory = true; headerRow = rows; } else { @@ -332,43 +328,15 @@ function buildTable(jsonResponse) { attachListener(headerRow); rows.appendChild(headerRow); } - //const matchingCampaigns = data.filter(campaign => campaign.category === category); const matchingCampaigns = deliverableList.filter(deliverable => deliverable.deliverableType === category); matchingCampaigns.forEach((campaign) => { - //isRowHidden = emptyCategory ? false : true; const tableRow = buildTableRow(campaign, programKpi, !emptyCategory); headerRow.appendChild(tableRow); - //isRowHidden = true; }) emptyCategory = false; - /* - // create subcategory headings - const subCats = getUniqueValues(matchingCampaigns, 'subcategory'); - subCats.forEach((subCat) => { - let subCatHeader; - if (!((subCat == undefined) || (subCat === ''))) { - if(emptyCategory) { - subCatHeader = buildHeaderRow(subCat, 'category', false); - } else { - subCatHeader = buildHeaderRow(subCat, 'subcategory', true); - } - attachListener(subCatHeader); - headerRow.appendChild(subCatHeader); - } else { - subCatHeader = rows; - isRowHidden = false; - } - - const matchingSubs = data.filter(campaign => campaign.subcategory === subCat); - matchingSubs.forEach((campaign) => { - const tableRow = buildTableRow(campaign, isRowHidden); - subCatHeader.appendChild(tableRow); - isRowHidden = true; - }); - }); - */ }) //sort the rows + sortRows(rows); return rows; } @@ -391,12 +359,20 @@ function getUniqueValues(array, filterValue) { return Array.from(uniqueValues); } +function lookupType(rawType) { + const typeLookup = typeMappings[rawType]?.name; + const deliverableTypeLabel = (typeLookup != undefined) ? typeLookup : deliverableJson.deliverableType; + return deliverableTypeLabel; +} + /** * @param {string} category - String value of the category property * @param {string} headerType - Type of header. Either 'category' or 'subcategory' * @param {boolean} isInactive - Determines whether or not the header will be hidden initially */ function buildHeaderRow(category, headerType, isInactive) { + //look up friendly name for deliverable type + const typeLabel = lookupType(category); const headerRow = document.createElement('div'); headerRow.classList.add('row', 'collapsible', 'header'); let divopen; @@ -411,12 +387,14 @@ function buildHeaderRow(category, headerType, isInactive) { ${divopen} -
${category}
+
${typeLabel}
`; return headerRow; } function buildTableRow(deliverableJson, kpi, createHidden) { + //look up friendly name for deliverable type + const typeLabel = lookupType(deliverableJson.deliverableType); const dataRow = document.createElement('div'); dataRow.classList.add('row', 'datarow'); if (createHidden) dataRow.classList.add('inactive'); @@ -429,7 +407,7 @@ function buildTableRow(deliverableJson, kpi, createHidden) { platformString = platformString.slice(0, -2); dataRow.innerHTML = `
${deliverableJson.deliverableName}
-
${deliverableJson.deliverableType}
+
${typeLabel}
${platformString}
Review Link @@ -463,6 +441,30 @@ function buildTableRow(deliverableJson, kpi, createHidden) { return dataRow; } +function sortRows(rows) { + const rowParent = rows; + const nodes = Array.from(rowParent.childNodes); + // Sort child nodes by class name + nodes.sort((a, b) => { + var classA = a.classList ? a.classList.contains('datarow') : false; + var classB = b.classList ? b.classList.contains('datarow') : false; + + if (classA && !classB) { + return 1; + } else if (!classA && classB) { + return -1; + } else { + return 0; + } + }); + + // Rearrange child nodes + nodes.forEach((node) => { + rowParent.appendChild(node); + }); + return rowParent; +} + function attachListener(htmlElement) { htmlElement.querySelector('.heading-wrapper').addEventListener('click', (event) => { const arrow = event.target; diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js index 51993236..25e03bf5 100644 --- a/scripts/shared-campaigns.js +++ b/scripts/shared-campaigns.js @@ -34,4 +34,34 @@ export const productMappings = { "name": "Not Available", "icon": "gear" } +} + +export const typeMappings = { + "email": { + "name": "E-Mail" + }, + "adobe-com": { + "name": "Adobe.com" + }, + "other": { + "name": "Other" + }, + "in-app": { + "name": "In-App" + }, + "paid-media-static": { + "name": "Paid Media: Static" + }, + "paid-media-video": { + "name": "Paid Media: Video" + }, + "owned-social": { + "name": "Owned Social" + }, + "discover": { + "name": "Discover" + }, + null: { + "name": "Not Available" + } } \ No newline at end of file From 8b98605aa3337680ea4da3a2e984d5f91d0f495c Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Tue, 14 May 2024 12:11:09 -0400 Subject: [PATCH 03/16] readd expand/collapse chevrons, add counts --- blocks/gmo-campaign-details/gmo-campaign-details.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 07d36976..4ee84c68 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -185,6 +185,7 @@ async function buildDeliverablesTable(deliverables, block) { //const rows = buildTableNoGroups(deliverables); const rows = buildTable(deliverables) const tableRoot = block.querySelector('.table-content'); + decorateIcons(rows); tableRoot.appendChild(rows); } @@ -320,15 +321,16 @@ function buildTable(jsonResponse) { console.log(category); // build header row let headerRow; + const matchingCampaigns = deliverableList.filter(deliverable => deliverable.deliverableType === category); + const matchCount = matchingCampaigns.length; if (category == null || category == undefined || category === '') { emptyCategory = true; headerRow = rows; } else { - headerRow = buildHeaderRow(category, 'header', false); + headerRow = buildHeaderRow(category, 'header', false, matchCount); attachListener(headerRow); rows.appendChild(headerRow); } - const matchingCampaigns = deliverableList.filter(deliverable => deliverable.deliverableType === category); matchingCampaigns.forEach((campaign) => { const tableRow = buildTableRow(campaign, programKpi, !emptyCategory); headerRow.appendChild(tableRow); @@ -370,7 +372,7 @@ function lookupType(rawType) { * @param {string} headerType - Type of header. Either 'category' or 'subcategory' * @param {boolean} isInactive - Determines whether or not the header will be hidden initially */ -function buildHeaderRow(category, headerType, isInactive) { +function buildHeaderRow(category, headerType, isInactive, matchCount) { //look up friendly name for deliverable type const typeLabel = lookupType(category); const headerRow = document.createElement('div'); @@ -387,7 +389,7 @@ function buildHeaderRow(category, headerType, isInactive) { ${divopen} -
${typeLabel}
+
${typeLabel} (${matchCount})
`; return headerRow; } From f5a94701f77d735bcc0a0289618275bd64913d8c Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Tue, 14 May 2024 15:01:09 -0400 Subject: [PATCH 04/16] add sort by date in groups --- .../gmo-campaign-details.js | 26 ++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 4ee84c68..f92febc2 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -315,10 +315,8 @@ function buildTable(jsonResponse) { const programKpi = jsonResponse.data.programList.items.primaryKpi; const rows = document.createElement('div'); const uniqueCategories = getUniqueValues(deliverableList, 'deliverableType'); - //let isRowHidden = true; let emptyCategory = false; uniqueCategories.forEach((category) => { - console.log(category); // build header row let headerRow; const matchingCampaigns = deliverableList.filter(deliverable => deliverable.deliverableType === category); @@ -335,6 +333,10 @@ function buildTable(jsonResponse) { const tableRow = buildTableRow(campaign, programKpi, !emptyCategory); headerRow.appendChild(tableRow); }) + // sort grouped rows by date + if (!emptyCategory) { + dateSort(headerRow); + } emptyCategory = false; }) //sort the rows @@ -342,6 +344,24 @@ function buildTable(jsonResponse) { return rows; } +function dateSort(parent) { + const childNodes = Array.from(parent.getElementsByClassName('datarow')); + childNodes.sort((a, b) => { + const dateA = new Date(a.querySelector('.completion-date').innerHTML); + const dateB = new Date(b.querySelector('.completion-date').innerHTML); + // Check if dates are valid + if (isNaN(dateA.getTime()) || isNaN(dateB.getTime())) { + return 0; // Move on if date is invalid + } + + return dateA - dateB; + }) + + childNodes.forEach((node) => { + parent.appendChild(node); + }) +} + function buildTableNoGroups(response) { const deliverableList = response.data.deliverableList.items; const programKpi = response.data.programList.items.primaryKpi; @@ -429,7 +449,7 @@ function buildTableRow(deliverableJson, kpi, createHidden) { -
${checkBlankString(deliverableJson.taskCompletionDate)}
+
${checkBlankString(deliverableJson.taskCompletionDate)}
${checkBlankString(deliverableJson.driver)}
` if (!(deliverableJson.linkedFolderLink == null)) { From 1187fdbe874364d607742989286a007a75c148c8 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Wed, 15 May 2024 13:23:30 -0400 Subject: [PATCH 05/16] add important links dynamic generation --- .../gmo-campaign-details.css | 18 +++++++++++++ .../gmo-campaign-details.js | 27 ++++++++++++++++++- .../gmo-campaign-header.css | 1 + blocks/gmo-campaign-list/gmo-campaign-list.js | 4 +-- scripts/shared-campaigns.js | 4 +++ 5 files changed, 51 insertions(+), 3 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.css b/blocks/gmo-campaign-details/gmo-campaign-details.css index d01ded58..02939db5 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.css +++ b/blocks/gmo-campaign-details/gmo-campaign-details.css @@ -257,7 +257,25 @@ body { } } } + .links-wrapper { + display: flex; + flex-direction: column; + width: 400px; + margin-right: 100px; + margin-top: unset; + & .links { + display: flex; + justify-content: space-between; + margin-top: 10px; + } + & .campaign-link { + font: normal normal normal 14px/21px Adobe Clean; + letter-spacing: 0px; + color: #0D66D0; + } + } } + .artifacts-wrapper { display: flex; flex-direction: column; diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index f92febc2..ce7e89d3 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -17,6 +17,7 @@ export default async function decorate(block) { const audiences = buildAudienceList(program).outerHTML; const date = formatDate(program.launchDate); const status = buildStatus(program.status).outerHTML; + const artifactLinks = buildArtifactLinks(program).outerHTML; blockConfig = readBlockConfig(block); block.innerHTML = `
@@ -78,6 +79,7 @@ export default async function decorate(block) {
+ ${artifactLinks}
Total Assets
- 7 +
@@ -168,6 +169,7 @@ export default async function decorate(block) { try { const imageObject = await searchAsset(program.programName, program.campaignName); insertImageIntoCampaignImg(block,imageObject); + document.getElementById('totalassets').textContent = imageObject.assetCount; } catch (error) { console.error("Failed to load campaign image:", error); } @@ -391,6 +393,7 @@ function buildTable(jsonResponse) { } function dateSort(parent) { + // refactor this const childNodes = Array.from(parent.getElementsByClassName('datarow')); childNodes.sort((a, b) => { const dateA = new Date(a.querySelector('.completion-date').innerHTML); @@ -495,7 +498,10 @@ function buildTableRow(deliverableJson, kpi, createHidden) {
-
${checkBlankString(deliverableJson.taskCompletionDate)}
+
+
${checkBlankString(deliverableJson.taskCompletionDate)}
+ ${deliverableJson.previousTaskCompletionDate ? '
Revised from ' + deliverableJson.previousTaskCompletionDate + '
': ""} +
${checkBlankString(deliverableJson.driver)}
`; if (!(deliverableJson.linkedFolderLink == null)) { diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 154dc28a..1c587940 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -9,17 +9,17 @@ const headerConfig = [ { 'name': 'Marketing Moments', 'attribute': 'campaign', - 'sortable': 'true' + 'sortable': true }, { 'name': 'Overview', 'attribute': 'description', - 'sortable': 'false' + 'sortable': false }, { 'name': 'Launch Date', 'attribute': 'launch', - 'sortable': 'true', + 'sortable': true, 'type': 'date' }, { @@ -29,7 +29,7 @@ const headerConfig = [ { 'name': 'Status', 'attribute': 'status', - 'sortable': 'true' + 'sortable': false } ] @@ -96,7 +96,7 @@ export default async function decorate(block, numPerPage = currentNumberPerPage, block.innerHTML = `
Last refreshed date: TBD
-
` + `; const listContainer = block.querySelector('.list-container'); listContainer.appendChild(listHeaders); listContainer.appendChild(listItems); diff --git a/scripts/assets.js b/scripts/assets.js index a1f140c0..9eb62894 100644 --- a/scripts/assets.js +++ b/scripts/assets.js @@ -88,8 +88,9 @@ export async function searchAsset(programName, campaignName, imageWidth = 80) { // Asset retrieved successfully const responseBody = await response.json(); const assetData = responseBody.results[0].hits[0]; + const totalAssets = responseBody.results[0].nbHits; const thumbnailURL = await getOptimizedDeliveryUrl(assetData.assetId, assetData['repo-name'], imageWidth); - return {imageUrl : thumbnailURL, imageAltText: assetData['repo-name']}; + return {imageUrl : thumbnailURL, imageAltText: assetData['repo-name'], assetCount: totalAssets}; } // Handle other response codes throw new Error(`Failed to search asset: ${response.status} ${response.statusText}`); From 7e24357757064ab1622817e892ca07f75070b23a Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Mon, 20 May 2024 15:21:59 -0400 Subject: [PATCH 10/16] implement graphql query for status mapping --- .../gmo-campaign-details.css | 4 +-- .../gmo-campaign-details.js | 25 +++++++++------- .../gmo-campaign-list/gmo-campaign-list.css | 2 ++ blocks/gmo-campaign-list/gmo-campaign-list.js | 30 +++++++++++-------- scripts/graphql.js | 30 +++++++++++++++++-- 5 files changed, 64 insertions(+), 27 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.css b/blocks/gmo-campaign-details/gmo-campaign-details.css index 6a3efa63..1005d4a1 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.css +++ b/blocks/gmo-campaign-details/gmo-campaign-details.css @@ -78,9 +78,9 @@ body { align-items: center; } & .campaign-status { - background-color: #EEF8F5; border-radius: 4px; - color: #33AB84; + color: black; + filter: saturate(0.5); font: normal normal normal 12px/15px Adobe Clean; height: 24px; line-height: 24px; diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 8ecb43b9..7fc5a2f7 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -7,17 +7,16 @@ import { getBaseConfigPath } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; let blockConfig; +const programName = getQueryVariable('programName'); export default async function decorate(block) { - const programName = getQueryVariable('programName'); - const programData = await getProgramInfo(programName, "program"); - const deliverables = getProgramInfo(programName, "deliverables"); + const programData = await getProgramInfo(programName, "getProgramDetails"); + const deliverables = getProgramInfo(programName, "getProgramDeliverables"); const program = programData.data.programList.items[0]; const kpis = buildKPIList(program).outerHTML; const products = buildProductList(program).outerHTML; const audiences = buildAudienceList(program).outerHTML; const date = formatDate(program.launchDate); - const status = buildStatus(program.status).outerHTML; const artifactLinks = buildArtifactLinks(program).outerHTML; blockConfig = readBlockConfig(block); block.innerHTML = ` @@ -192,6 +191,7 @@ export default async function decorate(block) { decorateIcons(block); buildChannelScope(await deliverables, block); buildDeliverablesTable(await deliverables, block); + buildStatus(program.status); } function insertImageIntoCampaignImg(block,imageObject) { @@ -318,14 +318,18 @@ function buildArtifactLinks(program) { return artifactLinks; } -function buildStatus(status) { +async function buildStatus(status) { const statusDiv = document.createElement('div'); statusDiv.classList.add('campaign-status'); - const statusLabel = statusMappings[status].label; - const statusColor = statusMappings[status].color; - statusDiv.textContent = statusLabel; - statusDiv.classList.add(statusColor); - return statusDiv; + const statusJson = await getProgramInfo(programName, "getStatusList") + // use new function that doesn't require programname + const statusArray = statusJson.data.jsonByPath.item.json.options; + const statusMatch = statusArray.filter(item => item.value === status); + const statusText = statusMatch.length > 0 ? statusMatch[0].text : status; + const statusHex = statusMatch[0]["color-code"]; + statusDiv.textContent = statusText; + statusDiv.style.backgroundColor = "#" + statusHex; + document.querySelector('.header-row1').appendChild(statusDiv); } function createAudience(audience) { @@ -393,7 +397,6 @@ function buildTable(jsonResponse) { } function dateSort(parent) { - // refactor this const childNodes = Array.from(parent.getElementsByClassName('datarow')); childNodes.sort((a, b) => { const dateA = new Date(a.querySelector('.completion-date').innerHTML); diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.css b/blocks/gmo-campaign-list/gmo-campaign-list.css index bbac4c8a..2153a884 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.css +++ b/blocks/gmo-campaign-list/gmo-campaign-list.css @@ -231,6 +231,8 @@ select { border-radius: 3px; height: 24px; line-height: 24px; + filter: saturate(0.5); + color: black; } .status.green { background-color: #E8FFF8; diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 1c587940..080ed634 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -1,6 +1,6 @@ import { readBlockConfig } from '../../scripts/lib-franklin.js'; import { decorateIcons } from '../../scripts/lib-franklin.js'; -import { graphqlAllCampaignsFilter, graphqlCampaignCount, generateFilterJSON } from '../../scripts/graphql.js'; +import { graphqlAllCampaignsFilter, graphqlCampaignCount, generateFilterJSON, getMappingInfo } from '../../scripts/graphql.js'; import { productMappings, statusMappings } from '../../scripts/shared-campaigns.js' import { getBaseConfigPath } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; @@ -41,6 +41,7 @@ let currentNumberPerPage = 4; //Get Campaign Count for pagination let campaignCount = await graphqlCampaignCount(); let blockConfig; +let statusMapping = await getMappingInfo("getStatusList"); //Custom event gmoCampaignListBlock to allow the gmo-campaign-header to trigger the gmo-campaign-list to update document.addEventListener('gmoCampaignListBlock', async function() { @@ -205,18 +206,9 @@ async function buildCampaignList(campaigns, numPerPage) { const campaignProducts = buildProductsList(checkBlankString(campaign.node.productOffering)); campaignProducts.classList.add('column-4', 'vertical-center'); - const campaignStatusWrapper = document.createElement('div'); + var campaignStatusWrapper = document.createElement('div'); campaignStatusWrapper.classList.add('status-wrapper', 'column-6', 'vertical-center'); - - const campaignStatus = document.createElement('div'); - const statusStr = checkBlankString(campaign.node.status); - const statusString = statusMappings[statusStr].label; - campaignStatus.textContent = statusString; - campaignStatus.classList.add(statusMappings[statusStr].color); - campaignStatus.classList.add('status'); - campaignStatus.dataset.property = 'status'; - campaignStatusWrapper.appendChild(campaignStatus); - + campaignStatusWrapper = buildStatus(campaignStatusWrapper, campaign); campaignRow.appendChild(campaignInfoWrapper); campaignRow.appendChild(campaignOverviewWrapper); campaignRow.appendChild(campaignLaunch); @@ -228,6 +220,20 @@ async function buildCampaignList(campaigns, numPerPage) { return listWrapper; } +function buildStatus(statusWrapper, campaign) { + const campaignStatus = document.createElement('div'); + const statusStr = checkBlankString(campaign.node.status); + const statusArray = statusMapping.data.jsonByPath.item.json.options; + const statusMatch = statusArray.filter(item => item.value === statusStr); + const statusText = statusMatch.length > 0 ? statusMatch[0].text : statusStr; + campaignStatus.textContent = statusText; + campaignStatus.style.backgroundColor = "#" + statusMatch[0]["color-code"]; + campaignStatus.classList.add('status'); + campaignStatus.dataset.property = 'status'; + statusWrapper.appendChild(campaignStatus); + return statusWrapper; +} + function buildProductsList(productList) { const campaignProducts = document.createElement('div'); const productEl = buildProduct(productList); diff --git a/scripts/graphql.js b/scripts/graphql.js index a35a4cc3..8db1d8b4 100644 --- a/scripts/graphql.js +++ b/scripts/graphql.js @@ -226,11 +226,11 @@ export function generateFilterJSON(filterParams) { export async function getProgramInfo(programName, queryType) { const baseApiUrl = `${await getGraphqlEndpoint()}/graphql/execute.json`; const projectId = 'gmo'; - const queryName = (queryType == "deliverables") ? "getProgramDeliverables" : "getProgramDetails"; + //const queryName = (queryType == "deliverables") ? "getProgramDeliverables" : "getProgramDetails"; const encodedProgramName = encodeURIComponent(programName); const encodedSemiColon = encodeURIComponent(';'); //persisted query URLs have to be encoded together with the first semicolon - const graphqlEndpoint = `${baseApiUrl}/${projectId}/${queryName}${encodedSemiColon}programName=${encodedProgramName}`; + const graphqlEndpoint = `${baseApiUrl}/${projectId}/${queryType}${encodedSemiColon}programName=${encodedProgramName}`; const jwtToken = await getBearerToken(); // Return the fetch promise chain so that it can be awaited outside @@ -251,3 +251,29 @@ export async function getProgramInfo(programName, queryType) { throw error; // Rethrow or handle error as appropriate }); } + +export async function getMappingInfo(queryType) { + const baseApiUrl = `${await getGraphqlEndpoint()}/graphql/execute.json`; + const projectId = 'gmo'; + //persisted query URLs have to be encoded together with the first semicolon + const graphqlEndpoint = `${baseApiUrl}/${projectId}/${queryType}`; + const jwtToken = await getBearerToken(); + + // Return the fetch promise chain so that it can be awaited outside + return fetch(graphqlEndpoint, { + method: 'GET', + headers: { + Authorization: jwtToken, + }, + }).then(response => { + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + return response.json(); + }).then(data => { + return data; // Make sure to return the data so that the promise resolves with it + }).catch(error => { + console.error('Error fetching data: ', error); + throw error; // Rethrow or handle error as appropriate + }); +} \ No newline at end of file From f00b88bdd8d6256c0240af6d15dc2c57c0449b62 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Tue, 21 May 2024 13:39:09 -0400 Subject: [PATCH 11/16] add deliverabletype graphql - todo: refactor, some superfluous function(s) can be removed --- .../gmo-campaign-details.js | 66 +++++++++++++------ blocks/gmo-campaign-list/gmo-campaign-list.js | 4 +- scripts/shared-campaigns.js | 2 + 3 files changed, 51 insertions(+), 21 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 7fc5a2f7..fc514354 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -1,13 +1,14 @@ import { decorateIcons, readBlockConfig } from '../../scripts/lib-franklin.js'; import { getQueryVariable } from '../../scripts/shared.js'; -import { getProgramInfo } from '../../scripts/graphql.js'; +import { getProgramInfo, getMappingInfo } from '../../scripts/graphql.js'; import { checkBlankString } from '../gmo-campaign-list/gmo-campaign-list.js' -import { statusMappings, productMappings, typeMappings } from '../../scripts/shared-campaigns.js'; +import { productMappings, typeMappings } from '../../scripts/shared-campaigns.js'; import { getBaseConfigPath } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; let blockConfig; const programName = getQueryVariable('programName'); +const deliverableMappings = await resolveMappings("getDeliverableTypeMapping"); export default async function decorate(block) { const programData = await getProgramInfo(programName, "getProgramDetails"); @@ -31,7 +32,6 @@ export default async function decorate(block) {
${program.programName} - ${status}
${program.campaignName ? '
' + program.campaignName + '
': ""}
@@ -190,8 +190,12 @@ export default async function decorate(block) { }); decorateIcons(block); buildChannelScope(await deliverables, block); - buildDeliverablesTable(await deliverables, block); + const table = await buildDeliverablesTable(await deliverables, block); + const tableRoot = block.querySelector('.table-content'); + await decorateIcons(table); + tableRoot.appendChild(table); buildStatus(program.status); + //await decorateIcons(block); } function insertImageIntoCampaignImg(block,imageObject) { @@ -201,12 +205,21 @@ function insertImageIntoCampaignImg(block,imageObject) { imgElement.alt = imageObject.imageAltText; campaignImgDiv.appendChild(imgElement); } + async function buildDeliverablesTable(deliverables, block) { //const rows = buildTableNoGroups(deliverables); - const rows = buildTable(deliverables) - const tableRoot = block.querySelector('.table-content'); - decorateIcons(rows); - tableRoot.appendChild(rows); + const rows = buildTable(deliverables); + /* + rows.then(async (datarows) => { + //await decorateIcons(datarows); + }); + */ + //await decorateIcons(await rows); + //const tableRoot = block.querySelector('.table-content'); + //await decorateIcons(rows); + //tableRoot.appendChild(rows); + //await decorateIcons(tableRoot); + return rows; } function switchTab(tab) { @@ -238,6 +251,12 @@ async function buildChannelScope(deliverables, block) { }) } +async function resolveMappings(mappingType) { + const response = await getMappingInfo(mappingType); + const mappingArray = response.data.jsonByPath.item.json.options; + return mappingArray; +} + function buildKPIList(program) { let kpiList = document.createElement('ul'); program.primaryKpi?.forEach((kpi) => { @@ -362,13 +381,13 @@ function formatDate(dateString) { return formattedDate; } -function buildTable(jsonResponse) { +async function buildTable(jsonResponse) { const deliverableList = jsonResponse.data.deliverableList.items; const programKpi = jsonResponse.data.programList.items.primaryKpi; const rows = document.createElement('div'); const uniqueCategories = getUniqueValues(deliverableList, 'deliverableType'); let emptyCategory = false; - uniqueCategories.forEach((category) => { + uniqueCategories.forEach(async (category) => { // build header row let headerRow; const matchingCampaigns = deliverableList.filter(deliverable => deliverable.deliverableType === category); @@ -377,12 +396,12 @@ function buildTable(jsonResponse) { emptyCategory = true; headerRow = rows; } else { - headerRow = buildHeaderRow(category, 'header', false, matchCount); + headerRow = await buildHeaderRow(category, 'header', false, matchCount); attachListener(headerRow); rows.appendChild(headerRow); } - matchingCampaigns.forEach((campaign) => { - const tableRow = buildTableRow(campaign, programKpi, !emptyCategory); + matchingCampaigns.forEach(async (campaign) => { + const tableRow = await buildTableRow(campaign, programKpi, !emptyCategory); headerRow.appendChild(tableRow); }) // sort grouped rows by date @@ -390,7 +409,7 @@ function buildTable(jsonResponse) { dateSort(headerRow); } emptyCategory = false; - }) + }); //sort the rows sortRows(rows); return rows; @@ -433,10 +452,19 @@ function getUniqueValues(array, filterValue) { return Array.from(uniqueValues); } -function lookupType(rawType) { +async function lookupType(rawType) { + // use deliverable type mapping here + const mappings = await deliverableMappings; + const typeMatch = mappings.filter(item => item.value === rawType); + const typeText = typeMatch.length > 0 ? typeMatch[0].text : rawType; + return typeText; + /* + //console.log(mappings); + const typeLookup = typeMappings[rawType]?.name; const deliverableTypeLabel = (typeLookup != undefined) ? typeLookup : deliverableJson.deliverableType; return deliverableTypeLabel; + */ } /** @@ -444,9 +472,9 @@ function lookupType(rawType) { * @param {string} headerType - Type of header. Either 'category' or 'subcategory' * @param {boolean} isInactive - Determines whether or not the header will be hidden initially */ -function buildHeaderRow(category, headerType, isInactive, matchCount) { +async function buildHeaderRow(category, headerType, isInactive, matchCount) { //look up friendly name for deliverable type - const typeLabel = lookupType(category); + const typeLabel = await lookupType(category); const headerRow = document.createElement('div'); headerRow.classList.add('row', 'collapsible', 'header'); let divopen; @@ -466,9 +494,9 @@ function buildHeaderRow(category, headerType, isInactive, matchCount) { return headerRow; } -function buildTableRow(deliverableJson, kpi, createHidden) { +async function buildTableRow(deliverableJson, kpi, createHidden) { //look up friendly name for deliverable type - const typeLabel = lookupType(deliverableJson.deliverableType); + const typeLabel = await lookupType(deliverableJson.deliverableType); const dataRow = document.createElement('div'); dataRow.classList.add('row', 'datarow'); if (createHidden) dataRow.classList.add('inactive'); diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 080ed634..7ec2cbac 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -1,7 +1,7 @@ import { readBlockConfig } from '../../scripts/lib-franklin.js'; import { decorateIcons } from '../../scripts/lib-franklin.js'; import { graphqlAllCampaignsFilter, graphqlCampaignCount, generateFilterJSON, getMappingInfo } from '../../scripts/graphql.js'; -import { productMappings, statusMappings } from '../../scripts/shared-campaigns.js' +import { productMappings } from '../../scripts/shared-campaigns.js' import { getBaseConfigPath } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; @@ -95,7 +95,7 @@ export default async function decorate(block, numPerPage = currentNumberPerPage, const listFooter = buildListFooter(campaignCount, numPerPage); block.innerHTML = ` -
Last refreshed date: TBD
+
`; const listContainer = block.querySelector('.list-container'); diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js index a20f0215..44489197 100644 --- a/scripts/shared-campaigns.js +++ b/scripts/shared-campaigns.js @@ -1,3 +1,4 @@ +/* export const statusMappings = { "PLN": { "label": "Planning", @@ -12,6 +13,7 @@ export const statusMappings = { "color": "green" } } +*/ export const productMappings = { "acrobat-pro": { From b35f3cff1cce9cfae55b967d66b1f5b33426cae5 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Wed, 22 May 2024 15:32:12 -0400 Subject: [PATCH 12/16] refactor product list to use graphql --- .../gmo-campaign-details.js | 94 ++++++------------- blocks/gmo-campaign-list/gmo-campaign-list.js | 35 +++---- icons/{lr-icon.svg => lightroom.svg} | 0 scripts/shared-campaigns.js | 59 +++--------- scripts/site-config.js | 9 ++ 5 files changed, 69 insertions(+), 128 deletions(-) rename icons/{lr-icon.svg => lightroom.svg} (100%) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index fc514354..95cc1d93 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -1,9 +1,9 @@ import { decorateIcons, readBlockConfig } from '../../scripts/lib-franklin.js'; import { getQueryVariable } from '../../scripts/shared.js'; -import { getProgramInfo, getMappingInfo } from '../../scripts/graphql.js'; +import { getProgramInfo } from '../../scripts/graphql.js'; +import { resolveMappings, filterArray } from '../../scripts/shared-campaigns.js'; import { checkBlankString } from '../gmo-campaign-list/gmo-campaign-list.js' -import { productMappings, typeMappings } from '../../scripts/shared-campaigns.js'; -import { getBaseConfigPath } from '../../scripts/site-config.js'; +import { getBaseConfigPath, getProductIconMapping } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; let blockConfig; @@ -15,7 +15,6 @@ export default async function decorate(block) { const deliverables = getProgramInfo(programName, "getProgramDeliverables"); const program = programData.data.programList.items[0]; const kpis = buildKPIList(program).outerHTML; - const products = buildProductList(program).outerHTML; const audiences = buildAudienceList(program).outerHTML; const date = formatDate(program.launchDate); const artifactLinks = buildArtifactLinks(program).outerHTML; @@ -112,7 +111,6 @@ export default async function decorate(block) {
Products
- ${products}
Feature Scope
@@ -190,12 +188,14 @@ export default async function decorate(block) { }); decorateIcons(block); buildChannelScope(await deliverables, block); - const table = await buildDeliverablesTable(await deliverables, block); + buildProductList(program); + const table = await buildTable(await deliverables).then(async (rows) => { + await decorateIcons(rows); + return rows; + }) const tableRoot = block.querySelector('.table-content'); - await decorateIcons(table); tableRoot.appendChild(table); buildStatus(program.status); - //await decorateIcons(block); } function insertImageIntoCampaignImg(block,imageObject) { @@ -206,22 +206,6 @@ function insertImageIntoCampaignImg(block,imageObject) { campaignImgDiv.appendChild(imgElement); } -async function buildDeliverablesTable(deliverables, block) { - //const rows = buildTableNoGroups(deliverables); - const rows = buildTable(deliverables); - /* - rows.then(async (datarows) => { - //await decorateIcons(datarows); - }); - */ - //await decorateIcons(await rows); - //const tableRoot = block.querySelector('.table-content'); - //await decorateIcons(rows); - //tableRoot.appendChild(rows); - //await decorateIcons(tableRoot); - return rows; -} - function switchTab(tab) { if (tab.classList.contains('active') || tab.classList.contains('tab-wrapper')) { return; @@ -251,12 +235,6 @@ async function buildChannelScope(deliverables, block) { }) } -async function resolveMappings(mappingType) { - const response = await getMappingInfo(mappingType); - const mappingArray = response.data.jsonByPath.item.json.options; - return mappingArray; -} - function buildKPIList(program) { let kpiList = document.createElement('ul'); program.primaryKpi?.forEach((kpi) => { @@ -282,23 +260,25 @@ function createKPI(kpi) { return kpiLi; } -function buildProductList(program) { +async function buildProductList(program) { + const configPath = getBaseConfigPath(); let product = checkBlankString(program.productOffering); - const productList = document.createElement('div'); - productList.classList.add('product', 'card-content'); + const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; + const config = await getProductIconMapping(); + const configMatch = filterArray(config, 'Product-offering', product); + const icon = configMatch ? configPath + configMatch[0]['Icon-path'] : defaultIcon; - // Ensure the product exists in the productMappings, otherwise use 'Not Available' - if (!productMappings[product]) { - product = 'Not Available'; - } + const productMappings = await resolveMappings("getProductList"); + const productsMatch = filterArray(productMappings, 'value', product); + const productsText = productsMatch ? productsMatch[0].text : product; - const productName = productMappings[product].name; - const productLabel = productMappings[product].icon; + const productList = document.createElement('div'); + productList.classList.add('product', 'card-content'); productList.innerHTML = ` - - ${productName} + + ${productsText} ` - return productList; + document.querySelector('.card.products').appendChild(productList); } function buildAudienceList(program) { @@ -340,11 +320,9 @@ function buildArtifactLinks(program) { async function buildStatus(status) { const statusDiv = document.createElement('div'); statusDiv.classList.add('campaign-status'); - const statusJson = await getProgramInfo(programName, "getStatusList") - // use new function that doesn't require programname - const statusArray = statusJson.data.jsonByPath.item.json.options; - const statusMatch = statusArray.filter(item => item.value === status); - const statusText = statusMatch.length > 0 ? statusMatch[0].text : status; + const statusArray = await resolveMappings("getStatusList"); + const statusMatch = filterArray(statusArray, 'value', status); + const statusText = statusMatch ? statusMatch[0].text : status; const statusHex = statusMatch[0]["color-code"]; statusDiv.textContent = statusText; statusDiv.style.backgroundColor = "#" + statusHex; @@ -412,6 +390,7 @@ async function buildTable(jsonResponse) { }); //sort the rows sortRows(rows); + await decorateIcons(rows); return rows; } @@ -424,7 +403,6 @@ function dateSort(parent) { if (isNaN(dateA.getTime()) || isNaN(dateB.getTime())) { return 0; // Move on if date is invalid } - return dateA - dateB; }) @@ -433,17 +411,6 @@ function dateSort(parent) { }) } -function buildTableNoGroups(response) { - const deliverableList = response.data.deliverableList.items; - const programKpi = response.data.programList.items.primaryKpi; - const rows = document.createElement('div'); - deliverableList.forEach((deliverable) => { - const tableRow = buildTableRow(deliverable, programKpi, false); - rows.appendChild(tableRow); - }) - return rows; -} - function getUniqueValues(array, filterValue) { const uniqueValues = new Set(); array.forEach(obj => { @@ -453,24 +420,17 @@ function getUniqueValues(array, filterValue) { } async function lookupType(rawType) { - // use deliverable type mapping here const mappings = await deliverableMappings; const typeMatch = mappings.filter(item => item.value === rawType); const typeText = typeMatch.length > 0 ? typeMatch[0].text : rawType; return typeText; - /* - //console.log(mappings); - - const typeLookup = typeMappings[rawType]?.name; - const deliverableTypeLabel = (typeLookup != undefined) ? typeLookup : deliverableJson.deliverableType; - return deliverableTypeLabel; - */ } /** * @param {string} category - String value of the category property * @param {string} headerType - Type of header. Either 'category' or 'subcategory' * @param {boolean} isInactive - Determines whether or not the header will be hidden initially + * @param {number} matchCount - Number of matching items, will display beside the label */ async function buildHeaderRow(category, headerType, isInactive, matchCount) { //look up friendly name for deliverable type diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 7ec2cbac..4a704950 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -1,8 +1,8 @@ import { readBlockConfig } from '../../scripts/lib-franklin.js'; import { decorateIcons } from '../../scripts/lib-franklin.js'; import { graphqlAllCampaignsFilter, graphqlCampaignCount, generateFilterJSON, getMappingInfo } from '../../scripts/graphql.js'; -import { productMappings } from '../../scripts/shared-campaigns.js' -import { getBaseConfigPath } from '../../scripts/site-config.js'; +import { resolveMappings, filterArray } from '../../scripts/shared-campaigns.js' +import { getBaseConfigPath, getProductIconMapping } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; const headerConfig = [ @@ -203,7 +203,7 @@ async function buildCampaignList(campaigns, numPerPage) { campaignLaunch.classList.add('column-3', 'campaign-launch-date', 'vertical-center'); campaignLaunch.dataset.property = 'launch'; - const campaignProducts = buildProductsList(checkBlankString(campaign.node.productOffering)); + const campaignProducts = await buildProductsList(checkBlankString(campaign.node.productOffering)); campaignProducts.classList.add('column-4', 'vertical-center'); var campaignStatusWrapper = document.createElement('div'); @@ -234,28 +234,29 @@ function buildStatus(statusWrapper, campaign) { return statusWrapper; } -function buildProductsList(productList) { +async function buildProductsList(productList) { const campaignProducts = document.createElement('div'); - const productEl = buildProduct(productList); + const productEl = await buildProduct(productList); campaignProducts.appendChild(productEl); return campaignProducts; } -function buildProduct(product) { - const productEl = document.createElement('div'); - productEl.classList.add('product-entry'); - - // Ensure the product exists in the productMappings, otherwise use 'Not Available' - if (!productMappings[product]) { - product = 'Not Available'; - } +async function buildProduct(product) { + const configPath = getBaseConfigPath(); + const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; + const iconMapping = await getProductIconMapping(); + const iconMatch = filterArray(iconMapping, 'Product-offering', product); + const icon = iconMatch ? configPath + iconMatch[0]['Icon-path'] : defaultIcon; - const productLabel = productMappings[product].name; - const productIcon = productMappings[product].icon; + const nameMapping = await resolveMappings("getProductList"); + const nameMatch = filterArray(nameMapping, 'value', product); + const productName = nameMatch ? nameMatch[0].text : product; + const productEl = document.createElement('div'); + productEl.classList.add('product-entry'); productEl.innerHTML = ` - - ${productLabel} + + ${productName} `; return productEl; diff --git a/icons/lr-icon.svg b/icons/lightroom.svg similarity index 100% rename from icons/lr-icon.svg rename to icons/lightroom.svg diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js index 44489197..cc1e7b45 100644 --- a/scripts/shared-campaigns.js +++ b/scripts/shared-campaigns.js @@ -1,20 +1,6 @@ -/* -export const statusMappings = { - "PLN": { - "label": "Planning", - "color": "green" - }, - "CUR": { - "label": "Current", - "color": "green" - }, - "CPL": { - "label": "Complete", - "color": "green" - } -} -*/ +import { getMappingInfo } from "./graphql.js"; +/* export const productMappings = { "acrobat-pro": { "name": "Acrobat Pro", @@ -41,33 +27,18 @@ export const productMappings = { "icon": "gear" } } +*/ -export const typeMappings = { - "email": { - "name": "E-Mail" - }, - "adobe-com": { - "name": "Adobe.com" - }, - "other": { - "name": "Other" - }, - "in-app": { - "name": "In-App" - }, - "paid-media-static": { - "name": "Paid Media: Static" - }, - "paid-media-video": { - "name": "Paid Media: Video" - }, - "owned-social": { - "name": "Owned Social" - }, - "discover": { - "name": "Discover" - }, - null: { - "name": "Not Available" - } +export async function resolveMappings(mappingType) { + const response = await getMappingInfo(mappingType); + const mappingArray = response.data.jsonByPath.item.json.options; + return mappingArray; +} + +/** + * Filter provided array based on provided key/value pair + */ +export function filterArray(array, key, value) { + const arrayMatch = array.filter(match => match[key] === value); + return arrayMatch.length > 0 ? arrayMatch : null; } \ No newline at end of file diff --git a/scripts/site-config.js b/scripts/site-config.js index 268a74f0..fa3cb712 100644 --- a/scripts/site-config.js +++ b/scripts/site-config.js @@ -366,3 +366,12 @@ export async function getLicenseAgreementText() { }); return licenseAgreement; } + +/** + * @returns {Array} + */ +export async function getProductIconMapping() { + const response = await getConfig('site-config.json'); + //return response; + return response['product-icons'].data; +} \ No newline at end of file From 7f95c13c3cb5f2106d7619f36034217c42b014c9 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Thu, 23 May 2024 13:00:04 -0400 Subject: [PATCH 13/16] fix deliverable type tags, fix multiline text --- .../gmo-campaign-details.css | 2 +- .../gmo-campaign-details.js | 16 +++++----- scripts/shared-campaigns.js | 29 ------------------- 3 files changed, 9 insertions(+), 38 deletions(-) diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.css b/blocks/gmo-campaign-details/gmo-campaign-details.css index 1005d4a1..89bbe7fb 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.css +++ b/blocks/gmo-campaign-details/gmo-campaign-details.css @@ -327,7 +327,7 @@ body { &:first-child, &:nth-child(2) { border-top: 2px solid #F4F4F4; } - & .deliverable-name, .platforms { + & .deliverable-name, .platforms, .deliverable-type, .review-link, .kpi { overflow: hidden; text-overflow: ellipsis; line-height: 16px; diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 95cc1d93..fbbb3cfc 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -8,7 +8,8 @@ import { searchAsset } from '../../scripts/assets.js'; let blockConfig; const programName = getQueryVariable('programName'); -const deliverableMappings = await resolveMappings("getDeliverableTypeMapping"); +const deliverableMappings = resolveMappings("getDeliverableTypeMapping"); +const productMappings = resolveMappings("getProductList"); export default async function decorate(block) { const programData = await getProgramInfo(programName, "getProgramDetails"); @@ -226,11 +227,11 @@ async function buildChannelScope(deliverables, block) { return; } const scopesParent = block.querySelector('.channel-scope-wrapper .tags-wrapper'); - uniqueScopes.forEach((scope) => { + uniqueScopes.forEach(async (scope) => { if (scope == null || scope == undefined || scope == '') return; const tag = document.createElement('div'); tag.classList.add('scope-tag'); - tag.textContent = scope; + tag.textContent = await lookupType(scope); scopesParent.appendChild(tag); }) } @@ -268,8 +269,7 @@ async function buildProductList(program) { const configMatch = filterArray(config, 'Product-offering', product); const icon = configMatch ? configPath + configMatch[0]['Icon-path'] : defaultIcon; - const productMappings = await resolveMappings("getProductList"); - const productsMatch = filterArray(productMappings, 'value', product); + const productsMatch = filterArray(await productMappings, 'value', product); const productsText = productsMatch ? productsMatch[0].text : product; const productList = document.createElement('div'); @@ -469,14 +469,14 @@ async function buildTableRow(deliverableJson, kpi, createHidden) { platformString = platformString.slice(0, -2); dataRow.innerHTML = `
${deliverableJson.deliverableName}
-
${typeLabel}
+
${typeLabel}
${platformString}
-
+
-
${checkBlankString(kpi)}
+
${checkBlankString(kpi)}
diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js index cc1e7b45..60686fef 100644 --- a/scripts/shared-campaigns.js +++ b/scripts/shared-campaigns.js @@ -1,34 +1,5 @@ import { getMappingInfo } from "./graphql.js"; -/* -export const productMappings = { - "acrobat-pro": { - "name": "Acrobat Pro", - "icon": "acro-icon" - }, - "acrobat-export-pdf": { - "name": "Acrobat Export PDF", - "icon": "acro-icon" - }, - "lightroom": { - "name": "Lightroom", - "icon": "lr-icon", - }, - "adobe-express": { - "name": "Adobe Express", - "icon": "express-icon" - }, - "photoshop": { - "name": "Photoshop", - "icon": "ps-icon" - }, - "Not Available": { - "name": "Not Available", - "icon": "gear" - } -} -*/ - export async function resolveMappings(mappingType) { const response = await getMappingInfo(mappingType); const mappingArray = response.data.jsonByPath.item.json.options; From 6c6fd4a9bba9ca677d6ddf12221b0de5007665a9 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Thu, 23 May 2024 14:46:33 -0400 Subject: [PATCH 14/16] fix sort on marketing moments column --- blocks/gmo-campaign-list/gmo-campaign-list.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 4a704950..50083966 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -183,8 +183,8 @@ async function buildCampaignList(campaigns, numPerPage) { const campaignName = document.createElement('div'); campaignName.classList.add('campaign-name-wrapper', 'vertical-center'); campaignName.innerHTML = ` -
${checkBlankString(campaign.node.programName)}
-
${checkBlankString(campaign.node.campaignName)}
+
${checkBlankString(campaign.node.programName)}
+
${checkBlankString(campaign.node.campaignName)}
` campaignInfoWrapper.appendChild(campaignIconLink); campaignInfoWrapper.appendChild(campaignName); From 59cd82949de47264c51e5c4514b69db2bd753d15 Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Fri, 24 May 2024 11:18:41 -0400 Subject: [PATCH 15/16] removing merge artifact --- scripts/shared-campaigns.js | 30 ------------------------------ 1 file changed, 30 deletions(-) diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js index 8fbb3cfc..60686fef 100644 --- a/scripts/shared-campaigns.js +++ b/scripts/shared-campaigns.js @@ -12,34 +12,4 @@ export async function resolveMappings(mappingType) { export function filterArray(array, key, value) { const arrayMatch = array.filter(match => match[key] === value); return arrayMatch.length > 0 ? arrayMatch : null; -} - -export const typeMappings = { - "email": { - "name": "E-Mail" - }, - "adobe-com": { - "name": "Adobe.com" - }, - "other": { - "name": "Other" - }, - "in-app": { - "name": "In-App" - }, - "paid-media-static": { - "name": "Paid Media: Static" - }, - "paid-media-video": { - "name": "Paid Media: Video" - }, - "owned-social": { - "name": "Owned Social" - }, - "discover": { - "name": "Discover" - }, - null: { - "name": "Not Available" - } } \ No newline at end of file From cd2a317ffd1921b06b28e5a6c9ff3731f9cfb9fc Mon Sep 17 00:00:00 2001 From: Michael Dickson Date: Fri, 24 May 2024 13:54:55 -0400 Subject: [PATCH 16/16] resolve PR comments --- .../gmo-campaign-details.js | 26 +++---- blocks/gmo-campaign-list/gmo-campaign-list.js | 33 +++------ scripts/shared-campaigns.js | 15 ---- scripts/shared-mappings.js | 72 +++++++++++++++++++ 4 files changed, 89 insertions(+), 57 deletions(-) delete mode 100644 scripts/shared-campaigns.js create mode 100644 scripts/shared-mappings.js diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index c1b1f5a0..11c878f1 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -1,15 +1,15 @@ import { decorateIcons, readBlockConfig } from '../../scripts/lib-franklin.js'; import { getQueryVariable } from '../../scripts/shared.js'; import { getProgramInfo } from '../../scripts/graphql.js'; -import { resolveMappings, filterArray } from '../../scripts/shared-campaigns.js'; +import { resolveMappings, filterArray, getProductMapping } from '../../scripts/shared-mappings.js'; import { checkBlankString } from '../gmo-campaign-list/gmo-campaign-list.js' -import { getBaseConfigPath, getProductIconMapping } from '../../scripts/site-config.js'; +import { getBaseConfigPath } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; let blockConfig; const programName = getQueryVariable('programName'); const deliverableMappings = resolveMappings("getDeliverableTypeMapping"); -const productMappings = resolveMappings("getProductList"); +//const productMappings = resolveMappings("getProductList"); export default async function decorate(block) { @@ -29,7 +29,6 @@ export default async function decorate(block) { const targetMarketAreas = buildTargetMarketAreaList(p0TargetMarketArea,p1TargetMarketArea).outerHTML; - const products = buildProductList(program).outerHTML; const audiences = buildAudienceList(program).outerHTML; const date = formatDate(program.launchDate); const artifactLinks = buildArtifactLinks(program).outerHTML; @@ -189,7 +188,7 @@ export default async function decorate(block) {
`; - + buildProductCard(program); try { const imageObject = await searchAsset(program.programName, program.campaignName); insertImageIntoCampaignImg(block,imageObject); @@ -321,22 +320,13 @@ function createLI(li) { return liItem; } -async function buildProductList(program) { - const configPath = getBaseConfigPath(); - let product = checkBlankString(program.productOffering); - const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; - const config = await getProductIconMapping(); - const configMatch = filterArray(config, 'Product-offering', product); - const icon = configMatch ? configPath + configMatch[0]['Icon-path'] : defaultIcon; - - const productsMatch = filterArray(await productMappings, 'value', product); - const productsText = productsMatch ? productsMatch[0].text : product; - +async function buildProductCard(program) { + const productMapping = await getProductMapping(program.productOffering); const productList = document.createElement('div'); productList.classList.add('product', 'card-content'); productList.innerHTML = ` - - ${productsText} + + ${productMapping.label} ` document.querySelector('.card.products').appendChild(productList); } diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 8215e6a9..76e6f8fd 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -1,8 +1,8 @@ import { readBlockConfig } from '../../scripts/lib-franklin.js'; import { decorateIcons } from '../../scripts/lib-franklin.js'; import { graphqlAllCampaignsFilter, graphqlCampaignCount, generateFilterJSON, getMappingInfo } from '../../scripts/graphql.js'; -import { resolveMappings, filterArray } from '../../scripts/shared-campaigns.js' -import { getBaseConfigPath, getProductIconMapping } from '../../scripts/site-config.js'; +import { getProductMapping } from '../../scripts/shared-mappings.js' +import { getBaseConfigPath } from '../../scripts/site-config.js'; import { searchAsset } from '../../scripts/assets.js'; const headerConfig = [ @@ -207,7 +207,7 @@ async function buildCampaignList(campaigns, numPerPage) { campaignLaunch.classList.add('column-3', 'campaign-launch-date', 'vertical-center'); campaignLaunch.dataset.property = 'launch'; - const campaignProducts = await buildProductsList(checkBlankString(campaign.node.productOffering)); + const campaignProducts = await buildProduct(checkBlankString(campaign.node.productOffering)); campaignProducts.classList.add('column-4', 'vertical-center'); var campaignStatusWrapper = document.createElement('div'); @@ -238,32 +238,17 @@ function buildStatus(statusWrapper, campaign) { return statusWrapper; } -async function buildProductsList(productList) { - const campaignProducts = document.createElement('div'); - const productEl = await buildProduct(productList); - campaignProducts.appendChild(productEl); - return campaignProducts; -} - async function buildProduct(product) { - const configPath = getBaseConfigPath(); - const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; - const iconMapping = await getProductIconMapping(); - const iconMatch = filterArray(iconMapping, 'Product-offering', product); - const icon = iconMatch ? configPath + iconMatch[0]['Icon-path'] : defaultIcon; - - const nameMapping = await resolveMappings("getProductList"); - const nameMatch = filterArray(nameMapping, 'value', product); - const productName = nameMatch ? nameMatch[0].text : product; - + const productParent = document.createElement('div'); + const productMapping = await getProductMapping(product); const productEl = document.createElement('div'); productEl.classList.add('product-entry'); productEl.innerHTML = ` - - ${productName} + + ${productMapping.label} `; - - return productEl; + productParent.appendChild(productEl); + return productParent; } function buildListHeaders(headerConfig) { diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js deleted file mode 100644 index 60686fef..00000000 --- a/scripts/shared-campaigns.js +++ /dev/null @@ -1,15 +0,0 @@ -import { getMappingInfo } from "./graphql.js"; - -export async function resolveMappings(mappingType) { - const response = await getMappingInfo(mappingType); - const mappingArray = response.data.jsonByPath.item.json.options; - return mappingArray; -} - -/** - * Filter provided array based on provided key/value pair - */ -export function filterArray(array, key, value) { - const arrayMatch = array.filter(match => match[key] === value); - return arrayMatch.length > 0 ? arrayMatch : null; -} \ No newline at end of file diff --git a/scripts/shared-mappings.js b/scripts/shared-mappings.js new file mode 100644 index 00000000..a44c6816 --- /dev/null +++ b/scripts/shared-mappings.js @@ -0,0 +1,72 @@ +import { getMappingInfo } from "./graphql.js"; +import { getProductIconMapping, getBaseConfigPath } from './site-config.js'; + +export async function resolveMappings(mappingType) { + const response = await getMappingInfo(mappingType); + const mappingArray = response.data.jsonByPath.item.json.options; + return mappingArray; +} + +/** + * Filter provided array based on provided key/value pair + */ +export function filterArray(array, key, value) { + const arrayMatch = array.filter(match => match[key] === value); + return arrayMatch.length > 0 ? arrayMatch : null; +} +/* +async function buildProduct(product) { + const configPath = getBaseConfigPath(); + const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; + const iconMapping = await getProductIconMapping(); + const iconMatch = filterArray(iconMapping, 'Product-offering', product); + const icon = iconMatch ? configPath + iconMatch[0]['Icon-path'] : defaultIcon; + + const nameMapping = await resolveMappings("getProductList"); + const nameMatch = filterArray(nameMapping, 'value', product); + const productName = nameMatch ? nameMatch[0].text : product; + + const productEl = document.createElement('div'); + productEl.classList.add('product-entry'); + productEl.innerHTML = ` + + ${productName} + `; + + return productEl; +} +async function buildProductList(program) { + const configPath = getBaseConfigPath(); + const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; + const iconMapping = await getProductIconMapping(); + const iconMatch = filterArray(iconMapping, 'Product-offering', product); + const icon = iconMatch ? configPath + iconMatch[0]['Icon-path'] : defaultIcon; + + const productsMatch = filterArray(await resolveMappings("getProductList"), 'value', product); + const productsText = productsMatch ? productsMatch[0].text : product; + + const productList = document.createElement('div'); + productList.classList.add('product', 'card-content'); + productList.innerHTML = ` + + ${productsText} + ` + document.querySelector('.card.products').appendChild(productList); +} +*/ + +export async function getProductMapping(product) { + const configPath = getBaseConfigPath(); + const defaultIcon = configPath + '/logo/products/default-app-icon.svg'; + const iconMapping = await getProductIconMapping(); + const iconMatch = filterArray(iconMapping, 'Product-offering', product); + const icon = iconMatch ? configPath + iconMatch[0]['Icon-path'] : defaultIcon; + + const productsMatch = filterArray(await resolveMappings("getProductList"), 'value', product); + const productsText = productsMatch ? productsMatch[0].text : product; + + return { + label: productsText, + icon: icon + } +} \ No newline at end of file