From 6859607fa6ee1593b3760c81e121cc8f8b034d32 Mon Sep 17 00:00:00 2001 From: Ajeyakrishna Date: Mon, 31 Jul 2023 20:47:58 +0530 Subject: [PATCH] Revert "Revert "feat : add search bar and filter for extension requests page"" This reverts commit be52e65c77146d61d4487cbf55e47b8090534b75. --- extension-requests/constants.js | 5 + extension-requests/index.html | 41 ++++++-- extension-requests/local-utils.js | 17 +++- extension-requests/script.js | 140 ++++++++++++++++++++++++-- extension-requests/style.css | 162 ++++++++++++++++++++++++++++++ users/images/funnel.svg | 4 +- utils.js | 13 +++ 7 files changed, 364 insertions(+), 18 deletions(-) diff --git a/extension-requests/constants.js b/extension-requests/constants.js index 95f314f1..4b8ff1a0 100644 --- a/extension-requests/constants.js +++ b/extension-requests/constants.js @@ -17,3 +17,8 @@ const extensionRequestCardHeadings = [ { title: 'Created At', key: 'timestamp', time: true }, { title: 'Task', key: 'taskId' }, ]; +const FILTER_MODAL = 'filter-modal'; +const FILTER_BUTTON = 'filter-button'; +const APPLY_FILTER_BUTTON = 'apply-filter-button'; +const CLEAR_BUTTON = 'clear-button'; +const USER_SEARCH_ELEMENT = 'assignee-search'; diff --git a/extension-requests/index.html b/extension-requests/index.html index 9675032c..73642087 100644 --- a/extension-requests/index.html +++ b/extension-requests/index.html @@ -3,7 +3,12 @@ - + + + + + + Extension Requests @@ -97,14 +102,38 @@

Update Extension Request Status

Extension Requests

+ +
+ + + +
+

- - - - - diff --git a/extension-requests/local-utils.js b/extension-requests/local-utils.js index 513555ad..863bc31f 100644 --- a/extension-requests/local-utils.js +++ b/extension-requests/local-utils.js @@ -1,10 +1,21 @@ +const Status = { + APPROVED: 'APPROVED', + PENDING: 'PENDING', + DENIED: 'DENIED', +}; async function getExtensionRequests(query = {}) { const url = new URL(`${API_BASE_URL}/extension-requests`); queryParams = ['assignee', 'status', 'taskId']; - queryParams.forEach( - (key) => query[key] && url.searchParams.set(key, query[key]), - ); + queryParams.forEach((key) => { + if (query[key]) { + if (Array.isArray(query[key])) { + query[key].forEach((value) => url.searchParams.append(key, value)); + } else { + url.searchParams.append(key, query[key]); + } + } + }); const res = await fetch(url, { credentials: 'include', diff --git a/extension-requests/script.js b/extension-requests/script.js index e7b68616..9b8d1f7e 100644 --- a/extension-requests/script.js +++ b/extension-requests/script.js @@ -14,29 +14,149 @@ const modalStatusForm = document.querySelector( ); const modalUpdateForm = document.querySelector('.extension-requests-form'); +const filterModal = document.getElementsByClassName(FILTER_MODAL)[0]; +const filterButton = document.getElementById(FILTER_BUTTON); +const applyFilterButton = document.getElementById(APPLY_FILTER_BUTTON); +const clearButton = document.getElementById(CLEAR_BUTTON); +const userSearchElement = document.getElementById(USER_SEARCH_ELEMENT); +let allCardsList; +let isFiltered = false; + const state = { currentExtensionRequest: null, }; const render = async () => { + toggleStatusCheckbox(Status.PENDING); + await populateExtensionRequests({ status: Status.PENDING }); +}; + +async function populateExtensionRequests(query = {}) { try { addLoader(container); - const extensionRequests = await getExtensionRequests(); - + extensionRequestsContainer.innerHTML = ''; + const extensionRequests = await getExtensionRequests(query); const allExtensionRequests = extensionRequests.allExtensionRequests; + + allCardsList = []; allExtensionRequests.forEach((data) => { - extensionRequestsContainer.appendChild( - createExtensionRequestCard(data, extensionRequestCardHeadings), + const extensionRequestCard = createExtensionRequestCard( + data, + extensionRequestCardHeadings, ); + data['htmlElement'] = extensionRequestCard; + allCardsList.push(data); + extensionRequestsContainer.appendChild(extensionRequestCard); }); } catch (error) { - errorHeading.textContent = 'Something went wrong'; + errorHeading.textContent = 'Something went wrong, Please reload'; errorHeading.classList.add('error-visible'); - reload(); + // reload(); } finally { removeLoader('loader'); } +} +function addCheckbox(labelText, value, groupName) { + const group = document.getElementById(groupName); + const label = document.createElement('label'); + const checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.name = groupName; + checkbox.value = value; + label.innerHTML = checkbox.outerHTML + ' ' + labelText; + label.classList.add('checkbox-label'); + label.appendChild(document.createElement('br')); + group.appendChild(label); +} + +function populateStatus() { + const statusList = [ + { name: 'Approved', id: 'APPROVED' }, + { name: 'Pending', id: 'PENDING' }, + { name: 'Denied', id: 'DENIED' }, + ]; + for (let i = 0; i < statusList.length; i++) { + const { name, id } = statusList[i]; + addCheckbox(name, id, 'status-filter'); + } +} +function toggleStatusCheckbox(statusValue) { + const element = document.querySelector( + `#status-filter input[value=${statusValue}]`, + ); + element.checked = !element.checked; +} + +function clearCheckboxes(groupName) { + const checkboxes = document.querySelectorAll(`input[name="${groupName}"]`); + checkboxes.forEach((cb) => { + cb.checked = false; + }); +} + +function getCheckedValues(groupName) { + const checkboxes = document.querySelectorAll( + `input[name="${groupName}"]:checked`, + ); + return Array.from(checkboxes).map((cb) => cb.value); +} + +applyFilterButton.addEventListener('click', async () => { + filterModal.classList.toggle('hidden'); + const checkedValuesStatus = getCheckedValues('status-filter'); + + await populateExtensionRequests({ status: checkedValuesStatus }); +}); + +clearButton.addEventListener('click', async function () { + clearCheckboxes('status-filter'); + filterModal.classList.toggle('hidden'); + + await populateExtensionRequests(); +}); +filterModal.addEventListener('click', (event) => { + event.stopPropagation(); +}); + +window.onclick = function () { + filterModal.classList.add('hidden'); }; + +const renderFilteredCards = (predicate) => { + extensionRequestsContainer.innerHTML = ''; + let isEmpty = true; + for (const card of allCardsList) { + if (predicate(card)) { + isEmpty = false; + extensionRequestsContainer.append(card.htmlElement); + } + } + + if (isEmpty) { + errorHeading.textContent = 'No Extension Requests found!'; + errorHeading.classList.add('error-visible'); + } else { + errorHeading.innerHTML = ''; + errorHeading.classList.remove('error-visible'); + } + // extensionRequestsContainer. +}; +userSearchElement.addEventListener( + 'input', + debounce((event) => { + if (!event.target.value && isFiltered) { + isFiltered = false; + renderFilteredCards((c) => true); + return; + } else if (!event.target.value) { + return; + } + + renderFilteredCards((card) => card.assignee.includes(event.target.value)); + isFiltered = true; + }, 500), +); + const showTaskDetails = async (taskId, approved) => { if (!taskId) return; try { @@ -114,7 +234,6 @@ function createExtensionRequestCard(data, dataHeadings) { main.appendChild(wrapperDiv); return main; } -render(); //PATCH requests functions async function onStatusFormSubmit(e) { @@ -207,3 +326,10 @@ function fillUpdateForm() { modalUpdateForm.querySelector('.extensionAssignee').value = assignee; modalUpdateForm.querySelector('.extensionReason').value = reason; } +filterButton.addEventListener('click', (event) => { + event.stopPropagation(); + filterModal.classList.toggle('hidden'); +}); + +populateStatus(); +render(); diff --git a/extension-requests/style.css b/extension-requests/style.css index f8237145..a3b2a0a6 100644 --- a/extension-requests/style.css +++ b/extension-requests/style.css @@ -1,4 +1,6 @@ :root { + --blue-color: #1d1283; + --blue-hover-color: #11085c; --dark-blue: #1b1378; --light-aqua: #d4f9f2; --scandal: #e5fcf5; @@ -7,9 +9,12 @@ --black: #181717; --light-gray: #d9d9d9; --razzmatazz: #df0057; + --red-color: red; --gray: #808080; --button-proceed: #008000; --modal-color: #00000048; + --black-color: black; + --light-gray-color: lightgray; } *, @@ -254,6 +259,141 @@ body { .error-visible { display: block; + align-self: center; +} + +.filter-button { + background-color: var(--blue-color); + color: var(--white); + border: none; + border-radius: 0.4rem; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 9rem; + height: 100%; + padding: 0.7rem; +} + +.funnel-icon { + width: 1.2rem; + height: 1.5rem; + margin-left: 0.5rem; +} + +.filter-button:hover { + background-color: var(--blue-hover-color); +} + +.search-filter { + display: flex; + justify-content: end; + align-items: center; + width: 100%; + padding: 2.5rem; +} + +#assignee-search { + width: 90%; + max-width: 15rem; + min-width: 10rem; + padding: 0.7rem 0.7rem; + border-radius: 0.4rem; + border: 2.5px solid var(--black-color); + font-size: medium; + background-color: var(--light-gray-color); + margin: 0 10px; +} + +/* Filter modal */ +.filter-modal { + width: 20%; + min-width: 15rem; + border: 1px solid var(--light-gray-color); + box-shadow: 0 0 10px rgba(0, 0, 0, 0.7); + border-radius: 0.31rem; + display: flex; + flex-direction: column; + align-items: center; + padding: 10px; + z-index: 1; + position: absolute; + top: 10.25rem; + right: 1rem; + background-color: var(--white); +} + +.filter-head { + display: flex; + justify-content: space-between; + width: 100%; + align-items: center; +} + +.filters-container { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} + +.modal-form { + text-align: initial; + padding: 0.5rem; + width: 80%; +} + +.checkbox-label { + display: block; + margin-bottom: 0.5rem; +} + +.clear-btn { + background-color: var(--white); + border: 1px solid var(--light-gray-color); + border-radius: 0.31rem; + padding: 0.31rem 0.62rem; + cursor: pointer; +} + +.clear-btn:hover { + background-color: var(--red-color); + color: var(--white); +} + +.filters { + width: 100%; + padding: 0.62rem; + border: 1px solid var(--light-gray-color); + border-radius: 0.31rem; + margin: 0.31rem 0.62rem; + cursor: pointer; +} + +.filters:hover { + background-color: var(--light-gray-color); + border: 1px solid var(--black-color); +} + +.apply-filter-button { + border: 1px solid var(--light-gray-color); + border-radius: 0.31rem; + padding: 0.62rem; + cursor: pointer; + width: 100%; + background-color: var(--blue-color); + color: var(--white); +} + +.apply-filter-button:hover { + background-color: var(--blue-hover-color); +} + +/* Filter modal end */ + +.hidden { + display: none; } @media screen and (max-width: 800px) { @@ -275,4 +415,26 @@ body { left: 10%; right: 10%; } + .search-filter { + justify-content: center; + } +} + +@media screen and (max-width: 440px) { + .filter-button { + width: min-content; + } + + .filter-text { + display: none; + } + .funnel-icon { + margin: auto; + } +} + +@media screen and (max-width: 320px) { + .filter-modal { + top: 12.5rem; + } } diff --git a/users/images/funnel.svg b/users/images/funnel.svg index de8aebf7..4af43b94 100644 --- a/users/images/funnel.svg +++ b/users/images/funnel.svg @@ -1,3 +1,3 @@ - - + + diff --git a/utils.js b/utils.js index b7bf4176..d86779c4 100644 --- a/utils.js +++ b/utils.js @@ -46,3 +46,16 @@ function addLoader(container) { function removeLoader(classname) { document.querySelector(`.${classname}`).remove(); } + +function debounce(func, delay) { + let timerId; + return (...args) => { + if (timerId) { + clearTimeout(timerId); + } + + timerId = setTimeout(() => { + func(...args); + }, delay); + }; +}