diff --git a/src/ui/static/js/services.js b/src/ui/static/js/services.js index 53d92826c..205e033cd 100644 --- a/src/ui/static/js/services.js +++ b/src/ui/static/js/services.js @@ -8,6 +8,10 @@ import { SettingsAdvanced, } from "./utils/settings.js"; +import { + Filter +} from "./utils/dashboard.js"; + class SettingsService { constructor() { this.prefix = "services"; @@ -549,130 +553,8 @@ class Dropdown { } } -class Filter { - constructor(prefix = "services") { - this.prefix = prefix; - this.container = - document.querySelector(`[data-${this.prefix}-filter]`) || null; - this.keyInp = document.querySelector("input#service-name-keyword"); - this.stateValue = "all"; - this.methodValue = "all"; - this.initHandler(); - } - - initHandler() { - if (!this.container) return; - //STATE HANDLER - this.container.addEventListener("click", (e) => { - try { - if ( - e.target - .closest("button") - .getAttribute(`data-${this.prefix}-setting-select-dropdown-btn`) === - "state" - ) { - setTimeout(() => { - const value = document - .querySelector( - `[data-${this.prefix}-setting-select-text="state"]`, - ) - .textContent.trim() - .toLowerCase(); - - this.stateValue = value; - //run filter - this.filter(); - }, 10); - } - } catch (err) {} - }); - //METHOD HANDLER - this.container.addEventListener("click", (e) => { - try { - if ( - e.target - .closest("button") - .getAttribute(`data-${this.prefix}-setting-select-dropdown-btn`) === - "method" - ) { - setTimeout(() => { - const value = document - .querySelector( - `[data-${this.prefix}-setting-select-text="method"]`, - ) - .textContent.trim() - .toLowerCase(); - - this.methodValue = value; - //run filter - this.filter(); - }, 10); - } - } catch (err) {} - }); - //KEYWORD HANDLER - this.keyInp.addEventListener("input", (e) => { - this.filter(); - }); - } - - filter() { - const services = document.querySelectorAll(`[data-${this.prefix}-card]`); - if (services.length === 0) return; - //reset - for (let i = 0; i < services.length; i++) { - const el = services[i]; - el.classList.remove("hidden"); - } - //filter type - this.setFilterState(services); - this.setFilterMethod(services); - this.setFilterKeyword(services); - } - - setFilterState(services) { - if (this.stateValue === "all") return; - for (let i = 0; i < services.length; i++) { - const el = services[i]; - const type = el - .querySelector(`[data-${this.prefix}-state]`) - .getAttribute(`data-${this.prefix}-state`) - .trim() - .toLowerCase(); - if (type !== this.stateValue) el.classList.add("hidden"); - } - } - - setFilterMethod(services) { - if (this.methodValue === "all") return; - for (let i = 0; i < services.length; i++) { - const el = services[i]; - const type = el - .querySelector(`[data-${this.prefix}-method]`) - .getAttribute(`data-${this.prefix}-method`) - .trim() - .toLowerCase(); - if (type !== this.methodValue) el.classList.add("hidden"); - } - } - - setFilterKeyword(jobs) { - const keyword = this.keyInp.value.trim().toLowerCase(); - if (!keyword) return; - for (let i = 0; i < jobs.length; i++) { - const el = jobs[i]; - const name = el - .querySelector(`[data-${this.prefix}-name]`) - .textContent.trim() - .toLowerCase(); - - if (!name.includes(keyword)) el.classList.add("hidden"); - } - } -} const setDropdown = new Dropdown(); -const setFilter = new Filter(); const setTabsSelect = new TabsSelect( document.querySelector("[data-services-tabs-select]"), document.querySelector("[data-advanced][data-services-modal-form]"), @@ -713,22 +595,33 @@ const checkServiceModalSelect = new CheckNoMatchFilter( document.querySelector("[data-services-nomatch]"), ); -try { - const checkServiceCardKeyword = new CheckNoMatchFilter( - document.querySelectorAll("input#service-name-keyword"), - "input", - document.querySelectorAll("[data-services-card]"), - false, - document.querySelector("[data-services-nomatch-card]"), - ); - - const checkServiceCardSelect = new CheckNoMatchFilter( - document.querySelectorAll( - "button[data-services-setting-select-dropdown-btn]", - ), - "select", - document.querySelectorAll("[data-services-card]"), - false, - document.querySelector("[data-services-nomatch-card]"), - ); -} catch (e) {} +const filterContainer = document.querySelector(`[data-services-filter]`); +if(filterContainer) { + const noMatchEl = document.querySelector("[data-services-nomatch-card]"); + const filterEls = document.querySelectorAll(`[data-services-card]`); + const keywordFilter = { + "handler": document.querySelector("input#service-name-keyword"), + "handlerType" : "input", + "value" : document.querySelector("input#service-name-keyword").value, + "filterEls": filterEls, + "filterAtt" : "data-services-name", + "filterType" : "keyword", + }; + const methodFilter = { + "handler": document.querySelector("[data-services-setting-select-dropdown='method']"), + "handlerType" : "select", + "value" : document.querySelector("[data-services-setting-select-text='method']").textContent.trim().toLowerCase(), + "filterEls": filterEls, + "filterAtt" : "data-services-method", + "filterType" : "match", + }; + const stateFilter = { + "handler": document.querySelector("[data-services-setting-select-dropdown='state']"), + "handlerType" : "select", + "value" : document.querySelector("[data-services-setting-select-text='state']").textContent.trim().toLowerCase(), + "filterEls": filterEls, + "filterAtt" : "data-services-state", + "filterType" : "match", + }; + new Filter("services", [keywordFilter, methodFilter, stateFilter], null, noMatchEl); +} diff --git a/src/ui/static/js/utils/dashboard.js b/src/ui/static/js/utils/dashboard.js index dd5ef22f1..f63ca5cf6 100644 --- a/src/ui/static/js/utils/dashboard.js +++ b/src/ui/static/js/utils/dashboard.js @@ -9,7 +9,7 @@ // noMatchEl : if exists, will be displayed when no match is found // containerEl : container of the filter elements, case noMatchEl exists, it will be hidden when no match is found class Filter { -constructor(prefix, filters, containerEl, noMatchEl) { +constructor(prefix, filters, containerEl = null, noMatchEl = null) { this.prefix = prefix; this.filters = filters; this.containerEl = containerEl; @@ -38,11 +38,9 @@ init() { setSelectHandler(handler) { handler.addEventListener("click", (e) => { try { - const handlerName = handler.getAttribute(`data-${this.prefix}-setting-select-dropdown-btn`); - const value = document - .querySelector(`[data-${this.prefix}-setting-select-text="${handlerName}"]`) - .textContent.trim(); - this.updateValue(handlerName, value); + if(!e.target.closest("button").hasAttribute('data-services-setting-select-dropdown-btn')) return; + const value = e.target.closest("button").getAttribute('value'); + this.updateValue(handler, value); this.filter(); } catch(err) {} @@ -79,56 +77,53 @@ resetFilter() { }); if(this.noMatchEl) this.noMatchEl.classList.add("hidden"); - - this.containerEl.classList.remove("hidden"); + if(this.containerEl)this.containerEl.classList.remove("hidden"); } filter() { // Start by resetting the filter this.resetFilter(); // Then apply all filters - let isAtLeastOneMatch = false; + this.filters.forEach((filter) => { const [filterType, value, filterEls, filterAtt] = this.getFilterData(filter); // keyword filter means that el filter value must contains the keyword if(filterType === "keyword") { - isAtLeastOneMatch = this.filterKeyword(value, filterEls, filterAtt) ? true : isAtLeastOneMatch; + this.filterKeyword(value, filterEls, filterAtt); } // match filter means that el filter value must be equal to the filter value if(filterType === "match") { - isAtLeastOneMatch = this.filterMatch(value, filterEls, filterAtt) ? true : isAtLeastOneMatch; + this.filterMatch(value, filterEls, filterAtt); } // bool filter means that el filter value must be equal to bool value if(filterType === "bool") { - isAtLeastOneMatch = this.filterBool(value, filterEls, filterAtt) ? true : isAtLeastOneMatch; + this.filterBool(value, filterEls, filterAtt); } // lower than filter means that el filter value must be lower than the filter value if(filterType === "lowerThan") { - isAtLeastOneMatch = this.filterLowerThan(value, filterEls, filterAtt) ? true : isAtLeastOneMatch; + this.filterLowerThan(value, filterEls, filterAtt); } // higher than filter means that el filter value must be higher than the filter value if(filterType === "higherThan") { - isAtLeastOneMatch = this.filterHigherThan(value, filterEls, filterAtt) ? true : isAtLeastOneMatch; + this.filterHigherThan(value, filterEls, filterAtt); } }); - // If no match is found, hide the container and display the no match element - if(!isAtLeastOneMatch) { - this.containerEl.classList.add("hidden"); - if(this.noMatchEl) this.noMatchEl.classList.remove("hidden"); - } - + setTimeout(() => { + if(!this.isAtLeastOneMatch()) { + if(this.containerEl) this.containerEl.classList.add("hidden"); + if(this.noMatchEl) this.noMatchEl.classList.remove("hidden"); + } + }, 50); } filterKeyword(value, filterEls, filterAtt) { - let isAtLeastOneMatch = false; - const keyword = value.trim().toLowerCase(); - if (!keyword) return false; + if (!keyword) return; for (let i = 0; i < filterEls.length; i++) { const el = filterEls[i]; @@ -137,17 +132,13 @@ filterKeyword(value, filterEls, filterAtt) { el.classList.add("hidden"); continue; } - isAtLeastOneMatch = true; } - return isAtLeastOneMatch; + return; } filterMatch(value, filterEls, filterAtt) { - - if(!value || value === "all") return false; - - let isAtLeastOneMatch = false; + if(!value || value === "all") return; for (let i = 0; i < filterEls.length; i++) { const el = filterEls[i]; @@ -156,18 +147,15 @@ filterMatch(value, filterEls, filterAtt) { el.classList.add("hidden"); continue; } - isAtLeastOneMatch = true; } - return isAtLeastOneMatch; + return; } filterBool(value, filterEls, filterAtt) { // Check if value is undefined or null - if(value === undefined || value === null) return false; - - let isAtLeastOneMatch = false; + if(value === undefined || value === null) return; for (let i = 0; i < filterEls.length; i++) { const el = filterEls[i]; @@ -176,7 +164,6 @@ filterBool(value, filterEls, filterAtt) { const isValueTruthy = truthyValues.includes(elValue); if(value && isValueTruthy || !value && !isValueTruthy) { - isAtLeastOneMatch = true; continue; } @@ -184,14 +171,13 @@ filterBool(value, filterEls, filterAtt) { } - return isAtLeastOneMatch; + return ; } filterLowerThan(value, filterEls, filterAtt) { // Check if value is undefined or null - if(!value) return false; + if(!value) return; - let isAtLeastOneMatch = false; for (let i = 0; i < filterEls.length; i++) { const el = filterEls[i]; @@ -208,18 +194,14 @@ filterLowerThan(value, filterEls, filterAtt) { el.classList.add("hidden"); continue; } - - isAtLeastOneMatch = true; } - return isAtLeastOneMatch; + return; } filterHigherThan(value, filterEls, filterAtt) { // Check if value is undefined or null - if(!value) return false; - - let isAtLeastOneMatch = false; + if(!value) return; for (let i = 0; i < filterEls.length; i++) { const el = filterEls[i]; @@ -237,10 +219,27 @@ filterLowerThan(value, filterEls, filterAtt) { continue; } - isAtLeastOneMatch = true; } - return isAtLeastOneMatch; + return; + } + + isAtLeastOneMatch() { + // loop on each filterEls and check if at least one is not hidden + let isOneMatch = false; + for(let i = 0; i < this.filters.length; i++) { + const filter = this.filters[i]; + const filterEls = filter.filterEls; + filterEls.forEach((el) => { + if(!el.classList.contains("hidden")) { + isOneMatch = true; + return; + } + }); + if(isOneMatch) break; + } + + return isOneMatch; } updateValue(handler, value) { @@ -255,182 +254,20 @@ filterLowerThan(value, filterEls, filterAtt) { } getFilterElValue(el, filterAtt) { - return filterAtt === "textContent" ? el.textContent.trim().toLowerCase() : el.getAttribute(`data-${this.prefix}-${filterAtt}`).trim().toLowerCase(); + return filterAtt === "textContent" ? el.textContent.trim().toLowerCase() : el.getAttribute(filterAtt).trim().toLowerCase(); } } + class Dropdown { -constructor(prefix = "bans") { - this.prefix = prefix; - this.container = document.querySelector("main"); - this.lastDrop = ""; - this.initDropdown(); -} - -initDropdown() { - this.container.addEventListener("click", (e) => { - //SELECT BTN LOGIC - try { - if ( - e.target - .closest("button") - .hasAttribute(`data-${this.prefix}-setting-select`) && - !e.target.closest("button").hasAttribute(`disabled`) - ) { - const btnName = e.target - .closest("button") - .getAttribute(`data-${this.prefix}-setting-select`); - if (this.lastDrop !== btnName) { - this.lastDrop = btnName; - this.closeAllDrop(); - } - - this.toggleSelectBtn(e); - } - } catch (err) {} - //SELECT DROPDOWN BTN LOGIC - try { - if ( - e.target - .closest("button") - .hasAttribute(`data-${this.prefix}-setting-select-dropdown-btn`) - ) { - const btn = e.target.closest("button"); - const btnValue = btn.getAttribute("value"); - const btnSetting = btn.getAttribute( - `data-${this.prefix}-setting-select-dropdown-btn`, - ); - //stop if same value to avoid new fetching - const isSameVal = this.isSameValue(btnSetting, btnValue); - if (isSameVal) return this.hideDropdown(btnSetting); - //else, add new value to custom - this.setSelectNewValue(btnSetting, btnValue); - //close dropdown and change style - this.hideDropdown(btnSetting); - - if ( - !e.target.closest("button").hasAttribute(`data-${this.prefix}-file`) - ) { - this.changeDropBtnStyle(btnSetting, btn); - } - //show / hide filter - if (btnSetting === "instances") { - this.hideFilterOnLocal(btn.getAttribute("data-_type")); - } - } - } catch (err) {} - }); -} - -closeAllDrop() { - const drops = document.querySelectorAll( - `[data-${this.prefix}-setting-select-dropdown]`, - ); - drops.forEach((drop) => { - drop.classList.add("hidden"); - drop.classList.remove("flex"); - document - .querySelector( - `svg[data-${this.prefix}-setting-select="${drop.getAttribute( - `data-${this.prefix}-setting-select-dropdown`, - )}"]`, - ) - .classList.remove("rotate-180"); - }); -} - -isSameValue(btnSetting, value) { - const selectCustom = document.querySelector( - `[data-${this.prefix}-setting-select-text="${btnSetting}"]`, - ); - const currVal = selectCustom.textContent; - return currVal === value ? true : false; -} - -setSelectNewValue(btnSetting, value) { - const selectCustom = document.querySelector( - `[data-${this.prefix}-setting-select="${btnSetting}"]`, - ); - selectCustom.querySelector( - `[data-${this.prefix}-setting-select-text]`, - ).textContent = value; -} - -hideDropdown(btnSetting) { - //hide dropdown - const dropdownEl = document.querySelector( - `[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`, - ); - dropdownEl.classList.add("hidden"); - dropdownEl.classList.remove("flex"); - //svg effect - const dropdownChevron = document.querySelector( - `svg[data-${this.prefix}-setting-select="${btnSetting}"]`, - ); - dropdownChevron.classList.remove("rotate-180"); -} - -changeDropBtnStyle(btnSetting, selectedBtn) { - const dropdownEl = document.querySelector( - `[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`, - ); - //reset dropdown btns - const btnEls = dropdownEl.querySelectorAll("button"); - - btnEls.forEach((btn) => { - btn.classList.remove( - "bg-primary", - "dark:bg-primary", - "text-gray-300", - "text-gray-300", - ); - btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700"); - }); - //highlight clicked btn - selectedBtn.classList.remove( - "bg-white", - "dark:bg-slate-700", - "text-gray-700", - ); - selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300"); -} - -toggleSelectBtn(e) { - const attribute = e.target - .closest("button") - .getAttribute(`data-${this.prefix}-setting-select`); - //toggle dropdown - const dropdownEl = document.querySelector( - `[data-${this.prefix}-setting-select-dropdown="${attribute}"]`, - ); - const dropdownChevron = document.querySelector( - `svg[data-${this.prefix}-setting-select="${attribute}"]`, - ); - dropdownEl.classList.toggle("hidden"); - dropdownEl.classList.toggle("flex"); - dropdownChevron.classList.toggle("rotate-180"); -} - -//hide date filter on local -hideFilterOnLocal(type) { - if (type === "local") { - this.hideInp(`input#from-date`); - this.hideInp(`input#to-date`); + constructor() + { + this.init(); } - if (type !== "local") { - this.showInp(`input#from-date`); - this.showInp(`input#to-date`); + init(){ + } } -showInp(selector) { - document.querySelector(selector).closest("div").classList.add("flex"); - document.querySelector(selector).closest("div").classList.remove("hidden"); -} - -hideInp(selector) { - document.querySelector(selector).closest("div").classList.add("hidden"); - document.querySelector(selector).closest("div").classList.remove("flex"); -} -} \ No newline at end of file +export { Filter, Dropdown }; \ No newline at end of file diff --git a/src/ui/templates/services.html b/src/ui/templates/services.html index 9f4ce106b..4ec9a3f71 100644 --- a/src/ui/templates/services.html +++ b/src/ui/templates/services.html @@ -106,7 +106,10 @@ {% for service in services %} {% set id_server_name = service["SERVER_NAME"]['value'].replace(".", "-") %}