update plugins and tests

* remove tabs and update by select component for better UX
* update script to make new elements work
* update keyword filter settings script
* no longer max-h with overflow on global  config page
* update service modal to fit new elements, enhance responsive and add separator
*remove error actions on test utils
* enhance some base components like button size, service card data position, modal size...
This commit is contained in:
Jordan Blasenhauer 2024-02-29 15:35:44 +01:00
parent f7a5c3a6b6
commit df3c16c120
12 changed files with 304 additions and 224 deletions

File diff suppressed because one or more lines are too long

View file

@ -534,7 +534,7 @@ const setSelect = new Select();
const setPassword = new Password();
const setDisabledPop = new DisabledPop();
const setNews = new News();
// const setBanner = new Banner();
const setBanner = new Banner();
const setDarkM = new darkMode();
const setFlash = new FlashMsg();
const setLoader = new Loader();

View file

@ -27,20 +27,25 @@ class Multiple {
}
const setPopover = new Popover("main", "global-config");
const setTabs = new Tabs("[global-config-tabs]", "global-config");
const setTabs = new Tabs(
document.querySelector("[data-global-config-tabs-container]"),
document.querySelector("[data-global-config-plugins-container]"),
);
const format = new FormatValue();
const setMultiple = new Multiple("global-config");
const setFilterGlobal = new FilterSettings(
"settings-filter",
"[data-service-content='settings']",
"keyword",
document.querySelector("[data-global-config-tabs-container]"),
document.querySelector("[data-global-config-plugins-container]"),
);
const checkServiceModalKeyword = new CheckNoMatchFilter(
document.querySelector("input#settings-filter"),
document.querySelector("input#keyword"),
"input",
document
.querySelector("[data-global-config-tabs]")
.querySelectorAll("[data-tab-handler]"),
.querySelector("[data-global-config-plugins-container]")
.querySelectorAll("[data-plugin-item]"),
document.querySelector("[data-global-config-form]"),
document.querySelector("[data-global-config-nomatch]"),
);

View file

@ -1406,14 +1406,19 @@ class Filter {
const setDropdown = new Dropdown();
const setFilter = new Filter();
const setTabs = new Tabs(
document.querySelector("[data-services-tabs]"),
document.querySelector("[data-services-modal-form]"),
);
const setPopover = new Popover();
const setTabs = new Tabs();
const setModal = new ServiceModal();
const format = new FormatValue();
const setFilterGlobal = new FilterSettings(
"settings-filter",
"[data-service-content='settings']",
document.querySelector("[data-services-tabs]"),
document.querySelector("[data-services-modal-form]"),
);
const setMultiple = new Multiple("services");
@ -1422,8 +1427,8 @@ const checkServiceModalKeyword = new CheckNoMatchFilter(
document.querySelector("input#settings-filter"),
"input",
document
.querySelector("[data-services-tabs-desktop]")
.querySelectorAll("[data-tab-handler]"),
.querySelector("[data-services-modal-form]")
.querySelectorAll("[data-plugin-item]"),
document.querySelector("[data-services-modal-form]"),
document.querySelector("[data-services-nomatch]"),
);

View file

@ -44,101 +44,105 @@ class Popover {
}
class Tabs {
constructor() {
constructor(tabContainer, contentContainer) {
this.tabContainer = tabContainer;
this.contentContainer = contentContainer;
this.tabArrow = tabContainer
.querySelector("[data-tab-dropdown-btn]")
.querySelector("[data-tab-dropdown-arrow]");
this.init();
}
init() {
window.addEventListener("click", (e) => {
try {
if (
e.target.closest("button").hasAttribute("data-tab-handler") ||
e.target.closest("button").hasAttribute("data-tab-handler-mobile")
) {
if (e.target.closest("button").hasAttribute("data-tab-handler")) {
//get needed data
const tab = e.target.closest("button");
const tabAtt =
tab.getAttribute("data-tab-handler") ||
tab.getAttribute("data-tab-handler-mobile");
const container = tab.closest("div[data-service-content]");
const tabAtt = tab.getAttribute("data-tab-handler");
// change style
this.resetTabsStyle(container);
this.highlightClicked(container, tabAtt);
this.resetTabsStyle();
this.highlightClicked(tabAtt);
//show content
this.hideAllSettings(container);
this.showSettingClicked(container, tabAtt);
this.hideAllSettings();
this.showSettingClicked(tabAtt);
//close dropdown and change btn textcontent on mobile
this.setDropBtnText(container, tabAtt);
this.closeDropdown(container);
this.setDropBtnText(tabAtt);
this.closeDropdown();
}
} catch (err) {}
} catch (e) {}
try {
if (e.target.closest("button").hasAttribute("data-tab-dropdown-btn")) {
const dropBtn = e.target.closest("button");
const container = dropBtn.closest("div[data-service-content]");
this.toggleDropdown(container);
this.toggleDropdown();
}
} catch (err) {}
});
}
resetTabsStyle(container) {
//reset desktop style
const tabsDesktop = container.querySelectorAll("button[data-tab-handler]");
tabsDesktop.forEach((tab) => {
tab.classList.remove("active");
});
//reset mobile style
const tabsMobile = container.querySelectorAll(
"button[data-tab-handler-mobile]",
resetTabsStyle() {
const tabsMobile = this.tabContainer.querySelectorAll(
"button[data-tab-handler]",
);
tabsMobile.forEach((tab) => {
tab.classList.remove("active");
});
}
highlightClicked(container, tabAtt) {
//desktop case
const tabDesktop = container.querySelector(
highlightClicked(tabAtt) {
const tabMobile = this.tabContainer.querySelector(
`button[data-tab-handler='${tabAtt}']`,
);
tabDesktop.classList.add("active");
//mobile case
const tabMobile = container.querySelector(
`button[data-tab-handler-mobile='${tabAtt}']`,
);
tabMobile.classList.add("active");
}
hideAllSettings(container) {
const plugins = container.querySelectorAll("[data-plugin-item]");
hideAllSettings() {
const plugins =
this.contentContainer.querySelectorAll("[data-plugin-item]");
plugins.forEach((plugin) => {
plugin.classList.add("hidden");
});
}
showSettingClicked(container, tabAtt) {
const plugin = container.querySelector(`[data-plugin-item='${tabAtt}']`);
showSettingClicked(tabAtt) {
const plugin = this.contentContainer.querySelector(
`[data-plugin-item='${tabAtt}']`,
);
plugin.classList.remove("hidden");
}
setDropBtnText(container, tabAtt) {
const dropBtn = container.querySelector("[data-tab-dropdown-btn]");
setDropBtnText(tabAtt) {
const dropBtn = this.tabContainer.querySelector("[data-tab-dropdown-btn]");
dropBtn.querySelector("span").textContent = tabAtt;
}
closeDropdown(container) {
const dropdown = container.querySelector("[data-tab-dropdown]");
closeDropdown() {
const dropdown = this.tabContainer.querySelector("[data-tab-dropdown]");
dropdown.classList.add("hidden");
dropdown.classList.remove("flex");
this.updateTabArrow();
}
toggleDropdown(container) {
const dropdown = container.querySelector("[data-tab-dropdown]");
toggleDropdown() {
const dropdown = this.tabContainer.querySelector("[data-tab-dropdown]");
dropdown.classList.toggle("hidden");
dropdown.classList.toggle("flex");
this.updateTabArrow();
}
updateTabArrow() {
const dropdown = this.tabContainer.querySelector("[data-tab-dropdown]");
if (dropdown.classList.contains("hidden")) {
this.tabArrow.classList.remove("rotate-180");
}
if (dropdown.classList.contains("flex")) {
this.tabArrow.classList.add("rotate-180");
}
}
}
@ -159,11 +163,11 @@ class FormatValue {
}
class FilterSettings {
constructor(inputID, container) {
constructor(inputID, tabContainer, contentContainer) {
this.input = document.querySelector(`input#${inputID}`);
//DESKTOP
this.container = document.querySelector(container);
this.deskTabs = this.container.querySelectorAll(`[data-tab-handler]`);
this.tabContainer = tabContainer;
this.contentContainer = contentContainer;
this.tabsEls = this.tabContainer.querySelectorAll(`[data-tab-handler]`);
this.init();
}
@ -172,8 +176,9 @@ class FilterSettings {
this.resetFilter();
//get inp format
const inpValue = this.input.value.trim().toLowerCase();
//loop all tabs
this.deskTabs.forEach((tab) => {
this.tabsEls.forEach((tab) => {
//get settings of tabs except multiples
const settings = this.getSettingsFromTab(tab);
@ -196,31 +201,65 @@ class FilterSettings {
//case no setting match, hidden tab and content
if (settingCount === hiddenCount) {
const tabName = tab.getAttribute(`data-tab-handler`);
//hide mobile and desk tabs
tab.classList.add("hidden");
this.container
.querySelector(`[data-tab-handler-mobile="${tabName}"]`)
.classList.add("hidden");
this.container
tab.classList.add("!hidden");
this.contentContainer
.querySelector(`[data-plugin-item=${tabName}]`)
.querySelector("[data-setting-header]")
.classList.add("hidden");
}
});
// check current tabs states
let isAllHidden = true;
let firstNotHiddenEl = null;
for (let i = 0; i < this.tabsEls.length; i++) {
const tab = this.tabsEls[i];
if (!tab.classList.contains("!hidden")) {
isAllHidden = false;
firstNotHiddenEl = tab;
break;
}
}
// case no tab match
if (isAllHidden) {
return (this.tabContainer.querySelector(
"[data-tab-dropdown-btn] span",
).textContent = "No match");
}
// click first not hidden tab
const currTabEl = this.tabContainer.querySelector(
"[data-tab-dropdown-btn] span",
);
const currTabName = currTabEl.textContent.toLowerCase().trim();
// case previously no match
if (currTabName.toLowerCase() === "no match") {
return firstNotHiddenEl.click();
}
const currTabBtn = this.tabContainer.querySelector(
`[data-tab-handler='${currTabName}']`,
);
if (!currTabBtn) return;
if (!currTabBtn.classList.contains("!hidden")) {
return currTabBtn.click();
}
if (currTabBtn.classList.contains("!hidden")) {
return firstNotHiddenEl.click();
}
});
}
resetFilter() {
this.deskTabs.forEach((tab) => {
this.tabsEls.forEach((tab) => {
const tabName = tab.getAttribute(`data-tab-handler`);
//hide mobile and desk tabs
tab.classList.remove("hidden");
this.container
.querySelector(`[data-tab-handler-mobile="${tabName}"]`)
.classList.remove("hidden");
this.container
tab.classList.remove("!hidden");
this.contentContainer
.querySelector(`[data-plugin-item=${tabName}]`)
.querySelector("[data-setting-header]")
.classList.remove("hidden");
const settings = this.getSettingsFromTab(tab);
settings.forEach((setting) => {
@ -231,7 +270,7 @@ class FilterSettings {
getSettingsFromTab(tabEl) {
const tabName = tabEl.getAttribute(`data-tab-handler`);
const settingContainer = this.container
const settingContainer = this.contentContainer
.querySelector(`[data-plugin-item="${tabName}"]`)
.querySelector(`[data-plugin-settings]`);
const settings = settingContainer.querySelectorAll(
@ -242,12 +281,20 @@ class FilterSettings {
}
class CheckNoMatchFilter {
constructor(input, type, elsToCheck, elContainer, noMatchEl) {
constructor(
input,
type,
elsToCheck,
elContainer,
noMatchEl,
classToCheck = "hidden",
) {
this.input = input;
this.type = type;
this.elsToCheck = elsToCheck;
this.elContainer = elContainer;
this.noMatchEl = noMatchEl;
this.classToCheck = classToCheck;
this.init();
}
@ -276,20 +323,24 @@ class CheckNoMatchFilter {
let isAllHidden = true;
for (let i = 0; i < this.elsToCheck.length; i++) {
const el = this.elsToCheck[i];
if (!el.classList.contains("hidden")) {
if (!el.classList.contains(this.classToCheck)) {
isAllHidden = false;
break;
}
}
if (isAllHidden) {
this.noMatchEl.classList.remove("hidden");
this.elContainer ? this.elContainer.classList.add("hidden") : false;
this.noMatchEl.classList.remove(this.classToCheck);
this.elContainer
? this.elContainer.classList.add(this.classToCheck)
: false;
}
if (!isAllHidden) {
this.elContainer ? this.elContainer.classList.remove("hidden") : false;
this.noMatchEl.classList.add("hidden");
this.elContainer
? this.elContainer.classList.remove(this.classToCheck)
: false;
this.noMatchEl.classList.add(this.classToCheck);
}
}, 20);
}

View file

@ -23,24 +23,28 @@
@apply hidden;
}
.separator {
@apply h-px mx-0 mt-3 mb-2 bg-transparent bg-gradient-to-r from-transparent via-black/40 to-transparent dark:bg-gradient-to-r dark:from-transparent dark:via-white dark:to-transparent;
}
.close-btn {
@apply dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-red-500 border border-red-500 uppercase align-middle transition-all rounded-lg cursor-pointer dark:bg-gray-200 dark:hover:brightness-75 bg-white hover:bg-white/80 focus:bg-white/80 leading-normal ease-in tracking-tight-rem shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
@apply dark:brightness-90 inline-block px-4 py-2 md:px-5 md:py-2.5 font-bold text-center text-red-500 border border-red-500 uppercase align-middle transition-all rounded-lg cursor-pointer dark:bg-gray-200 dark:hover:brightness-75 bg-white hover:bg-white/80 focus:bg-white/80 leading-normal ease-in tracking-tight-rem shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
}
.valid-btn {
@apply tracking-wide dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
@apply tracking-wide dark:brightness-90 inline-block px-4 py-2 md:px-5 md:py-2.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
}
.delete-btn {
@apply tracking-wide dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-red-500 hover:bg-red-500/80 focus:bg-red-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
@apply tracking-wide dark:brightness-90 inline-block px-4 py-2 md:px-5 md:py-2.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-red-500 hover:bg-red-500/80 focus:bg-red-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
}
.edit-btn {
@apply tracking-wide dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-yellow-500 hover:bg-yellow-500/80 focus:bg-yellow-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
@apply tracking-wide dark:brightness-90 inline-block px-4 py-2 md:px-5 md:py-2.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-yellow-500 hover:bg-yellow-500/80 focus:bg-yellow-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
}
.info-btn {
@apply tracking-wide dark:brightness-90 inline-block px-6 py-3 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
@apply tracking-wide dark:brightness-90 inline-block px-4 py-2 md:px-5 md:py-2.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-sky-500 hover:bg-sky-500/80 focus:bg-sky-500/80 leading-normal ease-in shadow-xs hover:-translate-y-px active:opacity-85 hover:shadow-md disabled:cursor-not-allowed dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400/0 dark:disabled:bg-gray-700 dark:disabled:border-gray-700/0 disabled:hover:translate-y-0 disabled:hover:bg-gray-400 disabled:hover:border-gray-400/0 dark:disabled:hover:translate-y-0 dark:disabled:hover:bg-gray-700 dark:disabled:hover:border-gray-700/0;
}
/*----------------------------------------------*/
@ -102,43 +106,27 @@
/*---------------SETTINGS_TABS-----------------*/
/*---------------------------------------------*/
.active.settings-tabs-tab-btn {
@apply border-primary dark:hover:bg-slate-800 dark:border-slate-600 dark:bg-slate-700 border my-1 relative px-3 py-3 font-bold text-center uppercase align-middle transition-all rounded-none cursor-pointer bg-white hover:bg-gray-100 leading-normal text-sm ease-in tracking-tight-rem shadow-xs hover:shadow-md brightness-90 z-10;
.settings-tabs-btn {
@apply dark:hover:brightness-95 dark:border-slate-600 dark:bg-slate-700 border-primary border w-full flex items-center justify-between rounded-lg hover:-translate-y-px my-1 px-4 py-2 md:px-6 md:py-3 font-bold text-center uppercase align-middle transition-all cursor-pointer bg-white hover:bg-gray-50 leading-normal text-sm ease-in tracking-tight-rem shadow-xs hover:shadow-md;
}
.settings-tabs-tab-btn {
@apply border-primary dark:hover:bg-slate-800 dark:border-slate-600 dark:bg-slate-700 border my-1 relative px-3 py-3 font-bold text-center uppercase align-middle transition-all rounded-none cursor-pointer bg-white hover:bg-gray-100 leading-normal text-sm ease-in tracking-tight-rem shadow-xs hover:shadow-md;
}
.settings-tabs-name {
@apply text-primary transition duration-300 ease-in-out dark:opacity-90 pl-3 pr-2 dark:text-gray-300;
}
.settings-tabs-popover-container {
@apply top-[60px] min-w-[150px] dark:brightness-90 bg-blue-500 transition z-50 rounded-md p-3 left-0 absolute;
}
.settings-tabs-popover-text {
@apply font-bold text-sm text-white m-0;
}
.settings-tabs-mobile-btn {
@apply dark:hover:brightness-95 dark:border-slate-600 dark:bg-slate-700 border-primary border w-full flex items-center justify-between rounded-lg hover:-translate-y-px my-1 px-6 py-3 font-bold text-center uppercase align-middle transition-all cursor-pointer bg-white hover:bg-gray-50 leading-normal text-sm ease-in tracking-tight-rem shadow-xs hover:shadow-md;
}
.settings-tabs-mobile-btn-text {
.settings-tabs-btn-text {
@apply transition duration-300 ease-in-out dark:opacity-90 dark:text-gray-300 text-primary;
}
.active.settings-tabs-mobile-dropdown-btn {
.active.settings-tabs-dropdown-btn {
@apply border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative px-6 py-2 text-center 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 z-[1000] dark:bg-primary bg-primary text-gray-300;
}
.first.settings-tabs-mobile-dropdown-btn {
.first.settings-tabs-dropdown-btn {
@apply border-t rounded-t border-b border-l border-r;
}
.settings-tabs-mobile-dropdown-btn {
.last.settings-tabs-dropdown-btn {
@apply rounded-b;
}
.settings-tabs-dropdown-btn {
@apply flex justify-between border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 bg-white text-gray-700 my-0 relative px-6 py-2 text-center align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300;
}

View file

@ -1,32 +1,99 @@
{% extends "base.html" %}
{% block content %}
<div data-service-content="settings"
class="col-span-12 gap-y-4 grid grid-cols-12">
<div class="p-4 col-span-12 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">
<div data-{{ current_endpoint }}-tabs-header class="flex flex-col xs:flex-row xs:justify-start xs:items-center gap-x-4 gap-y-2 my-3">
<h5 class="transition duration-300 ease-in-out 0 ml-2 font-bold text-md uppercase dark:text-white/90 mb-0">CONFIGS</h5>
<!-- search inpt-->
<div class="flex relative col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3">
<label class="sr-only" for="settings-filter">filter settings</label>
<input type="text"
id="settings-filter"
name="settings-filter"
class="col-span-12 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="key words"
pattern="(.*?)"
required />
</div>
<!-- end search inpt-->
</div>
<!-- tabs -->
{% include "settings_tabs.html" %}
<!-- end tabs-->
<div data-global-config-tabs-container
class="z-100 w-full grid grid-cols-12 h-fit max-h-100 sm:max-h-125 col-span-12 md:col-span-6 lg:col-span-4 p-4 relative break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div data-{{ current_endpoint }}-tabs-header class="col-span-12">
<div class="flex flex-col xs:flex-row xs:justify-start xs:items-center gap-x-4 gap-y-2 my-3">
<h5 class="transition duration-300 ease-in-out 0 ml-2 font-bold text-md uppercase dark:text-white/90 mb-0">PLUGINS</h5>
</div>
{% include "settings_tabs.html" %}
</div>
</div>
<!-- filter -->
{% set filters = [
{
"type": "input",
"name": "Search",
"label": "search",
"id": "keyword",
"placeholder": "keyword",
"pattern": "(.*?)"
}
] %}
<div data-global-config-filter
class="h-fit p-4 col-span-12 md:col-span-6 lg:col-span-5 xl:col-span-4 2xl:col-span-3 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">FILTER</h5>
<div class="mx-2 grid grid-cols-12 gap-x-4 gap-y-2">
{% for filter in filters %}
{% if filter['type'] == 'input' %}
<!-- search inpt-->
<div class="flex flex-col relative col-span-12">
<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">
<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-global-config-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="global-config-{{ filter['id'] }}"
data-name="global-config-{{ filter['id'] }}"
data-global-config-setting-select-text="{{ filter['id'] }}">{{ filter['value'] }}</span>
<!-- chevron -->
<svg data-global-config-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-global-config-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-global-config-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>
<!-- end filter -->
<div data-global-config-plugins-container
class="col-span-12 gap-y-4 grid grid-cols-12">
<!-- form global conf -->
<form data-global-config-form
id="form-edit-global-configs"
method="POST"
class="flex flex-col justify-between overflow-hidden overflow-y-auto max-h-135 md:max-h-160 dark:brightness-110 col-span-12 break-words bg-white shadow-xl p-4 dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
class="flex flex-col justify-between overflow-hidden overflow-y-auto dark:brightness-110 col-span-12 break-words bg-white shadow-xl p-4 dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<!-- plugin item -->
{% include "settings_plugins.html" %}

View file

@ -228,7 +228,7 @@
{% endif %}
</div>
<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">
class="text-left sm:mb-2 font-semibold text-gray-600 dark:text-white/80">
{{ service["SERVER_NAME"]['method'] }}
</h6>
{% set details = [

View file

@ -1,17 +1,17 @@
<!-- modal -->
<div data-service-content="settings"
<div data-services-plugins-container
data-services-modal
class="dark:brightness-110 hidden w-screen h-screen fixed bg-gray-600/50 z-[1001] top-0 left-0 justify-center items-center">
<div data-services-modal-card
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-4 pt-4 pb-8 w-full sm:min-w-[500px] h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div class="w-full flex justify-between mb-2">
<div class="flex justify-start items-center">
class="overflow-y-auto mx-0 sm:mx-6 lg:mx-8 my-3 px-3 sm:px-4 pt-4 pb-8 w-full h-fit sm:min-w-[500px] max-w-[1000px] max-h-[95vh] md:max-h-[90vh] flex flex-col break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div class="w-full flex justify-between items-start mb-2">
<div class="flex flex-col sm:flex-row justify-start items-start sm:items-center">
<p data-services-modal-title
class="transition duration-300 ease-in-out dark:opacity-90 dark:text-gray-200 mb-1 font-sans font-semibold leading-normal uppercase text-md">
SERVICE MODAL
</p>
<button data-toggle-draft-btn
class="transition hover:brightness-75 dark:hover:brightness-110 ml-4 flex items-center border border-gray-700 dark:border-gray-300 rounded py-1 px-2">
class="transition hover:brightness-75 dark:hover:brightness-110 sm:ml-4 flex items-center border border-gray-700 dark:border-gray-300 rounded py-1 px-2">
<p data-toggle-draft="true"
class="hidden dark:text-gray-300 mb-0 mr-2 pointer-events-none">Draft</p>
<p data-toggle-draft="false"
@ -43,26 +43,33 @@
</button>
</div>
<div data-services-tabs-header
class="flex flex-col sm:flex-row justify-start items-start sm:items-center gap-x-4 gap-y-2 my-3">
<h5 class="transition duration-300 ease-in-out dark:opacity-90 ml-2 font-bold text-md uppercase dark:text-white mb-0">
CONFIGS
</h5>
<!-- search inpt-->
<div class="flex relative">
<label class="sr-only" for="settings-filter">search</label>
<input type="text"
id="settings-filter"
name="settings-filter"
class="col-span-12 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="key words"
pattern="(.*?)"
required />
class="flex flex-col">
<div class="flex flex-col sm:flex-row justify-start w-full items-start sm:items-center gap-y-3 gap-x-4">
<div class="w-full sm:min-w-[250px] max-w-[300px]">
{% include "settings_tabs.html" %}
</div>
<!-- search inpt-->
<div class="flex relative w-full max-w-[200px]">
<label class="sr-only" for="settings-filter">search</label>
<input type="text"
id="settings-filter"
name="settings-filter"
class="col-span-12 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="search by keyword"
pattern="(.*?)"
required />
</div>
<!-- end search inpt-->
</div>
<div class="w-full min-w-[300px] my-1 sm:my-0">
<hr class="separator" />
</div>
<!-- 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">
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"
@ -77,13 +84,14 @@
</div>
<!-- new and edit form -->
<form data-services-modal-form
class="w-full h-full flex flex-col justify-between"
class="w-full h-[90vh] overflow-auto flex flex-col justify-between"
id="form-new"
method="POST">
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<input type="hidden" id="operation" value="new" name="operation" />
<input type="hidden" value="new" name="OLD_SERVER_NAME" />
<input type="hidden" value="no" name="is_draft" />
{% include "settings_plugins.html" %}
<!-- action button -->
<div class="w-full justify-center flex mt-10">

View file

@ -3,7 +3,7 @@
{% for plugin in plugins %}
<div data-plugin-item="{{ plugin['id'] }}"
id="{{ plugin['id'] }}"
class="{% if loop.index != 1 %}hidden{% endif %} w-full">
class="{% if loop.index != 1 %}hidden{% endif %} w-full px-1">
<!-- title and desc -->
<div class="col-span-12" data-setting-header>
{% if (plugin['type'] == "pro" and not is_pro_version) or (plugin['type'] == "pro" and is_pro_version and not pro_status) %}
@ -56,7 +56,8 @@
== "global-config" and value['context'] == "global" and not value['multiple'] or setting != "IS_DRAFT" and current_endpoint ==
"services" and value['context'] == "multisite" and not value['multiple'] %}
<div data-setting-container
class="mx-0 sm:mx-4 my-2 col-span-12 md:mx-6 md:my-3 md:col-span-6 2xl:mx-6 2xl:my-3 2xl:col-span-4"
data-{{current_endpoint}}-context="{{ value['context'] }}"
class="mx-0 sm:mx-2 my-2 col-span-12 md:my-3 md:col-span-6 2xl:my-3 2xl:col-span-4"
id="form-edit-{{ current_endpoint }}-{{ value["id"] }}">
<!-- title and info -->
<div class="flex items-center my-1 relative z-10">
@ -250,8 +251,8 @@
{% for multiple in multList %}
<!-- plugin multiple handler -->
<div data-multiple-handler
class="flex items-center mx-0 sm:mx-4 md:mx-6 md:my-3 my-2 2xl:mx-6 2xl:my-3 col-span-12 ">
<h5 class="input-title">{{ multiple }}</h5>
class="flex items-center mx-0 sm:mx-4 md:mx-6 mb-2 mt-5 2xl:mx-6 col-span-12 ">
<h5 class="input-title max-w-[150px] sm:max-w-[350px]">{{ multiple }}</h5>
<button data-{{ current_endpoint }}-multiple-add="{{ multiple }}" type="button" class="ml-3 dark:brightness-90 inline-block px-3 py-1.5 font-bold text-center text-white uppercase align-middle transition-all rounded-lg cursor-pointer bg-green-500 hover:bg-green-500/80 focus:bg-green-500/80 leading-normal text-md ease-in tracking-tight-rem shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md">
Add
</button>
@ -261,14 +262,14 @@
</div>
<!-- end plugin multiple handler-->
<!-- multiple settings -->
<div data-{{ current_endpoint }}-settings-multiple="{{ multiple }}_SCHEMA" class="bg-gray-50 dark:bg-slate-900/30 hidden w-full mb-8 grid-cols-12 border dark:border-gray-700 rounded">
<div data-{{ current_endpoint }}-settings-multiple="{{ multiple }}_SCHEMA" class="bg-gray-50 dark:bg-slate-900/30 hidden w-full mb-4 mt-2 grid-cols-12 border dark:border-gray-700 rounded">
{% for setting, value in plugin["settings"].items() %}
{# render only setting that match the multiple id and context #}
{% if current_endpoint
== "global-config" and value['context'] == "global" and value['multiple'] == multiple or current_endpoint ==
"services" and value['context'] == "multisite" and value['multiple'] == multiple %}
<div data-setting-container="{{ setting }}_SCHEMA"
class="mx-0 sm:mx-4 my-2 col-span-12 md:mx-6 md:my-3 md:col-span-6 2xl:mx-6 2xl:my-3 2xl:col-span-4"
class="mx-2 md:mx-3 my-2 md:my-3 col-span-12 md:col-span-6 2xl:col-span-4"
id="form-edit-{{ current_endpoint }}-{{ value["id"] }}_SCHEMA">
<!-- title and info -->
<div class="flex items-center my-1 relative z-10">

View file

@ -1,48 +1,14 @@
{% set current_endpoint = current_endpoint or url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<div data-{{ current_endpoint }}-tabs class="col-span-12 grid grid-cols-12 {% if current_endpoint == 'services' %}mb-4{% endif %}">
<!-- desktop tabs -->
<div role="tablist" data-{{ current_endpoint }}-tabs-desktop class="hidden md:block col-span-12">
<!-- tabs -->
{% for plugin in plugins %}
{% if current_endpoint == "services" and plugin["settings"]
and check_settings(plugin["settings"], "multisite") or current_endpoint == "global-config" and plugin["settings"]
and check_settings(plugin["settings"], "global") %}
<button role="tab"
data-tab-handler="{{ plugin['id'] }}"
class="{% if loop.first %}active{% endif %} settings-tabs-tab-btn">
<span class="w-full flex justify-between items-center">
<!-- text and icon -->
<span class="settings-tabs-name">{{ plugin['name'] }}</span>
<svg data-popover-btn="{{ plugin['name'] }}"
class=" fill-blue-500 h-5 w-5 mr-2 hover:brightness-95"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M256 512c141.4 0 256-114.6 256-256S397.4 0 256 0S0 114.6 0 256S114.6 512 256 512zM216 336h24V272H216c-13.3 0-24-10.7-24-24s10.7-24 24-24h48c13.3 0 24 10.7 24 24v88h8c13.3 0 24 10.7 24 24s-10.7 24-24 24H216c-13.3 0-24-10.7-24-24s10.7-24 24-24zm40-144c-17.7 0-32-14.3-32-32s14.3-32 32-32s32 14.3 32 32s-14.3 32-32 32z" />
</svg>
<!-- end text and icon -->
<!-- popover -->
<span data-popover-content="{{ plugin['name'] }}"
class="settings-tabs-popover-container hidden">
<span class="settings-tabs-popover-text">{{ plugin['description'] }}</span>
</span>
<!-- end popover -->
</span>
</button>
{% endif %}
{% endfor %}
<!--end tabs-->
</div>
<!-- end desktop tabs -->
<!-- mobile tabs -->
<div class="md:hidden relative col-span-12 h-full">
<div data-{{ current_endpoint }}-tabs class="col-span-12 grid grid-cols-12">
<div class="relative col-span-12 h-full">
<button data-tab-dropdown-btn
aria-controls="tab-dropdown-mobile"
class="settings-tabs-mobile-btn">
<span aria-description="current tab" class="settings-tabs-mobile-btn-text">
aria-controls="tab-dropdown"
class="settings-tabs-btn">
<span aria-description="current tab" class="settings-tabs-btn-text">
{% if current_endpoint == "global-config" %}general{% endif %}
</span>
<!-- chevron -->
<svg class="transition-transform h-4 w-4 fill-primary dark:fill-gray-300"
<svg data-tab-dropdown-arrow class="transition-transform h-4 w-4 fill-primary dark:fill-gray-300"
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" />
@ -50,34 +16,24 @@
<!-- end chevron -->
</button>
<!-- dropdown-->
<div id="tab-dropdown-mobile"
<div id="tab-dropdown"
role="listbox"
data-tab-dropdown
class="hidden z-100 absolute flex-col w-full overflow-hidden overflow-y-auto max-h-90">
class="hidden z-100 absolute flex-col w-full overflow-hidden overflow-y-auto h-90 max-h-[50vh]">
{% set first_el = "True" %}
{% for plugin in plugins %}
{% if current_endpoint == "services" and plugin["settings"]
and check_settings(plugin["settings"], "multisite") or current_endpoint == "global-config" and plugin["settings"]
and check_settings(plugin["settings"], "global") %}
{% if loop.first %}
<button role="option"
data-tab-handler-mobile="{{ plugin['id'] }}"
data-tab-handler="{{ plugin['id'] }}"
data-select="false"
id="edit-{{ current_endpoint }}-{{ plugin['id'] }}-tab"
class="active first settings-tabs-mobile-dropdown-btn">{{ plugin['name'] }}</button>
{% else %}
<button role="option"
data-tab-handler-mobile="{{ plugin['id'] }}"
data-select="false"
id="edit-{{ current_endpoint }}-{{ plugin['id'] }}-tab"
class="settings-tabs-mobile-dropdown-btn {% if loop.index == loop.length %}rounded-b{% endif %}">
{{ plugin['name'] }}
</button>
{% endif %}
class=" {% if loop.first %}
active first{% endif%} {% if loop.last%} last {% endif%} settings-tabs-dropdown-btn">{{ plugin['name'] }}</button>
{% endif %}
{% endfor %}
</div>
<!-- end dropdown-->
</div>
<!-- end mobile tabs -->
</div>

View file

@ -8,7 +8,6 @@ from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.remote.webelement import WebElement
from selenium.webdriver.support import expected_conditions as EC
from selenium.common.exceptions import ElementClickInterceptedException, TimeoutException, WebDriverException
from selenium.webdriver.common.action_chains import ActionChains
def safe_get_element(driver, by: str, selector: str, *, driver_wait: Optional[WebDriverWait] = None, multiple: bool = False, error: bool = False) -> Union[WebElement, List[WebElement]]:
@ -61,7 +60,7 @@ def assert_button_click(driver, button: Union[str, WebElement], by: str = "xpath
sleep(0.5)
ActionChains(driver).move_to_element(button).click(button).perform()
button.click()
clicked = True
return clicked