mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
enhance services page + udpate script and tests
* add timeout to execute nomatch showing in order to execute it after the filter logic * add services card filter : now can filter with name, method or draft/online state * display no matching filter on services card and service settings too * card services filtering only showing if at least 4 services *add new services filter to testing *update test utils to try fixing issue
This commit is contained in:
parent
d1f2455f96
commit
af667ada49
7 changed files with 634 additions and 37 deletions
File diff suppressed because one or more lines are too long
|
|
@ -38,30 +38,30 @@ const setFilterGlobal = new FilterSettings(
|
|||
document
|
||||
.querySelector("input#settings-filter")
|
||||
.addEventListener("input", () => {
|
||||
console.log("input");
|
||||
const tabs = document
|
||||
.querySelector("[data-global-config-tabs-desktop]")
|
||||
.querySelectorAll("[data-tab-handler]");
|
||||
let isAllHidden = true;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const plugin = tabs[i];
|
||||
if (!plugin.classList.contains("hidden")) {
|
||||
console.log(plugin);
|
||||
isAllHidden = false;
|
||||
break;
|
||||
setTimeout(() => {
|
||||
const tabs = document
|
||||
.querySelector("[data-global-config-tabs-desktop]")
|
||||
.querySelectorAll("[data-tab-handler]");
|
||||
let isAllHidden = true;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const plugin = tabs[i];
|
||||
if (!plugin.classList.contains("hidden")) {
|
||||
isAllHidden = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const formEl = document.querySelector("[data-global-config-form]");
|
||||
const noMatchEl = document.querySelector("[data-global-config-nomatch]");
|
||||
const formEl = document.querySelector("[data-global-config-form]");
|
||||
const noMatchEl = document.querySelector("[data-global-config-nomatch]");
|
||||
|
||||
if (isAllHidden) {
|
||||
noMatchEl.classList.remove("hidden");
|
||||
formEl.classList.add("hidden");
|
||||
}
|
||||
if (isAllHidden) {
|
||||
noMatchEl.classList.remove("hidden");
|
||||
formEl.classList.add("hidden");
|
||||
}
|
||||
|
||||
if (!isAllHidden) {
|
||||
formEl.classList.remove("hidden");
|
||||
noMatchEl.classList.add("hidden");
|
||||
}
|
||||
if (!isAllHidden) {
|
||||
formEl.classList.remove("hidden");
|
||||
noMatchEl.classList.add("hidden");
|
||||
}
|
||||
}, 20);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1044,6 +1044,308 @@ class Multiple {
|
|||
}
|
||||
}
|
||||
|
||||
class Dropdown {
|
||||
constructor(prefix = "services") {
|
||||
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`);
|
||||
}
|
||||
|
||||
if (type !== "local") {
|
||||
this.showInp(`input#from-date`);
|
||||
this.showInp(`input#to-date`);
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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];
|
||||
console.log(el);
|
||||
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 setPopover = new Popover();
|
||||
const setTabs = new Tabs();
|
||||
const setModal = new ServiceModal();
|
||||
|
|
@ -1054,3 +1356,69 @@ const setFilterGlobal = new FilterSettings(
|
|||
);
|
||||
|
||||
const setMultiple = new Multiple("services");
|
||||
|
||||
// Hide / Show no matching message on service modal
|
||||
document
|
||||
.querySelector("input#settings-filter")
|
||||
.addEventListener("input", () => {
|
||||
setTimeout(() => {
|
||||
const tabs = document
|
||||
.querySelector("[data-services-tabs-desktop]")
|
||||
.querySelectorAll("[data-tab-handler]");
|
||||
let isAllHidden = true;
|
||||
for (let i = 0; i < tabs.length; i++) {
|
||||
const plugin = tabs[i];
|
||||
if (!plugin.classList.contains("hidden")) {
|
||||
isAllHidden = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const formEl = document.querySelector("[data-services-modal-form]");
|
||||
const noMatchEl = document.querySelector("[data-services-nomatch]");
|
||||
|
||||
if (isAllHidden) {
|
||||
noMatchEl.classList.remove("hidden");
|
||||
formEl.classList.add("hidden");
|
||||
}
|
||||
|
||||
if (!isAllHidden) {
|
||||
formEl.classList.remove("hidden");
|
||||
noMatchEl.classList.add("hidden");
|
||||
}
|
||||
}, 20);
|
||||
});
|
||||
|
||||
// Hide / Show no matching message for services card
|
||||
try {
|
||||
document
|
||||
.querySelector("input#service-name-keyword")
|
||||
.addEventListener("input", () => {
|
||||
setTimeout(() => {
|
||||
const cards = document.querySelectorAll("[data-services-card]");
|
||||
let isAllHidden = true;
|
||||
for (let i = 0; i < cards.length; i++) {
|
||||
const card = cards[i];
|
||||
if (!card.classList.contains("hidden")) {
|
||||
isAllHidden = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
const formEl = document.querySelector("[data-services-modal-form]");
|
||||
const noMatchEl = document.querySelector(
|
||||
"[data-services-nomatch-card]",
|
||||
);
|
||||
|
||||
if (isAllHidden) {
|
||||
noMatchEl.classList.remove("hidden");
|
||||
formEl.classList.add("hidden");
|
||||
}
|
||||
|
||||
if (!isAllHidden) {
|
||||
formEl.classList.remove("hidden");
|
||||
noMatchEl.classList.add("hidden");
|
||||
}
|
||||
}, 20);
|
||||
});
|
||||
} catch (e) {}
|
||||
|
|
|
|||
165
src/ui/templates/services.html
vendored
165
src/ui/templates/services.html
vendored
|
|
@ -1,5 +1,25 @@
|
|||
{% extends "base.html" %}
|
||||
{% block content %}
|
||||
{% set methods = ["all"] %}
|
||||
{% set states = ["all", "draft", "online"] %}
|
||||
{% set draft_count = [] %}
|
||||
{% set online_count = [] %}
|
||||
{% for services_batched in services|batch(3) %}
|
||||
{% for service in
|
||||
services_batched %}
|
||||
{% if service['SERVER_NAME']['method'] not in methods %}
|
||||
{% if methods.append(service['SERVER_NAME']['method']) %}{% endif %}
|
||||
{% endif %}
|
||||
{% if service.get('IS_DRAFT', '') == "yes" %}
|
||||
{% if draft_count.append(1) %}{% endif %}
|
||||
{% endif %}
|
||||
{% if service.get('IS_DRAFT', '') == "no" %}
|
||||
{% if online_count.append(1) %}{% endif %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% set draft_count = draft_count|length %}
|
||||
{% set online_count = online_count|length %}
|
||||
<!-- actions -->
|
||||
<div data-services-service
|
||||
class="col-span-12 relative flex justify-center min-w-0 break-words rounded-2xl bg-clip-border">
|
||||
|
|
@ -22,19 +42,150 @@
|
|||
</div>
|
||||
<!-- end actions -->
|
||||
<!-- services container-->
|
||||
<div class="p-0 my-4 sm:mx-4 md:px-4 grid grid-cols-12 col-span-12 lg:gap-x-4 relative min-w-0 break-words rounded-2xl bg-clip-border">
|
||||
<div class="p-0 my-4 sm:mx-2 md:mx-4 md:px-4 grid grid-cols-12 col-span-12 md:gap-x-4 gap-y-4 relative min-w-0 break-words rounded-2xl bg-clip-border">
|
||||
{% if services|length == 0 %}
|
||||
<div class="col-span-12 sm:col-span-4 sm:col-start-5">
|
||||
<div class="transition duration-300 ease-in-out dark:opacity-90 text-center relative w-full p-4 text-white bg-blue-500 rounded-lg">
|
||||
No service to show
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
{% endif %}
|
||||
|
||||
{% if services|length >= 4 %}
|
||||
<!-- info-->
|
||||
{% set services_info = [
|
||||
{"name" : "SERVICES TOTAL", "data" : services|length|string},
|
||||
{"name" : "TOTAL DRAFT", "data" : draft_count|string},
|
||||
{"name" : "TOTAL ONLINE", "data" : online_count|string},
|
||||
] %}
|
||||
<div class="h-fit col-span-12 md:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
|
||||
{% for info in services_info %}
|
||||
<div class="mx-1 flex items-center my-4">
|
||||
<p class="transition duration-300 ease-in-out font-bold mb-0 font-sans text-sm leading-normal uppercase dark:text-gray-500 dark:opacity-80">
|
||||
{{ info['name'] }}
|
||||
</p>
|
||||
<p class="transition duration-300 ease-in-out pl-2 col-span-1 mb-0 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-80">
|
||||
{{ info['data'] }}
|
||||
</p>
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- end info -->
|
||||
<!-- filter -->
|
||||
{% set filters = [
|
||||
{
|
||||
"type": "input",
|
||||
"name": "Search",
|
||||
"label": "search",
|
||||
"id": "service-name-keyword",
|
||||
"placeholder": "service name",
|
||||
"pattern": "(.*?)"
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"name": "Method",
|
||||
"id": "method",
|
||||
"value": "all",
|
||||
"values": methods
|
||||
},
|
||||
{
|
||||
"type": "select",
|
||||
"name": "State",
|
||||
"id": "state",
|
||||
"value": "all",
|
||||
"values": states
|
||||
}
|
||||
] %}
|
||||
<div data-services-filter
|
||||
class="h-fit col-span-12 md:col-span-8 2xl:col-span-6 3xl:col-span-5 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
|
||||
<h5 class="mb-2 font-bold dark:text-white/90">FILTER</h5>
|
||||
<div class="mx-2 grid grid-cols-12 gap-4">
|
||||
{% for filter in filters %}
|
||||
{% if filter['type'] == 'input' %}
|
||||
<!-- search inpt-->
|
||||
<div class="flex flex-col relative col-span-12 md:col-span-6">
|
||||
<h5 class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300">
|
||||
{{ filter['name'] }}
|
||||
</h5>
|
||||
<label for="{{ filter['id'] }}" class="sr-only">{{ filter['label'] }}</label>
|
||||
<input type="text"
|
||||
id="{{ filter['id'] }}"
|
||||
name="{{ filter['id'] }}"
|
||||
class="dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
|
||||
placeholder="{{ filter['placeholder'] }}"
|
||||
pattern="{{ filter['pattern'] }}"
|
||||
required />
|
||||
</div>
|
||||
<!-- end search inpt-->
|
||||
{% endif %}
|
||||
{% if filter['type'] == 'select' %}
|
||||
<!-- select -->
|
||||
<div class="flex flex-col relative col-span-12 md:col-span-6">
|
||||
<h5 class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300">
|
||||
{{ filter['name'] }}
|
||||
</h5>
|
||||
<button aria-controls="filter-{{ filter['id'] }}"
|
||||
data-services-setting-select="{{ filter['id'] }}"
|
||||
class="disabled:opacity-75 dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400 dark:disabled:bg-gray-800 dark:disabled:border-gray-800 duration-300 ease-in-out dark:opacity-90 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 focus:border-green-500 flex justify-between align-middle items-center text-left text-sm leading-5.6 ease w-full rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-1.5 py-1 md:px-3 font-normal text-gray-700 transition-all placeholder:text-gray-500">
|
||||
<span aria-description="current filter state value"
|
||||
id="services-{{ filter['id'] }}"
|
||||
data-name="services-{{ filter['id'] }}"
|
||||
data-services-setting-select-text="{{ filter['id'] }}">all</span>
|
||||
<!-- chevron -->
|
||||
<svg data-services-setting-select="{{ filter['id'] }}"
|
||||
class="transition-transform h-4 w-4 fill-gray-500"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 512 512">
|
||||
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z" />
|
||||
</svg>
|
||||
</button>
|
||||
<!-- end chevron -->
|
||||
<!-- dropdown-->
|
||||
<div id="filter-{{ filter['id'] }}"
|
||||
role="listbox"
|
||||
data-services-setting-select-dropdown="{{ filter['id'] }}"
|
||||
class="hidden z-100 absolute h-full flex-col w-full translate-y-16">
|
||||
{% for value in filter['values'] %}
|
||||
<button role="option"
|
||||
data-services-setting-select-dropdown-btn="{{ filter['id'] }}"
|
||||
value="{{ value }}"
|
||||
class="{% if loop.first %}dark:bg-primary bg-primary text-gray-300 border-t rounded-t {% else %} bg-white {% endif %} {% if loop.last %}rounded-b{% endif %} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
|
||||
{{ value }}
|
||||
</button>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end select -->
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-services-nomatch-card
|
||||
class="hidden w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words">
|
||||
<div class="col-span-12 flex flex-col justify-center items-center h-fit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="mb-2 w-8 h-8 stroke-white">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
|
||||
</svg>
|
||||
<h5 class="font-bold dark:text-white/90 mx-2 text-white">No settings match</h5>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if services|length > 0 %}
|
||||
<!-- end filter -->
|
||||
{% for services_batched in services|batch(3) %}
|
||||
{% for service in
|
||||
services_batched %}
|
||||
{% set id_server_name = service["SERVER_NAME"]['value'].replace(".", "-") %}
|
||||
<div data-services-service="{{ service['SERVER_NAME']['value'] }}"
|
||||
<div data-services-card data-services-service="{{ service['SERVER_NAME']['value'] }}"
|
||||
class="my-2 dark:brightness-110 overflow-hidden hover:scale-102 transition col-span-12 lg:col-span-6 3xl:col-span-4 p-4 w-full shadow-md break-words bg-white dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
|
||||
<div data-services-settings
|
||||
class="hidden"
|
||||
|
|
@ -49,12 +200,12 @@
|
|||
class="hidden"
|
||||
data-value="{{ service['SERVER_NAME']['method'] }}"></div>
|
||||
<div class="flex justify-between items-start">
|
||||
<h5 class="transition duration-300 ease-in-out text-center sm:text-left mb-1 font-bold dark:text-white/90">
|
||||
<h5 data-services-name="{{ service["SERVER_NAME"]['value'] }}" class="transition duration-300 ease-in-out text-center sm:text-left mb-1 font-bold dark:text-white/90">
|
||||
{{ service["SERVER_NAME"]['value'] }}
|
||||
</h5>
|
||||
{% if service.get('IS_DRAFT', "no") == "yes" and service["SERVER_NAME"]['method'] in ["ui", "default"] %}
|
||||
<button class="group relative">
|
||||
<p class="dark:text-gray-300 -z-10 opacity-0 group-hover:z-10 group-hover:opacity-100 transition fixed bg-white dark:bg-slate-800 rounded right-12 px-1 py-0.5">
|
||||
<p data-services-state="draft" class="dark:text-gray-300 -z-10 opacity-0 group-hover:z-10 group-hover:opacity-100 transition fixed bg-white dark:bg-slate-800 rounded right-12 px-1 py-0.5">
|
||||
Draft
|
||||
</p>
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
|
|
@ -66,7 +217,7 @@
|
|||
</button>
|
||||
{% else %}
|
||||
<button class="group relative">
|
||||
<p class="dark:text-gray-300 -z-10 opacity-0 group-hover:z-10 group-hover:opacity-100 transition fixed bg-white dark:bg-slate-800 rounded right-12 px-1 py-0.5">
|
||||
<p data-services-state="online" class="dark:text-gray-300 -z-10 opacity-0 group-hover:z-10 group-hover:opacity-100 transition fixed bg-white dark:bg-slate-800 rounded right-12 px-1 py-0.5">
|
||||
Online
|
||||
</p>
|
||||
<svg data-toggle-draft="false"
|
||||
|
|
@ -79,7 +230,7 @@
|
|||
</button>
|
||||
{% endif %}
|
||||
</div>
|
||||
<h6 class="text-center sm:text-left mb-2 font-semibold text-gray-600 dark:text-white/80">
|
||||
<h6 data-services-method="{{ service["SERVER_NAME"]['method'] }}" class="text-center sm:text-left mb-2 font-semibold text-gray-600 dark:text-white/80">
|
||||
{{ service["SERVER_NAME"]['method'] }}
|
||||
</h6>
|
||||
{% set details = [
|
||||
|
|
|
|||
14
src/ui/templates/services_modal.html
vendored
14
src/ui/templates/services_modal.html
vendored
|
|
@ -61,6 +61,20 @@
|
|||
<!-- end search inpt-->
|
||||
</div>
|
||||
{% include "settings_tabs.html" %}
|
||||
<div data-services-nomatch
|
||||
class="hidden w-full overflow-hidden grid grid-cols-12 max-h-100 sm:max-h-125 col-span-12 p-4 relative break-words">
|
||||
<div class="col-span-12 flex flex-col justify-center items-center h-fit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor"
|
||||
class="mb-2 w-8 h-8 dark:stroke-white/90 stroke-gray-800">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="m21 21-5.197-5.197m0 0A7.5 7.5 0 1 0 5.196 5.196a7.5 7.5 0 0 0 10.607 10.607ZM10.5 7.5v6m3-3h-6" />
|
||||
</svg>
|
||||
<h5 class="font-bold dark:text-white/90 mx-2 text-gray-800">No settings match</h5>
|
||||
</div>
|
||||
</div>
|
||||
<!-- new and edit form -->
|
||||
<form data-services-modal-form
|
||||
class="w-full h-full flex flex-col justify-between"
|
||||
|
|
|
|||
|
|
@ -9,10 +9,11 @@ from selenium.common.exceptions import TimeoutException
|
|||
|
||||
from wizard import DRIVER
|
||||
from base import TEST_TYPE
|
||||
from utils import access_page, assert_alert_message, assert_button_click, safe_get_element, wait_for_service
|
||||
from utils import access_page, assert_alert_message, assert_button_click, safe_get_element, wait_for_service, verify_select_filters
|
||||
|
||||
exit_code = 0
|
||||
|
||||
|
||||
try:
|
||||
log_info("Navigating to the services page ...")
|
||||
access_page(DRIVER, "/html/body/aside[1]/div[1]/div[3]/ul/li[4]/a", "services")
|
||||
|
|
@ -233,7 +234,70 @@ try:
|
|||
log_error("The service is still working, exiting ...")
|
||||
exit(1)
|
||||
|
||||
log_info("The service is not working, as expected, trying to delete it ...")
|
||||
log_info("Create another service app3.example.com to get filters (need at least 4 services on page)")
|
||||
|
||||
assert_button_click(DRIVER, "//button[@data-services-action='new']")
|
||||
|
||||
server_name_input = safe_get_element(DRIVER, By.ID, "SERVER_NAME")
|
||||
assert isinstance(server_name_input, WebElement), "Input is not a WebElement"
|
||||
|
||||
server_name_input.clear()
|
||||
server_name_input.send_keys("app3.example.com")
|
||||
|
||||
access_page(DRIVER, "//button[@data-services-modal-submit='']", "services", False)
|
||||
|
||||
if TEST_TYPE == "linux":
|
||||
wait_for_service("app3.example.com")
|
||||
|
||||
try:
|
||||
services = safe_get_element(DRIVER, By.XPATH, "//div[@data-services-service]", multiple=True, error=True)
|
||||
assert isinstance(services, list), "Services is not a list"
|
||||
except TimeoutException:
|
||||
log_exception("Services not found, exiting ...")
|
||||
exit(1)
|
||||
|
||||
if len(services) < 4:
|
||||
log_error(f"The service hasn't been created ({len(services)} services found), exiting ...")
|
||||
exit(1)
|
||||
|
||||
server_name_elem = safe_get_element(DRIVER, By.XPATH, "//div[@data-services-service='app3.example.com']//h5")
|
||||
assert isinstance(server_name_elem, WebElement), "Server name element is not a WebElement"
|
||||
if server_name_elem.text.strip() != "app3.example.com":
|
||||
log_error('The service "app3.example.com" is not present, exiting ...')
|
||||
exit(1)
|
||||
|
||||
service_method_elem = safe_get_element(DRIVER, By.XPATH, "//div[@data-services-service='app3.example.com']//h6")
|
||||
assert isinstance(service_method_elem, WebElement), "Service method element is not a WebElement"
|
||||
if service_method_elem.text.strip() != "ui":
|
||||
log_error("The service should have been created by the ui, exiting ...")
|
||||
exit(1)
|
||||
|
||||
log_info("Service app3.example.com is present, trying filters...")
|
||||
|
||||
# Set keyword with no matching settings
|
||||
keyword_no_match = "dqz48 é84 dzq 584dz5qd4"
|
||||
btn_keyword = safe_get_element(DRIVER, "js", 'document.querySelector("button#service-name-keyword")')
|
||||
btn_keyword.send_keys(keyword_no_match)
|
||||
sleep(0.1)
|
||||
|
||||
# Check that the no matching element is shown and other card hide
|
||||
is_no_match = DRIVER.execute_script('return document.querySelector("[data-services-nomatch-card]").classList.contains("hidden") ? false : true')
|
||||
if not is_no_match:
|
||||
log_error(f"Filter keyword with value {keyword_no_match} shouldn't match something.")
|
||||
exit(1)
|
||||
|
||||
# Reset
|
||||
btn_keyword.send_keys("")
|
||||
|
||||
# Test select filters
|
||||
select_filters = [
|
||||
{"name": "Method", "id": "method", "value": "all", "update_value": "123456"},
|
||||
{"name": "State", "id": "state", "value": "all", "update_value": "123456"},
|
||||
]
|
||||
|
||||
verify_select_filters(DRIVER, "services", select_filters)
|
||||
|
||||
log_info("Filters working as expected, trying to delete app2.example.com ...")
|
||||
|
||||
try:
|
||||
delete_button = safe_get_element(DRIVER, By.XPATH, "//button[@data-services-action='delete' and @data-services-name='app2.example.com']", error=True)
|
||||
|
|
|
|||
|
|
@ -167,18 +167,18 @@ def wait_for_service(service: str = "www.example.com"):
|
|||
def verify_select_filters(driver, page_name: str, filter_items: list):
|
||||
for item in filter_items:
|
||||
# Update in order to get no match
|
||||
driver.execute_script(f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn=`{item['id']}`][value=`{item['value']}`]').setAttribute('value', '{item['update_value']}')")
|
||||
select_btn = safe_get_element(driver, "js", f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn=`{item['id']}`][value=`{item['update_value']}`]')")
|
||||
driver.execute_script(f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn={item['id']}][value={item['value']}]').setAttribute('value', '{item['update_value']}')")
|
||||
select_btn = safe_get_element(driver, "js", f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn={item['id']}][value={item['update_value']}]')")
|
||||
select_btn.click()
|
||||
|
||||
# Verify
|
||||
bans_hidden = safe_get_element(driver, "js", f'document.querySelectorAll("[data-{page_name}-list-item][class*=`hidden`]")')
|
||||
bans_hidden = safe_get_element(driver, "js", f'document.querySelectorAll("[data-{page_name}-list-item][class*=hidden]")')
|
||||
if len(bans_hidden) == 0:
|
||||
log_error(f"The {item['name']} filter is not working, exiting ...")
|
||||
exit(1)
|
||||
|
||||
# Reset
|
||||
driver.execute_script(f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn=`{item['id']}`][value=`{item['update_value']}`]').setAttribute('value', '{item['value']}')")
|
||||
select_btn = safe_get_element(driver, "js", f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn=`{item['id']}`][value=`{item['value']}`]')")
|
||||
driver.execute_script(f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn={item['id']}][value={item['update_value']}]').setAttribute('value', '{item['value']}')")
|
||||
select_btn = safe_get_element(driver, "js", f"document.querySelector('[data-{page_name}-setting-select-dropdown-btn={item['id']}][value={item['value']}]')")
|
||||
select_btn.click()
|
||||
sleep(0.1)
|
||||
|
|
|
|||
Loading…
Reference in a new issue