Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ASSETS-88901 : (Backend) Campaign Page: Filters #72

Merged
merged 11 commits into from
May 2, 2024
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions blocks/gmo-campaign-header/gmo-campaign-header.css
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
border-radius: 4px;
display: flex;
align-items: center;
position: relative; /* Added to be the anchor for absolute positioning for autocomplete feature*/
& > .icon {
background-color: #FFF;
height: 14px;
Expand Down Expand Up @@ -161,3 +162,38 @@
}
}
}

/* Add the following for autocomplete styling */

.autocomplete-items {
position: absolute;
top: 100%;
left: 0;
right: 0;
border: 1px solid #cccccc;
border-top: none;
z-index: 10;
background: #ffffff;
overflow-y: auto;
max-height: 200px;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}

.autocomplete-items div {
padding: 10px 15px;
cursor: pointer;
border-bottom: 1px solid #f0f0f0;
font-size: 14px; /* Adjust the font-size as needed */
color: #333; /* Adjust the text color as needed */
line-height: 1.4; /* Adjust line-height for better readability if necessary */
}

.autocomplete-items div:hover {
background-color: #e9e9e9;
}

.autocomplete-items div:last-child {
border-bottom: none;
}
185 changes: 135 additions & 50 deletions blocks/gmo-campaign-header/gmo-campaign-header.js
Original file line number Diff line number Diff line change
@@ -1,26 +1,25 @@
import { decorateIcons } from '../../scripts/lib-franklin.js';
import { graphqlStatusList, graphqlProductList, graphqlCampaignByName } from '../../scripts/graphql.js';

export default async function decorate(block) {
block.innerHTML = `
<div class="inputs-wrapper">
<div class="search-wrapper">
<span class="icon icon-search"></span>
<input id="campaign-search" maxlength="512" type="search" class="campaign-search" placeholder="Search Marketing Moments...">
<!-- autocomplete feature-->
<div id="autocomplete-list" class="autocomplete-items"></div>
</div>
<div class="filter-wrapper">
<div class="label">Categories</div>
<div class="filter-dropdown" id="campaign-categories">
<div class="label">Business Line</div>
<div class="filter-dropdown" id="campaign-business-line">
<div class="dropdown-button">
<div class="dropdown-label">All Categories</div>
<div class="dropdown-label">All Business Line</div>
<span class="icon icon-chevronDown"></span>
<span class="icon icon-chevronUp inactive"></span>
</div>
<div class="dropdown-content" id="dropdownOptions">
<a href="#" id="option1" data-value="option1" data-type="category" class="dropoption">Option 1</a>
<a href="#" id="option2" data-value="option2" data-type="category" class="dropoption">Option 2</a>
<a href="#" id="option3" data-value="option3" data-type="category" class="dropoption">Option 3</a>
<a href="#" id="option4" data-value="option4" data-type="category" class="dropoption">Option 4</a>
<a href="#" id="option5" data-value="option5" data-type="category" class="dropoption">Option 5</a>
<div class="dropdown-content" id="dropdownBusinessOptions">
<a href="#" id="option1" data-value="digital-media-dme" data-type="category" class="dropoption">Digital Media (DMe)</a>
</div>
</div>
</div>
Expand All @@ -32,7 +31,7 @@ export default async function decorate(block) {
<span class="icon icon-chevronDown"></span>
<span class="icon icon-chevronUp inactive"></span>
</div>
<div class="dropdown-content" id="dropdownOptions">
<div class="dropdown-content" id="dropdownStatusOptions">
<a href="#" id="option1" data-value="option1" data-type="status" class="dropoption">Option 1</a>
<a href="#" id="option2" data-value="option2" data-type="status" class="dropoption">Option 2</a>
<a href="#" id="option3" data-value="option3" data-type="status" class="dropoption">Option 3</a>
Expand All @@ -41,23 +40,7 @@ export default async function decorate(block) {
</div>
</div>
</div>
<div class="filter-wrapper">
<div class="label">Cloud Business</div>
<div class="filter-dropdown" id="campaign-business">
<div class="dropdown-button">
<div class="dropdown-label">All Cloud Businesses</div>
<span class="icon icon-chevronDown"></span>
<span class="icon icon-chevronUp inactive"></span>
</div>
<div class="dropdown-content" id="dropdownOptions">
<a href="#" id="option1" data-value="option1" data-type="business" class="dropoption">Option 1</a>
<a href="#" id="option2" data-value="option2" data-type="business" class="dropoption">Option 2</a>
<a href="#" id="option3" data-value="option3" data-type="business" class="dropoption">Option 3</a>
<a href="#" id="option4" data-value="option4" data-type="business" class="dropoption">Option 4</a>
<a href="#" id="option5" data-value="option5" data-type="business" class="dropoption">Option 5</a>
</div>
</div>
</div>

<div class="filter-wrapper">
<div class="label">Products</div>
<div class="filter-dropdown" id="campaign-products">
Expand All @@ -66,29 +49,12 @@ export default async function decorate(block) {
<span class="icon icon-chevronDown"></span>
<span class="icon icon-chevronUp inactive"></span>
</div>
<div class="dropdown-content" id="dropdownOptions">
<a href="#" id="option1" data-value="option1" data-type="product" class="dropoption">Option 1</a>
<a href="#" id="option2" data-value="option2" data-type="product" class="dropoption">Option 2</a>
<a href="#" id="option3" data-value="option3" data-type="product" class="dropoption">Option 3</a>
<a href="#" id="option4" data-value="option4" data-type="product" class="dropoption">Option 4</a>
<a href="#" id="option5" data-value="option5" data-type="product" class="dropoption">Option 5</a>
</div>
</div>
</div>
<div class="filter-wrapper">
<div class="label">Other (TBD)</div>
<div class="filter-dropdown" id="campaign-other">
<div class="dropdown-button">
<div class="dropdown-label">Other</div>
<span class="icon icon-chevronDown"></span>
<span class="icon icon-chevronUp inactive"></span>
</div>
<div class="dropdown-content" id="dropdownOptions">
<a href="#" id="option1" data-value="option1" data-type="other" class="dropoption">Option 1</a>
<a href="#" id="option2" data-value="option2" data-type="other" class="dropoption">Option 2</a>
<a href="#" id="option3" data-value="option3" data-type="other" class="dropoption">Option 3</a>
<a href="#" id="option4" data-value="option4" data-type="other" class="dropoption">Option 4</a>
<a href="#" id="option5" data-value="option5" data-type="other" class="dropoption">Option 5</a>
<div class="dropdown-content" id="dropdownProductOptions">
<a href="#" id="option1" data-value="option1" data-type="productOffering" class="dropoption">Option 1</a>
<a href="#" id="option2" data-value="option2" data-type="productOffering" class="dropoption">Option 2</a>
<a href="#" id="option3" data-value="option3" data-type="productOffering" class="dropoption">Option 3</a>
<a href="#" id="option4" data-value="option4" data-type="productOffering" class="dropoption">Option 4</a>
<a href="#" id="option5" data-value="option5" data-type="productOffering" class="dropoption">Option 5</a>
</div>
</div>
</div>
Expand All @@ -100,6 +66,115 @@ export default async function decorate(block) {
</div>
<span class="icon icon-close inactive"></span>
`;





// autocomplete feature
const autocompleteList = document.getElementById('autocomplete-list');
// Get the input element by its ID
const searchInput = document.getElementById('campaign-search');
searchInput.addEventListener('input', async function() {
const value = this.value;
if (value)
{
const graphqlData = await graphqlCampaignByName(value);
//Get unique values
const searchItems = Array.from(new Set(graphqlData.data.campaignList.items.map(item => item.campaignName)));
autocomplete(value, searchItems);
}
else
{
//The value has been cleard so trigger the gmo-campaign-list block from the gmo-campaign-header
sendGmoCampaignListBlockEvent();
}
});

function autocomplete(value, items) {
clearAutocomplete();
if (!value) return;
const filteredItems = items.filter(item => item.toLowerCase().includes(value.toLowerCase()));
filteredItems.forEach(item => {
const entry = document.createElement('div');
entry.innerHTML = item;
entry.addEventListener('click', function() {
searchInput.value = this.innerText;
clearAutocomplete();
});
autocompleteList.appendChild(entry);
});
}

function clearAutocomplete() {
while (autocompleteList.firstChild) {
autocompleteList.removeChild(autocompleteList.firstChild);
}
}

// Listen for click events on the autocomplete list
autocompleteList.addEventListener('click', function(event) {
//Trigger the gmo-campaign-list block from the gmo-campaign-header
sendGmoCampaignListBlockEvent();
});

// Listen for change events on the autocomplete list
autocompleteList.addEventListener('change', function(event) {
//Trigger the gmo-campaign-list block from the gmo-campaign-header
sendGmoCampaignListBlockEvent();
});

//Status List
const statusResponse = await graphqlStatusList();
const statuses = statusResponse.data.campaignList.items;

// Extract unique statuses
const uniqueStatuses = Array.from(new Set(statuses.map(item => item.status)));
let dropdownContent = document.getElementById('dropdownStatusOptions');
// Clear existing options
dropdownContent.innerHTML = '';
// Append new options
uniqueStatuses.forEach((status, index) => {
// Create a new anchor element for each status
var anchor = document.createElement('a');
anchor.href = "#";
anchor.id = "option" + (index + 1); // increment index for 1-based id
//anchor.dataset.value = "option" + (index + 1);
anchor.dataset.value = status;
anchor.dataset.type = "status";
anchor.className = "dropoption";
anchor.textContent = status; // using the status as the text
// Append to the dropdown
dropdownContent.appendChild(anchor);
});

//Product List
const productResponse = await graphqlProductList();
const products = productResponse.data.campaignList.items;

// Extract unique statuses
const uniqueProducts = Array.from(new Set(products.map(item => item.productOffering)));
let dropdownProductContent = document.getElementById('dropdownProductOptions');
// Clear existing options
dropdownProductContent.innerHTML = '';
// Append new options
uniqueProducts.forEach((product, index) => {
// Create a new anchor element for each status
var anchor = document.createElement('a');
anchor.href = "#";
anchor.id = "option" + (index + 1); // increment index for 1-based id
//anchor.dataset.value = "option" + (index + 1);
anchor.dataset.value = product;
anchor.dataset.type = "productOffering";//field in graphQL
anchor.className = "dropoption";
anchor.textContent = product; // using the status as the text
// Append to the dropdown
dropdownProductContent.appendChild(anchor);
});

//End product dropdown


document.querySelectorAll('.dropdown-button').forEach((button) => {
button.addEventListener('click', (event) => {
toggleDropdown(event.target);
Expand Down Expand Up @@ -160,6 +235,9 @@ function handleSelectedFilter(option) {
} else {
filterTagRoot.removeChild(document.querySelector(`.selected-filter[data-value='${filterValue}'][data-type='${filterType}']`));
}

//Trigger the gmo-campaign-list block from the gmo-campaign-header
sendGmoCampaignListBlockEvent();
}

function resetAllFilters() {
Expand All @@ -170,6 +248,8 @@ function resetAllFilters() {
const filterTagRoot = document.querySelector('.selected-filters-list');
filterTagRoot.replaceChildren();
checkResetBtn();
//Trigger the gmo-campaign-list block from the gmo-campaign-header
sendGmoCampaignListBlockEvent();
}

function checkResetBtn() {
Expand All @@ -181,3 +261,8 @@ function checkResetBtn() {
resetFiltersBtn.classList.add('inactive');
}
}

function sendGmoCampaignListBlockEvent() {
const blockEvent = new CustomEvent('gmoCampaignListBlock');
document.dispatchEvent(blockEvent);
}
61 changes: 52 additions & 9 deletions blocks/gmo-campaign-list/gmo-campaign-list.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { readBlockConfig } from '../../scripts/lib-franklin.js';
import { decorateIcons } from '../../scripts/lib-franklin.js';
import { graphqlAllCampaigns, graphqlCampaignCount } from '../../scripts/graphql.js';
import { graphqlAllCampaignsFilter, graphqlAllCampaigns, graphqlCampaignCount, generateFilterJSON } from '../../scripts/graphql.js';

const icon = 'https://delivery-p108396-e1046543.adobeaemcloud.com/adobe/assets/deliver/urn:aaid:aem:acdaa42f-00ae-42f4-97e5-8309c42d9076/marketing-hub-102023-lockup-video.png'
TyroneAEM marked this conversation as resolved.
Show resolved Hide resolved

Expand Down Expand Up @@ -36,17 +36,45 @@ const headerConfig = [
let currentPageInfo = {};
let cursorArray = [];
let currentPage = 1;
let currentNumberPerPage = 4
let currentNumberPerPage = 4;
//Get Campaign Count for pagination
const campaignCount = await graphqlCampaignCount();
let campaignCount = await graphqlCampaignCount();

//Custom event gmoCampaignListBlock to allow the gmo-campaign-header to trigger the gmo-campaign-list to update
document.addEventListener('gmoCampaignListBlock', async function() {
//Build graphq filter that is passed to the graphql persisted queries
const graphQLFilterArray = getFilterValues();
const searchInputValue = document.getElementById('campaign-search').value;
if (searchInputValue!=='')
{
graphQLFilterArray.push({type:'campaignName', value:searchInputValue, operator:'='})
}
const graphqlFilter = generateFilterJSON(graphQLFilterArray);
const block = document.querySelector('.gmo-campaign-list.block');
//Get Campaign Count for pagination
campaignCount = await graphqlCampaignCount(graphqlFilter);
//Trigger loading the gmo-campaign-block
//Reset page variables
currentPageInfo = {};
cursorArray = [];
currentPage = 1;
currentNumberPerPage = 4;
decorate( block, currentNumberPerPage, '', false, false, graphqlFilter);
});


export default async function decorate(block, numPerPage = currentNumberPerPage, cursor = '', previousPage = false, nextPage = false) {
export default async function decorate(block, numPerPage = currentNumberPerPage, cursor = '', previousPage = false, nextPage = false, graphQLFilter = {}) {

const campaignPaginatedResponse = await graphqlAllCampaigns(numPerPage, cursor);
const campaignPaginatedResponse = await graphqlAllCampaignsFilter(numPerPage, cursor,graphQLFilter);
const campaigns = campaignPaginatedResponse.data.campaignPaginated.edges;
currentPageInfo = campaignPaginatedResponse.data.campaignPaginated.pageInfo;
//Current cursor used in previous page logic
currentPageInfo.currentCursor = cursor;
//Next Page
if (currentPageInfo.hasNextPage){
currentPageInfo.nextCursor = campaigns[campaigns.length - 1].cursor;
}

currentPageInfo.nextCursor = campaigns[campaigns.length - 1].cursor;
if (!previousPage && !nextPage)
{
cursorArray = campaigns.map(item => item.cursor);
Expand Down Expand Up @@ -89,9 +117,23 @@ export default async function decorate(block, numPerPage = currentNumberPerPage,
{
footerNext.classList.remove('active');
}

decorateIcons(block);
}


function getFilterValues(){
// Select all elements with the class 'selected-filter'
const filters = document.querySelectorAll('.selected-filter');
// Create an array to hold the data-type and data-value attributes
const filterAttributes = [];
// Loop through each filter element and extract both 'data-type' and 'data-value' attributes
filters.forEach(filter => {
const dataType = filter.getAttribute('data-type');
const dataValue = filter.getAttribute('data-value');
filterAttributes.push({ type: dataType, value: dataValue, operator : "=" });
});

return filterAttributes;
}

function buildCampaignList(campaigns, numPerPage) {
Expand Down Expand Up @@ -329,10 +371,11 @@ function nextPage(nextBtn) {
function prevPage(prevBtn) {
if (currentPageInfo.hasPreviousPage) {
currentPage--;

const block = document.querySelector('.gmo-campaign-list.block');
const currentCursor = currentPageInfo.nextCursor || currentPageInfo.currentCursor;
//Calculate cursor for previous page
const indexCursor = cursorArray.indexOf(currentPageInfo.nextCursor) - currentPageInfo.itemCount - currentNumberPerPage;
const indexCursor = cursorArray.indexOf(currentCursor) - currentPageInfo.itemCount - currentNumberPerPage;

decorate(block, currentNumberPerPage, cursorArray[indexCursor], true, false);
if (!(prevBtn.classList.contains('active'))) {
return;
Expand Down
Loading
Loading