diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.css b/blocks/gmo-campaign-details/gmo-campaign-details.css index d0bae026..bc4a3596 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.css +++ b/blocks/gmo-campaign-details/gmo-campaign-details.css @@ -12,6 +12,7 @@ body { align-items: center; border: 1px solid #D3D3D3; border-radius: 4px; + cursor: pointer; & > .icon { width: 20px; height: 30px; @@ -54,6 +55,9 @@ body { width: 17px; } } + & > .campaign-img { + background-color: #e1e1e1; + } & .header-row1 { display: flex; align-items: center; @@ -127,11 +131,18 @@ body { font: normal normal normal 14px/21px Adobe Clean; letter-spacing: 0px; color: black; + &.hide-overflow { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 3; + overflow: hidden; + } } & .button.no-bg { font: normal normal normal 14px/17px Adobe Clean; margin-top: 10px; color: #505050; + cursor: pointer; } } .kpis-wrapper { @@ -142,6 +153,10 @@ body { -moz-columns: 2; font: normal normal normal 14px/21px Adobe Clean; } + & div { + font-size: 14px; + margin-top: 15px; + } } .use-cases-wrapper { margin-bottom: 30px; @@ -209,6 +224,9 @@ body { padding-left: 15px; } } + &.audiences > div { + font-size: 14px; + } } .milestone, .card-content { font: normal normal normal 14px/21px Adobe Clean; diff --git a/blocks/gmo-campaign-details/gmo-campaign-details.js b/blocks/gmo-campaign-details/gmo-campaign-details.js index 03babcda..760dc344 100644 --- a/blocks/gmo-campaign-details/gmo-campaign-details.js +++ b/blocks/gmo-campaign-details/gmo-campaign-details.js @@ -1,4 +1,9 @@ import { decorateIcons } from '../../scripts/lib-franklin.js'; +import { getQueryVariable } from '../../scripts/shared.js'; +import { getProgramDetails } from '../../scripts/graphql.js'; +import { checkBlankString } from '../gmo-campaign-list/gmo-campaign-list.js' +import { statusMappings, productMappings } from '../../scripts/shared-campaigns.js'; +import { getBaseConfigPath } from '../../scripts/site-config.js'; const testData = [ { @@ -423,7 +428,16 @@ import { decorateIcons } from '../../scripts/lib-franklin.js'; export default async function decorate(block) { //const rows = buildTable(testData); + // /graphql/execute.json/gmo/getProgramDetails;programName= + const programName = getQueryVariable('programName'); + const graphqlData = await getProgramDetails(programName); + const program = graphqlData.data.programList.items[0]; const rows = buildTableNoGroups(testData); + 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; block.innerHTML = `
@@ -435,12 +449,12 @@ export default async function decorate(block) {
- Express Mobile Beta -
In Progress
+ ${program.programName} + ${status}
- 03/07/2024 + ${date}
@@ -453,20 +467,11 @@ export default async function decorate(block) {
At a Glance Product Value - - Express mobile public beta is not a major at scale marketing moment (due to the limited nature of beta experience) with key audiences of - Existing Express users, investors and media. Marketing approach is signaling to the market our continued momentum with the new mobile - beta release, focusing efforts on PR, social/community and in-app surfaces. - -
Read more
+
${checkBlankString(program.productValue.plaintext)}
+
Read more
KPIs to Measure Success -
    -
  • PR impressions & dedicated earned stories
  • -
  • Mobile exports
  • -
  • Community & social interactions
  • -
  • 100% by EOL
  • -
+ ${kpis}
Hero Use Cases @@ -519,10 +524,7 @@ export default async function decorate(block) {
Products
-
- - Adobe Express Mobile App -
+ ${products}
Feature Scope
@@ -534,26 +536,7 @@ export default async function decorate(block) {
Audiences
-
- - Existing Express Users -
-
- - Prospects with priority on communicators -
-
- - CC entitled members who have not used Express -
-
- - CC free unentitled members on mobile (PsX, LR) -
-
- - K12 -
+ ${audiences}
@@ -597,6 +580,13 @@ export default async function decorate(block) { block.querySelector('.tab-wrapper').addEventListener('click', (event) => { switchTab(event.target); }) + block.querySelector('.back-button').addEventListener('click', () => { + const host = location.origin + getBaseConfigPath(); + document.location.href = host + `/campaigns`; + }) + block.querySelector('.read-more').addEventListener('click', () => { + document.querySelector('.overview-wrapper > .description').classList.toggle('hide-overflow'); + }) decorateIcons(block); } @@ -612,6 +602,98 @@ function switchTab(tab) { tab.classList.toggle('active'); } +function buildKPIList(program) { + let kpiList = document.createElement('ul'); + program.primaryKpi?.forEach((kpi) => { + const kpiLi = createKPI(kpi); + kpiList.appendChild(kpiLi); + }) + program.additionalKpi?.forEach((kpi) => { + const kpiLi = createKPI(kpi); + kpiList.appendChild(kpiLi); + }) + if (kpiList.children.length == 0) { + kpiList.remove(); + kpiList = document.createElement('div'); + kpiList.textContent = "Not Available"; + } + return kpiList; +} + +function createKPI(kpi) { + const kpiLi = document.createElement('li'); + const kpiText = parseString(kpi); + kpiLi.textContent = kpiText; + return kpiLi; +} + +function buildProductList(program) { + const product = checkBlankString(program.productOffering); + const productList = document.createElement('div'); + productList.classList.add('product', 'card-content'); + const productName = productMappings[product].name; + const productLabel = productMappings[product].icon; + productList.innerHTML = ` + + ${productName} + ` + return productList; +} + +function buildAudienceList(program) { + const audienceList = document.createElement('div'); + program.primaryAudience?.forEach((audience) => { + const audienceDiv = createAudience(audience); + audienceList.appendChild(audienceDiv); + }) + program.additionalAudiences?.forEach((audience) => { + const audienceDiv = createAudience(audience); + audienceList.appendChild(audienceDiv); + }) + if (audienceList.children.length == 0) audienceList.textContent = "Not Available"; + return audienceList; +} + +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; +} + +function createAudience(audience) { + const text = parseString(audience); + const audienceDiv = document.createElement('div'); + audienceDiv.classList.add('audience', 'card-content'); + audienceDiv.innerHTML = ` + + ${text} + `; + return audienceDiv; +} + +function parseString(text) { + let parsed = text.replace(/-/g, ' ').split(' '); + parsed[0] = parsed[0].charAt(0).toUpperCase() + parsed[0].slice(1); + parsed = parsed.join(' '); + return parsed; +} + +function formatDate(dateString) { + const parts = dateString.split('-'); + const yyyy = parts[0]; + const mm = parts[1]; + const dd = parts[2]; + + // Formatting the date into mm/dd/yyyy format + const formattedDate = mm + '/' + dd + '/' + yyyy; + + return formattedDate; +} + function buildTable(data) { const rows = document.createElement('div'); const uniqueCategories = getUniqueValues(data, 'category'); diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.css b/blocks/gmo-campaign-list/gmo-campaign-list.css index a636f0b9..08bd5ad6 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.css +++ b/blocks/gmo-campaign-list/gmo-campaign-list.css @@ -89,6 +89,9 @@ body { } .campaign-description { line-height:20px; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 4; overflow: hidden; text-overflow: ellipsis; } diff --git a/blocks/gmo-campaign-list/gmo-campaign-list.js b/blocks/gmo-campaign-list/gmo-campaign-list.js index 6894173c..c66257df 100644 --- a/blocks/gmo-campaign-list/gmo-campaign-list.js +++ b/blocks/gmo-campaign-list/gmo-campaign-list.js @@ -1,6 +1,8 @@ import { readBlockConfig } from '../../scripts/lib-franklin.js'; import { decorateIcons } from '../../scripts/lib-franklin.js'; import { graphqlAllCampaignsFilter, graphqlCampaignCount, generateFilterJSON } from '../../scripts/graphql.js'; +import { productMappings, statusMappings } from '../../scripts/shared-campaigns.js' +import { getBaseConfigPath } from '../../scripts/site-config.js'; const headerConfig = [ { @@ -102,17 +104,13 @@ export default async function decorate(block, numPerPage = currentNumberPerPage, const footerPrev = document.querySelector('.footer-pagination-button.prev'); if (currentPageInfo.hasPreviousPage){ footerPrev.classList.add('active'); - } - else - { + } else { footerPrev.classList.remove('active'); } if (currentPageInfo.hasNextPage){ footerNext.classList.add('active'); - } - else - { + } else { footerNext.classList.remove('active'); } decorateIcons(block); @@ -138,22 +136,29 @@ function buildCampaignList(campaigns, numPerPage) { const listWrapper = document.createElement('div'); listWrapper.classList.add('list-items'); listWrapper.dataset.totalresults = campaigns.length; - + const host = location.origin + getBaseConfigPath(); + campaigns.forEach((campaign, index) => { const campaignRow = document.createElement('div'); campaignRow.classList.add('campaign-row'); if ((index + 1) > numPerPage) campaignRow.classList.add('hidden'); const campaignInfoWrapper = document.createElement('div'); campaignInfoWrapper.classList.add('campaign-info-wrapper','column-1'); + const campaignIconLink = document.createElement('a'); + campaignIconLink.href = host + `/campaign-details?programName=${campaign.node.programName}` + const campaignIcon = document.createElement('div'); campaignIcon.classList.add('campaign-icon'); + campaignIcon.dataset.programname = campaign.node.programName; + campaignIcon.dataset.campaignname = campaign.node.campaignName; + campaignIconLink.appendChild(campaignIcon); const campaignName = document.createElement('div'); campaignName.classList.add('campaign-name-wrapper', 'vertical-center'); campaignName.innerHTML = `
${checkBlankString(campaign.node.campaignName)}
${checkBlankString(campaign.node.programName)}
` - campaignInfoWrapper.appendChild(campaignIcon); + campaignInfoWrapper.appendChild(campaignIconLink); campaignInfoWrapper.appendChild(campaignName); const campaignOverviewWrapper = document.createElement('div'); campaignOverviewWrapper.classList.add('column-2', 'campaign-description-wrapper','vertical-center'); @@ -171,9 +176,10 @@ function buildCampaignList(campaigns, numPerPage) { const campaignStatusWrapper = document.createElement('div'); campaignStatusWrapper.classList.add('status-wrapper', 'column-6','vertical-center'); const campaignStatus = document.createElement('div'); - const statusString = checkBlankString(campaign.node.status); + const statusStr = checkBlankString(campaign.node.status); + const statusString = statusMappings[statusStr].label; campaignStatus.textContent = statusString; - campaignStatus.classList.add(determineStatusColor(statusString)); + campaignStatus.classList.add(statusMappings[statusStr].color); campaignStatus.classList.add('status'); campaignStatus.dataset.property = 'status'; campaignStatusWrapper.appendChild(campaignStatus); @@ -197,13 +203,16 @@ function buildProductsList(productList) { function buildProduct(product) { const productEl = document.createElement('div'); - let productIcon = product; - if (product == null) product = 'None'; - if (product == 'Not Available') productIcon = "gear"; + //let productIcon = product; + if (product == null) product = 'Not Available'; + //if (product == 'Not Available') productIcon = "gear"; + const productLabel = productMappings[product].name; + const productIcon = productMappings[product].icon; + productEl.classList.add('product-entry'); productEl.innerHTML = ` - ${product} + ${productLabel} ` return productEl; } @@ -431,6 +440,7 @@ function sortColumn(dir, property) { }); } +/* function determineStatusColor(status) { switch(status) { case 'Current': @@ -443,12 +453,18 @@ function determineStatusColor(status) { return 'red'; } } +*/ // supply dummy data if none present -function checkBlankString(string) { +export function checkBlankString(string) { if (string == undefined || string == '' ) { return 'Not Available'; } else { return string; } } + +function openCampaignDetails(campaignName) { + document.location.href = `/campaign-details?campaignName=${campaignName}`; +} + diff --git a/icons/Acrobat.svg b/icons/acro-icon.svg similarity index 100% rename from icons/Acrobat.svg rename to icons/acro-icon.svg diff --git a/icons/Express.svg b/icons/express-icon.svg similarity index 100% rename from icons/Express.svg rename to icons/express-icon.svg diff --git a/icons/Lightroom.svg b/icons/lr-icon.svg similarity index 100% rename from icons/Lightroom.svg rename to icons/lr-icon.svg diff --git a/icons/Photoshop.svg b/icons/ps-icon.svg similarity index 100% rename from icons/Photoshop.svg rename to icons/ps-icon.svg diff --git a/scripts/graphql.js b/scripts/graphql.js index 35243c75..e39b9c3c 100644 --- a/scripts/graphql.js +++ b/scripts/graphql.js @@ -221,3 +221,32 @@ export function generateFilterJSON(filterParams) { console.debug('result', result); return result; } + +export async function getProgramDetails(programName) { + const baseApiUrl = `${await getGraphqlEndpoint()}/graphql/execute.json`; + const projectId = 'gmo'; + const queryName = '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 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 + }); +} diff --git a/scripts/shared-campaigns.js b/scripts/shared-campaigns.js new file mode 100644 index 00000000..51993236 --- /dev/null +++ b/scripts/shared-campaigns.js @@ -0,0 +1,37 @@ +export const statusMappings = { + "PLN": { + "label": "Planning", + "color": "green" + }, + "CUR": { + "label": "Current", + "color": "green" + }, + "CPL": { + "label": "Complete", + "color": "green" + } +} + +export const productMappings = { + "acrobat-pro": { + "name": "Acrobat Pro", + "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" + } +} \ No newline at end of file