NotesWhat is notes.io?

Notes brand slogan

Notes - notes.io

export default class CreatorHistoryScreen {
constructor(el) {
this.el = el;
this.requestListApi = this.el?.getAttribute('data-api-url');
this.visibilityApi = this.el?.getAttribute('data-visibility-api');
this.navBtn = this.el.querySelector('.add-creative-asset-btn');
this.navUrl = this.el?.getAttribute('data-nav-url');
this.searchInput = this.el.querySelector('.search-input');
this.assetsData = []; // Hold full data for sorting
this.sortConfig = { key: null, direction: 'asc' }; // Track sorting state
this.searchKey = '';
this.selectedUnit = '';
this.searchTimeout = null;
this.activeSwitchToggles = 0;
this.originalPaginationStates = {
prev: false,
next: false
}
// For Menu item active state
const menuFlag = 'creator';
setActiveMenu(menuFlag);
// Fetch request list
this.fetchRequestList();
this.initEventListners();
initSortedAndHighlightDropdowns();
}

initEventListners() {
const assetSizeDropdown = this.populateAssetSize();
// To handle sort and search events
this.el.addEventListener('click', (e) => {
if (e.target.matches('[data-sort]')) {
const key = e.target.getAttribute('data-sort');
if (this.filteredAssets) {
if (key === 'assetVisibility') {
const sortValue = e.target.getAttribute('data-sort-value');
const button = e.target.closest('.sort-btn-group').querySelectorAll('[data-sort="assetVisibility"]');
button.forEach(btn => btn.classList.remove('active'));
e.target.classList.add('active');
this.handleVisibilitySort(sortValue);
}
} else {
this.handleSort(key);
}
}
this.searchInput.addEventListener('keydown', (e) => {
if (e.type === 'keydown' && e.key === 'Enter') {
e.preventDefault();
this.showTableSpinner();
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => this.handleSearch(), 300);
}
});
if (e.target.matches('.dropdown-item')) {
e.preventDefault();
this.searchInput.value = '';
this.searchKey = this.mapSearchKey(e.target.dataset.value);
if (this.searchKey === 'assetCreatedDate') {
this.searchInput.setAttribute('type', 'date');
} else {
this.searchInput.setAttribute('type', 'text');
}
const selectedValue = e.target.innerText;
if (selectedValue === 'Size' || selectedValue === 'Inches' || selectedValue === 'mm') {
assetSizeDropdown?.classList.remove('d-none');
this.searchInput.placeholder = 'Enter size (WxH)';
} else {
assetSizeDropdown?.classList.add('d-none');
this.selectedUnit = '';
this.searchInput.placeholder = 'Search';
const assetSizeBtn = assetSizeDropdown.querySelector('.btn-dropdown');
assetSizeBtn.innerHTML = secureHtml(`Unit of Measurement <span class="arrow-down cmp-icon-pseudo"></span>`);
}

const dropdown = e.target.closest('.dropdown');
dropdown.querySelector('.btn-dropdown').innerHTML = secureHtml(`<span class="ellipsis">${e.target.innerText}</span> <span class="arrow-down cmp-icon-pseudo"></span>`);
}
});

this.el.querySelector('.search-icon').addEventListener('click', () => {
const value = this.searchInput.value;
if (/[^a-zA-Z0-9]/.test(value[0])) {
alert('Search term cannot be only special characters.');
return;
} else {
this.showTableSpinner();
clearTimeout(this.searchTimeout);
this.searchTimeout = setTimeout(() => this.handleSearch(), 300);
}
});

// Add Creative Asset Page Navigation
this.navBtn?.addEventListener('click', () => {
if (this.navUrl) {
if (window.location.href.includes("cognizant") || window.location.href.includes("localhost")) {
window.location.href = secureHtml(this.navUrl);
} else {
return;
}
}
});
}

// Populate Asset Size Dropdown
populateAssetSize() {
const assetSizeDropdown = document.createElement("div");
assetSizeDropdown.className = "dropdown asset-size d-none";
assetSizeDropdown.innerHTML = secureHtml(`
<button class="btn-dropdown asset-size-btn" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Unit of Measurement <span class="arrow-down cmp-icon-pseudo"></span>
</button>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#" data-value="mm">mm</a></li>
<li><a class="dropdown-item" href="#" data-value="inches">Inches</a></li>
</ul>
`);
document.querySelector('.searchbar-container').insertBefore(assetSizeDropdown, document.querySelector('.searchbar-container .search-col'));
assetSizeDropdown.querySelectorAll('.dropdown-item').forEach(item => {
item.addEventListener('click', (e) => {
e.preventDefault();
const selectedValue = item.getAttribute('data-value');
this.selectedUnit = selectedValue;
const button = assetSizeDropdown.querySelector('.btn-dropdown');
button.innerHTML = secureHtml(`${selectedValue} <span class="arrow-down cmp-icon-pseudo"></span>`);
});
});
return assetSizeDropdown;
}

// To show loader inside table
showTableSpinner() {
const tableBody = this.el.querySelector("#creator-history-table");
tableBody.innerHTML = secureHtml(`
<tr class="loading-spinner-row">
<td colspan="13" class="text-center py-3">
<div class="spinner-border text-primary" role="status">
<span class="visually-hidden">Loading...</span>
</div>
</td>
</tr>
`);
}

showTableDataError(message) {
const tableBody = this.el.querySelector("table tbody");
tableBody.innerHTML = secureHtml(`
<tr>
<td colspan="13" class="text-center py-4 error-message">
<p class="error-message"><b>Error</b></br>
${message}</p>
</td>
</tr>
`);
}

// To map and search the asset metadata to the search value
mapSearchKey(value) {
const map = {
"Creative Asset Name": "creativeAssetName",
"Creative Asset Size": "creativeAssetSize",
"Creative Asset Type": "creativeAssetType",
"Creative Asset Theme": "creativeAssetTheme",
"Creative Asset Style": "creativeAssetStyle",
"Creative Asset Category": "creativeAssetCategory",
"Creative Asset Sub-Category": "creativeAssetSubCategory",
"Creative Asset Orientation": "creativeAssetOrientation",
"Creative Asset Purpose": "creativeAssetPurpose",
"Creator Name": "creatorName",
"Creative Asset Added Date": "assetCreatedDate"
};
return map[value] || '';
}

mapSearchKeyExceptions(value) {
const map = {
"ready to use": "readytouse",
"readytouse": "readytouse",
"table drapes": "tabledrapes",
"tabledrapes": "tabledrapes",
};
return map[value.toLowerCase()] || value;
}

normaliseSearchText(value, removeSpaces = false) {
let normalised = value?.trim().toLowerCase();
return removeSpaces ? normalised?.replace(/s+/g, '') : normalised;
}
// To fetch th erequest list
async fetchRequestList() {
try {
this.showTableSpinner();
const response = await fetch(this.requestListApi);
let data = await response.json();
data = JSON.parse(secureHtml(JSON.stringify(data), { allowedTags: [] }));
this.assetsData = data.resultant;
this.assetsData.forEach((data) => {
data.creativeAssetSize = `${data.creativeAssetWidth} x ${data.creativeAssetHeight} ${data.creativeAssetUnit}`;
});
this.filteredAssets = [...this.assetsData];
this.paginationInit(this.assetsData);
} catch (error) {
this.showTableDataError('We encountered an unexpected error and were unable to call the API. Please Try Again.')
console.error('Error fetching filter options:', error);
}
}

// To handle the search values
handleSearch() {
let rawSearchValue = this.searchInput.value.trim().toLowerCase();

// Apply exceptions mapping first, before any other processing
if (this.searchKey === "creativeAssetType" || this.searchKey === "creativeAssetCategory") {
rawSearchValue = this.mapSearchKeyExceptions(rawSearchValue);
}
if (this.searchKey === 'assetCreatedDate') {
const originalDate = this.searchInput.value;
const [year, month, day] = originalDate.split('-');
rawSearchValue = `${day}/${month}/${year}`;
if (!originalDate) rawSearchValue = '';
}
if (!rawSearchValue) {
// If search is empty, reset to full list
this.filteredAssets = [...this.assetsData];
} else if (this.searchKey || this.selectedUnit) {
// Search by the selected dropdown key
const searchValue = this.normaliseSearchText(rawSearchValue, true); // Always remove spaces for specific field search
this.filteredAssets = this.assetsData.filter(item => {
let value = '';
const normalizedSize = this.normaliseSearchText(item?.creativeAssetSize, true);
if (this.selectedUnit && normalizedSize?.includes(this.selectedUnit)) {
value = item['creativeAssetSize']?.toString().toLowerCase();
} else {
value = item[this.searchKey]?.toString().toLowerCase();
}
const normalisedValue = this.normaliseSearchText(value, true); // Match without spaces
return normalisedValue?.includes(searchValue);
});
} else {
// If no search key selected, search across all fields
const normaliseSearchWithSpaces = this.normaliseSearchText(rawSearchValue, false);
const normaliseSearchWithNoSpaces = this.normaliseSearchText(rawSearchValue, true);
this.filteredAssets = this.assetsData.filter(item => {
return Object.values(item).some(val => {
const strVal = val?.toString().toLowerCase() || '';
const normalisedValWithSpaces = this.normaliseSearchText(strVal, false);
const normalisedValWithNoSpaces = this.normaliseSearchText(strVal, true);
return normalisedValWithSpaces.includes(normaliseSearchWithSpaces) ||
normalisedValWithSpaces.includes(normaliseSearchWithNoSpaces) ||
normalisedValWithNoSpaces.includes(normaliseSearchWithSpaces) ||
normalisedValWithNoSpaces.includes(normaliseSearchWithNoSpaces);
});
});
}
this.paginationInit(this.filteredAssets);
}

// To reset all sortIcons based on its class
resetAllSortIcons() {
const allSortIcons = this.el.querySelectorAll('.sort-icon');
allSortIcons.forEach(icon => {
icon.classList.remove('asc', 'desc');
})
}

// To handle sort logic
handleSort(key) {
this.resetAllSortIcons();
const sortButton = this.el.querySelector(`#sort-${key}`);
const sortIcon = sortButton.querySelector('.sort-icon');
if (this.sortConfig.key === key) {
this.sortConfig.direction = this.sortConfig.direction === 'asc' ? 'desc' : 'asc';
} else {
this.sortConfig = { key, direction: 'desc' };
}
sortIcon.classList.add(this.sortConfig.direction);
const direction = this.sortConfig.direction === 'asc' ? 1 : -1;
this.filteredAssets.sort((a, b) => {
let valA = a[key];
let valB = b[key];
if (key === 'assetCreatedDate') {
const parseDate = (dateStr) => {
const [day, month, year] = dateStr.split('/');
return new Date(`${year}-${month}-${day}`).getTime();
}
// Convert to timestamps for reliable comparison
const dateA = parseDate(valA);
const dateB = parseDate(valB);
// Handle cases where date parsing fails (invalid dates)
if (isNaN(dateA) || isNaN(dateB)) {
// Fallback to string comparison if dates are invalid
return valA?.toString().localeCompare(valB.toString()) * direction;
}
return (dateA - dateB) * direction;
}
// Handle other types of sorting
valA = a[key]?.toString().toLowerCase() || '';
valB = b[key]?.toString().toLowerCase() || '';
return valA.localeCompare(valB) * direction;
});
this.paginationInit(this.filteredAssets);
}

// TO handle sort for asset visibility
handleVisibilitySort(preference) {
this.filteredAssets.sort((a, b) => {
const valA = a.assetVisibility === 'true' || a.assetVisibility === true;
const valB = b.assetVisibility === 'true' || b.assetVisibility === true;
if (preference === 'hide') {
return (valA === valB ? 0 : valA ? -1 : 1);
} else {
return (valA === valB ? 0 : valA ? 1 : -1);
}
});

this.paginationInit(this.filteredAssets);
}

// To generate the list of assets in the table
generateList(listItems, currentPage, itemsPerPage) {
const dynamicFiltersContainer = this.el.querySelector("#creator-history-table");
const startIndex = (currentPage - 1) * itemsPerPage;
const paginatedAssets = listItems.slice(startIndex, startIndex + itemsPerPage);
if (listItems.length === 0) {
// Show "no results" message
dynamicFiltersContainer.innerHTML = secureHtml(`
<tr>
<td colspan="13" class="text-center py-4 error-message">
No assets match the search value
</td>
</tr>
`);
return;
}
let requestListdata = "";
paginatedAssets.forEach(lists => {
const assetId = `id-${Date.now()}-${Math.floor(Math.random() * 1000)}`;
const isHorizontalAsset = isHorizontal(lists.creativeAssetWidth, lists.creativeAssetHeight);
let imageConstrain;
if (isHorizontalAsset) {
imageConstrain = 'wid=1005&hei=816&fit=constrain';
} else {
imageConstrain = 'wid=634&hei=816&fit=constrain';
}
requestListdata += `<tr>
<td><a role="button" data-thumbnail="${sanitizeValue(lists.creativeAssetThumbnail)}" data-pdfurl=${sanitizeValue(lists.assetOpenApiUrl)} data-constrain=${sanitizeValue(imageConstrain)} data-bs-toggle="modal" data-bs-target="#assetPreviewModal">${sanitizeValue(lists.creativeAssetName)}</a></td>
<td>${sanitizeValue(lists.creativeAssetType) === 'customizable' ? 'Customizable' : 'Ready to use'}</td>
<td>${sanitizeValue(lists.creativeAssetCategory) === 'Tabledrapes' ? 'Table drapes' : sanitizeValue(lists.creativeAssetCategory)}</td>
<td>${sanitizeValue(lists.creativeAssetSubCategory) === 'Nameboards' ? 'Name boards' : sanitizeValue(lists.creativeAssetSubCategory)}</td>
<td>${sanitizeValue(lists.creativeAssetUnit) ? `${sanitizeValue(lists.creativeAssetWidth)} x ${sanitizeValue(lists.creativeAssetHeight)} ${sanitizeValue(lists.creativeAssetUnit)}` : ''}</td>
<td>${sanitizeValue(lists.creativeAssetTheme)}</td>
<td>${sanitizeValue(lists.creativeAssetStyle)}</td>
<td>${sanitizeValue(lists.creativeAssetOrientation)}</td>
<td>${sanitizeValue(lists.creativeAssetPurpose)}</td>
<td>${sanitizeValue(lists.creatorName)}</td>
<td>${sanitizeValue(lists.creativeAssetDescription)}</td>
<td>${sanitizeValue(lists.assetCreatedDate)}</td>
<td class="sticky-col">
<div class="custom-switch-wrapper form-check form-switch form-switch-lg">
<input type="checkbox" id="visibilitySwitch${sanitizeValue(assetId)}" class="form-check-input visibility-switch ${lists.assetVisibility === 'false' || lists.assetVisibility === '' ? 'border-0' : ''}" data-project-id="${lists.projectId}" data-template-path="${lists.creativeAssetPath}" role="switch" ${lists.assetVisibility === 'false' || lists.assetVisibility === '' ? 'checked' : ''}>
<label class="custom-switch-label ${lists.assetVisibility === 'false' || lists.assetVisibility === '' ? 'unhide-text' : ''}" for="visibilitySwitch${assetId}">
<span class="switch-text">${lists.assetVisibility === 'false' || lists.assetVisibility === '' ? 'Hide' : 'Unhide'}</span>
</label>
</div>
</td>
</tr>`;
});
dynamicFiltersContainer.innerHTML = secureHtml(requestListdata);
this.attachSwitchEvents();
// To preview asset thumbnail
this.previewPopup();
}

// To handle switch hide and unhide
attachSwitchEvents() {
const allSwitches = this.el.querySelectorAll('.visibility-switch');
allSwitches.forEach((input, i) => {
const label = input.nextElementSibling.querySelector('.switch-text');
input.addEventListener('change', async (e) => {
const paginationBtnPrev = this.el.querySelector('.pagination-buttons.previous button');
const paginationBtnNext = this.el.querySelector('.pagination-buttons.next button');
// Store original state only if this is the first toggle
if (this.activeSwitchToggles === 0) {
this.originalPaginationStates.prev = paginationBtnPrev?.disabled ?? false;
this.originalPaginationStates.next = paginationBtnNext?.disabled ?? false;

paginationBtnPrev && (paginationBtnPrev.disabled = true);
paginationBtnNext && (paginationBtnNext.disabled = true);
}

this.activeSwitchToggles++; // To track concurrent toggles
// label.classList.add('opacity-0');
const isChecked = input.checked;
const originalState = isChecked;
const switchInput = input;
const templatePath = input.dataset.templatePath || '';

// To show loader and disable input during posting
if (isChecked) {
label.parentElement.classList.add('loader-right');
} else {
label.parentElement.classList.add('loader-left');
}
label.innerHTML = secureHtml(`Loading <span class="dot-one"> .</span>
<span class="dot-two"> .</span>
<span class="dot-three"> .</span>`);
input.disabled = true;

// Payload to define the visibility
let payload = {
templatePath: templatePath,
visibility: isChecked ? "unhide" : "hide"
};
try {
const token = await fetchCSRFToken() || '';
const response = await fetch(`${this.visibilityApi}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'CSRF-Token': token
},
body: JSON.stringify(payload)
});
if (!response.ok) {
throw new Error('Failed to update visibility');
}
// setTimeout(() => {
// label.classList.remove('opacity-0');
// }, 300);
this.isChecked = !isChecked;
label.textContent = isChecked ? "Hide" : "Unhide";
label.parentElement.classList.toggle('unhide-text', isChecked);
input.classList.toggle('border-0', isChecked);
this.assetsData[i].assetVisibility = JSON.stringify(this.isChecked);
// const result = await response.json();
// console.log('Visibility status:' result);
} catch (error) {
console.error('Error updating asset visibility:', error);
// Revert switch if POST fails
// setTimeout(() => {
// label.classList.remove('opacity-0');
// }, 300);
switchInput.checked = !originalState;
label.textContent = switchInput.checked ? "Hide" : "Unhide";
this.showErrorToast(`<b>Error</b></br>
We encountered an unexpected error and were unable to call the API. Please Try Again.`);
} finally {
label.parentElement.classList.remove('loader-left', 'loader-right');
input.disabled = false;

this.activeSwitchToggles--;

if (this.activeSwitchToggles === 0) {
// Resetting the nav buttons after hide/unhide toggle
paginationBtnNext && (paginationBtnNext.disabled = this.originalPaginationStates.next);
paginationBtnPrev && (paginationBtnPrev.disabled = this.originalPaginationStates.prev);
}
}
});
});
}

showErrorToast(message) {
const toastElement = document.getElementById('errorToast');
const toastBody = document.getElementById('toast-body');
toastBody.innerHTML = secureHtml(message);
const toast = Toast.getOrCreateInstance(toastElement, {
autohide: true,
delay: 3000,
animation: true
});
toast.show();
}
// To set and render assets with pagination
paginationInit(assets) {
this.filterPagination = this.el.querySelector('#creator-history-pagination');
const itemsPerPage = 10;
const totalAssets = Math.ceil(assets.length / itemsPerPage);
// Hide pagination if no results
this.filterPagination.style.display = assets.length === 0 ? 'none' : 'block';
const onPageChange = (initialPage) => {
this.generateList(assets, initialPage, itemsPerPage);
updatePagination({
component: this.filterPagination,
totalPages: totalAssets,
currentPage: initialPage,
onPageChange
});
};
onPageChange(1);
}

// Preview Modal for asset thumbnail preview
previewPopup() {
const previewPopupContent = document.querySelector('#assetPreviewThumbnail');
document.querySelector('#creator-history-table').addEventListener('click', (event) => {
const modalDialog = previewPopupContent?.parentElement?.closest('.modal-dialog');
if (!modalDialog) return;

const target = event.target;
const isPdf = target.getAttribute('data-pdfurl')?.includes('.pdf');

modalDialog.classList.remove('modal-lg');
modalDialog.classList.add('top-0');

if (target.tagName === 'A' && !isPdf) {
const thumbnailPath = target.getAttribute("data-thumbnail");
const constrain = target.getAttribute("data-constrain");
if (thumbnailPath) {
const thumbnailImage = `<img src="${thumbnailPath}?${constrain}" alt="thumbnail preview" />`;
previewPopupContent.innerHTML = secureHtml(thumbnailImage);
}
} else if (isPdf) {
modalDialog.classList.add('modal-lg');
const pdfUrl = target.getAttribute("data-pdfurl");
if (pdfUrl) {
previewPopupContent.innerHTML = secureHtml(`<iframe src="${pdfUrl}" width="100%" height="400px" frameborder="0"></iframe>`);
}
}
});
}
}
     
 
what is notes.io
 

Notes is a web-based application for online taking notes. You can take your notes and share with others people. If you like taking long notes, notes.io is designed for you. To date, over 8,000,000,000+ notes created and continuing...

With notes.io;

  • * You can take a note from anywhere and any device with internet connection.
  • * You can share the notes in social platforms (YouTube, Facebook, Twitter, instagram etc.).
  • * You can quickly share your contents without website, blog and e-mail.
  • * You don't need to create any Account to share a note. As you wish you can use quick, easy and best shortened notes with sms, websites, e-mail, or messaging services (WhatsApp, iMessage, Telegram, Signal).
  • * Notes.io has fabulous infrastructure design for a short link and allows you to share the note as an easy and understandable link.

Fast: Notes.io is built for speed and performance. You can take a notes quickly and browse your archive.

Easy: Notes.io doesn’t require installation. Just write and share note!

Short: Notes.io’s url just 8 character. You’ll get shorten link of your note when you want to share. (Ex: notes.io/q )

Free: Notes.io works for 14 years and has been free since the day it was started.


You immediately create your first note and start sharing with the ones you wish. If you want to contact us, you can use the following communication channels;


Email: [email protected]

Twitter: http://twitter.com/notesio

Instagram: http://instagram.com/notes.io

Facebook: http://facebook.com/notesio



Regards;
Notes.io Team

     
 
Shortened Note Link
 
 
Looding Image
 
     
 
Long File
 
 

For written notes was greater than 18KB Unable to shorten.

To be smaller than 18KB, please organize your notes, or sign in.