update dashboard

*add new global classes (checkbox, popover, select, filter, tabs)
*fix service modal error when setting current service data
This commit is contained in:
BlasenhauerJ 2023-01-29 22:37:53 +01:00
parent 580f33e56c
commit 3d96fdb344
15 changed files with 442 additions and 582 deletions

View File

@ -1490,13 +1490,8 @@ h6 {
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.translate-y-6 {
--tw-translate-y: 1.5rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}
.-translate-y-12 {
--tw-translate-y: -3rem;
.translate-y-12 {
--tw-translate-y: 3rem;
transform: translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y));
}

View File

@ -290,5 +290,5 @@ const setFlashSidebar = new Sidebar(
);
const setNews = new News();
const setDarkM = new darkMode();
const setCheckbox = new Checkbox("[sidebar-info]");
const setCheckbox = new Checkbox();
const setFlash = new FlashMsg();

View File

@ -1,87 +1,10 @@
import { Checkbox, Select, Password } from "./utils/form.js";
import { Popover, Tabs, FormatValue } from "./utils/settings.js";
class FilterSettings {
constructor(prefix) {
this.prefix = prefix;
this.input = document.querySelector("input#settings-filter");
//DESKTOP
this.deskTabs = document.querySelectorAll(`[${this.prefix}-item-handler]`);
this.init();
}
init() {
this.input.addEventListener("input", () => {
this.resetFilter();
//get inp format
const inpValue = this.input.value.trim().toLowerCase();
//loop all tabs
this.deskTabs.forEach((tab) => {
//get settings of tabs except multiples
const settings = this.getSettingsFromTab(tab);
//compare total count to currCount to determine
//if tabs need to be hidden
const settingCount = settings.length;
let hiddenCount = 0;
settings.forEach((setting) => {
try {
const title = setting
.querySelector("h5")
.textContent.trim()
.toLowerCase();
if (!title.includes(inpValue)) {
setting.classList.add("hidden");
hiddenCount++;
}
} catch (err) {}
});
//case no setting match, hidden tab and content
if (settingCount === hiddenCount) {
const tabName = tab.getAttribute(`${this.prefix}-item-handler`);
//hide mobile and desk tabs
tab.classList.add("hidden");
document
.querySelector(`[${this.prefix}-mobile-item-handler="${tabName}"]`)
.classList.add("hidden");
document
.querySelector(`[${this.prefix}-item=${tabName}]`)
.querySelector("[setting-header]")
.classList.add("hidden");
}
});
});
}
resetFilter() {
this.deskTabs.forEach((tab) => {
const tabName = tab.getAttribute(`${this.prefix}-item-handler`);
//hide mobile and desk tabs
tab.classList.remove("hidden");
document
.querySelector(`[${this.prefix}-mobile-item-handler="${tabName}"]`)
.classList.remove("hidden");
document
.querySelector(`[${this.prefix}-item=${tabName}]`)
.querySelector("[setting-header]")
.classList.remove("hidden");
const settings = this.getSettingsFromTab(tab);
settings.forEach((setting) => {
setting.classList.remove("hidden");
});
});
}
getSettingsFromTab(tabEl) {
const tabName = tabEl.getAttribute(`${this.prefix}-item-handler`);
const settingContainer = document
.querySelector(`[${this.prefix}-item="${tabName}"]`)
.querySelector(`[${this.prefix}-settings]`);
const settings = settingContainer.querySelectorAll("[setting-container]");
return settings;
}
}
import {
Popover,
Tabs,
FormatValue,
FilterSettings,
} from "./utils/settings.js";
class Multiple {
constructor(prefix) {
@ -103,12 +26,15 @@ class Multiple {
}
}
const setCheckbox = new Checkbox("[global-config-form]");
const setSelect = new Select("[global-config-form]", "global-config");
const setCheckbox = new Checkbox();
const setSelect = new Select();
const setPassword = new Password();
const setPopover = new Popover("main", "global-config");
const setTabs = new Tabs("[global-config-tabs]", "global-config");
const format = new FormatValue();
const setMultiple = new Multiple("global-config");
const setFilter = new FilterSettings("global-config");
const setFilterGlobal = new FilterSettings(
"settings-filter",
"[service-content='settings']"
);

View File

@ -256,6 +256,7 @@ class FetchLogs {
this.fromDate = Date.parse(this.fromDateInp.value)
? Date.parse(this.fromDateInp.value)
: Date.now() - 86400000;
console.log(Date.parse(this.fromDateInp.value));
this.toDate = Date.parse(this.toDateInp.value)
? Date.parse(this.toDateInp.value)
: false;
@ -433,7 +434,7 @@ class LogsDate {
}
}
const setCheckbox = new Checkbox("[logs-settings]");
const setCheckbox = new Checkbox();
const dropdown = new Dropdown("logs");
const setLogs = new FetchLogs();
const setFilter = new Filter("logs");

View File

@ -1,5 +1,10 @@
import { Checkbox, Select, Password } from "./utils/form.js";
import { Popover, Tabs, FormatValue } from "./utils/settings.js";
import {
Popover,
Tabs,
FormatValue,
FilterSettings,
} from "./utils/settings.js";
class ServiceModal {
constructor() {
@ -41,6 +46,7 @@ class ServiceModal {
//actions
try {
if (e.target.closest("button").hasAttribute("services-action")) {
console.log("click");
//do nothing if same btn and service as before
const isLastSame = this.isLastServAndAct(e.target);
if (isLastSame) return;
@ -51,20 +57,28 @@ class ServiceModal {
const serviceName = e.target
.closest("button")
.getAttribute("services-name");
console.log(action);
console.log(serviceName);
if (action === "edit" || action === "new") {
this.setForm(action, serviceName, this.formNewEdit);
this.setDefaultValue();
console.log("set default");
}
if (action === "delete")
this.setForm(action, serviceName, this.formDelete);
let form;
if (action === "edit" || action === "new") form = this.formNewEdit;
if (action === "delete") form = this.formDelete;
this.setForm(action, serviceName, form);
//reset settings value
if (action === "edit" || action === "new") this.setDefaultValue();
//get custom settings of service and apply it to modal settings
if (action === "edit") {
const servicesSettings = e.target
.closest("[services-service]")
.querySelector("[services-settings]")
.getAttribute("value");
console.log(servicesSettings);
const obj = JSON.parse(servicesSettings);
console.log(obj);
console.log("start update");
this.updateModalData(obj);
}
//open modal when all done
@ -92,8 +106,19 @@ class ServiceModal {
setDefaultValue() {
this.inputs.forEach((inpt) => {
const defaultVal = inpt.getAttribute("default-value");
const defaultMethod = inpt.getAttribute("default-method");
let defaultVal = "";
try {
defaultVal = inpt.getAttribute("default-value");
} catch (err) {
defaultVal = "";
}
let defaultMethod = "ui";
try {
defaultMethod = inpt.getAttribute("default-method");
} catch (err) {
defaultMethod = "ui";
}
//SET METHOD
this.setDisabled(inpt, defaultMethod);
@ -108,32 +133,44 @@ class ServiceModal {
}
});
console.log("input default");
this.selects.forEach((select) => {
const defaultVal = select.getAttribute("default-value");
const defaultMethod = select.getAttribute("default-method");
let defaultVal = "";
try {
defaultVal = select.getAttribute("default-value");
} catch (err) {
defaultVal = "";
}
let defaultMethod = "ui";
try {
defaultMethod = select.getAttribute("default-method");
} catch (err) {
defaultMethod = "ui";
}
if (defaultMethod === "ui" || defaultMethod === "default") {
document
.querySelector(
`[services-setting-select=${select.getAttribute(
"services-setting-select-default"
)}]`
`[setting-select=${select.getAttribute("setting-select-default")}]`
)
.removeAttribute("disabled");
} else {
document
.querySelector(
`[services-setting-select=${select.getAttribute(
"services-setting-select-default"
)}]`
`[setting-select=${select.getAttribute("setting-select-default")}]`
)
.setAttribute("disabled", "");
}
select.parentElement
.querySelector(
`button[services-setting-select-dropdown-btn][value='${defaultVal}']`
`button[setting-select-dropdown-btn][value='${defaultVal}']`
)
.click();
});
console.log("default select");
//server name always enabled for default
this.setNameSetting("ui", "");
}
@ -259,7 +296,7 @@ class ServiceModal {
if (inpt.tagName === "SELECT") {
inpt.parentElement
.querySelector(
`button[services-setting-select-dropdown-btn][value='${value}']`
`button[setting-select-dropdown-btn][value='${value}']`
)
.click();
}
@ -771,95 +808,17 @@ class Multiple {
}
}
class FilterSettings {
constructor(prefix) {
this.prefix = prefix;
this.input = document.querySelector("input#settings-filter");
//DESKTOP
this.deskTabs = document.querySelectorAll(`[${this.prefix}-item-handler]`);
this.init();
}
init() {
this.input.addEventListener("input", () => {
this.resetFilter();
//get inp format
const inpValue = this.input.value.trim().toLowerCase();
//loop all tabs
this.deskTabs.forEach((tab) => {
//get settings of tabs except multiples
const settings = this.getSettingsFromTab(tab);
//compare total count to currCount to determine
//if tabs need to be hidden
const settingCount = settings.length;
let hiddenCount = 0;
settings.forEach((setting) => {
try {
const title = setting
.querySelector("h5")
.textContent.trim()
.toLowerCase();
if (!title.includes(inpValue)) {
setting.classList.add("hidden");
hiddenCount++;
}
} catch (err) {}
});
//case no setting match, hidden tab and content
if (settingCount === hiddenCount) {
const tabName = tab.getAttribute(`${this.prefix}-item-handler`);
//hide mobile and desk tabs
tab.classList.add("hidden");
document
.querySelector(`[${this.prefix}-mobile-item-handler="${tabName}"]`)
.classList.add("hidden");
document
.querySelector(`[${this.prefix}-item=${tabName}]`)
.querySelector("[setting-header]")
.classList.add("hidden");
}
});
});
}
resetFilter() {
this.deskTabs.forEach((tab) => {
const tabName = tab.getAttribute(`${this.prefix}-item-handler`);
//hide mobile and desk tabs
tab.classList.remove("hidden");
document
.querySelector(`[${this.prefix}-mobile-item-handler="${tabName}"]`)
.classList.remove("hidden");
document
.querySelector(`[${this.prefix}-item=${tabName}]`)
.querySelector("[setting-header]")
.classList.remove("hidden");
const settings = this.getSettingsFromTab(tab);
settings.forEach((setting) => {
setting.classList.remove("hidden");
});
});
}
getSettingsFromTab(tabEl) {
const tabName = tabEl.getAttribute(`${this.prefix}-item-handler`);
const settingContainer = document
.querySelector(`[${this.prefix}-item="${tabName}"]`)
.querySelector(`[${this.prefix}-settings]`);
const settings = settingContainer.querySelectorAll("[setting-container]");
return settings;
}
}
const setCheckbox = new Checkbox("[services-modal-form]");
const setSelect = new Select("[services-modal-form]", "services");
const setCheckbox = new Checkbox();
const setSelect = new Select();
const setPassword = new Password();
const setPopover = new Popover("main", "services");
const setTabs = new Tabs("[services-tabs]", "services");
const setPopover = new Popover();
const setTabs = new Tabs();
const setModal = new ServiceModal();
const format = new FormatValue();
const setFilterGlobal = new FilterSettings(
"settings-filter",
"[service-content='settings']"
);
const setMultiple = new Multiple("services");
const setFilter = new FilterSettings("services");

View File

@ -1,14 +1,13 @@
class Checkbox {
constructor(container) {
this.container = container;
this.checkContainer = document.querySelector(`${this.container}`);
constructor() {
this.init();
}
init() {
this.checkContainer.addEventListener("click", (e) => {
//checkbox click
window.addEventListener("click", (e) => {
//prevent default checkbox behavior
try {
//case a related checkbox element is clicked and checkbox is enabled
if (
e.target.closest("div").hasAttribute("checkbox-handler") &&
!e.target
@ -16,80 +15,57 @@ class Checkbox {
.querySelector('input[type="checkbox"]')
.hasAttribute("disabled")
) {
//change DOM
//get related checkbox
const checkboxEl = e.target
.closest("div")
.querySelector('input[type="checkbox"]');
//set attribute value for new state
checkboxEl.checked
? checkboxEl.setAttribute("value", "yes")
: checkboxEl.setAttribute("value", "no");
}
} catch (err) {}
//nested elements click
try {
if (
e.target.closest("svg").hasAttribute("checkbox-handler") &&
!e.target
.closest("div")
.querySelector('input[type="checkbox"]')
.hasAttribute("disabled")
) {
e.target
.closest("div")
.querySelector('input[type="checkbox"]')
.click();
}
} catch (err) {}
});
}
}
class Select {
constructor(container, prefixAtt) {
this.prefix = prefixAtt;
this.container = container;
this.SelectContainer = document.querySelector(`${this.container}`);
constructor() {
this.init();
}
init() {
this.SelectContainer.addEventListener("click", (e) => {
window.addEventListener("click", (e) => {
//SELECT BTN LOGIC
try {
if (
e.target
.closest("button")
.hasAttribute(`${this.prefix}-setting-select`) &&
e.target.closest("button").hasAttribute(`setting-select`) &&
!e.target.closest("button").hasAttribute(`disabled`)
) {
this.toggleSelectBtn(e);
const btnEl = e.target.closest("button");
this.toggleSelectBtn(btnEl);
}
} catch (err) {}
//SELECT DROPDOWN BTN LOGIC
try {
if (
e.target
.closest("button")
.hasAttribute(`${this.prefix}-setting-select-dropdown-btn`)
e.target.closest("button").hasAttribute(`setting-select-dropdown-btn`)
) {
const btn = e.target.closest("button");
const btn = e.target.closest(`button[setting-select-dropdown-btn]`);
const btnValue = btn.getAttribute("value");
const btnSetting = btn.getAttribute(
`${this.prefix}-setting-select-dropdown-btn`
);
//add new value to custom
const selectCustom = document.querySelector(
`[${this.prefix}-setting-select="${btnSetting}"]`
);
selectCustom.querySelector(
`[${this.prefix}-setting-select-text]`
).textContent = btnValue;
const selectCustom = btn
.closest("div[select-container]")
.querySelector(`button[setting-select]`);
selectCustom.querySelector(`[setting-select-text]`).textContent =
btnValue;
//add selected to new value
//change style
const dropdownEl = document.querySelector(
`[${this.prefix}-setting-select-dropdown="${btnSetting}"]`
);
const dropdownEl = btn.closest(`div[setting-select-dropdown]`);
dropdownEl.classList.add("hidden");
dropdownEl.classList.remove("flex");
@ -115,18 +91,15 @@ class Select {
btn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
//close dropdown
const dropdownChevron = document.querySelector(
`svg[${this.prefix}-setting-select="${btnSetting}"]`
);
const dropdownChevron =
selectCustom.querySelector(`svg[setting-select]`);
dropdownChevron.classList.remove("rotate-180");
//update real select element
this.updateSelected(
document.querySelector(
`[${this.prefix}-setting-select-default="${btnSetting}"]`
),
btnValue
);
const realSel = btn
.closest("div[setting-container]")
.querySelector("select");
this.updateSelected(realSel, btnValue);
}
} catch (err) {}
});
@ -147,17 +120,12 @@ class Select {
newOption.setAttribute("selected", "");
}
toggleSelectBtn(e) {
const attribut = e.target
.closest("button")
.getAttribute(`${this.prefix}-setting-select`);
toggleSelectBtn(btn) {
//toggle dropdown
const dropdownEl = document.querySelector(
`[${this.prefix}-setting-select-dropdown="${attribut}"]`
);
const dropdownChevron = document.querySelector(
`svg[${this.prefix}-setting-select="${attribut}"]`
);
const dropdownEl = btn
.closest("div")
.querySelector(`[setting-select-dropdown]`);
const dropdownChevron = btn.querySelector(`svg[setting-select]`);
dropdownEl.classList.toggle("hidden");
dropdownEl.classList.toggle("flex");
dropdownChevron.classList.toggle("rotate-180");

View File

@ -1,141 +1,93 @@
class Popover {
constructor(container, prefix) {
this.prefix = prefix;
this.container = container;
this.popoverContainer = document.querySelector(`${this.container}`);
constructor() {
this.init();
}
init() {
let popoverCount = 0; //for auto hide
let btnPopoverAtt = ""; //to manage info btn clicked
this.popoverContainer.addEventListener("click", (e) => {
window.addEventListener("pointerover", (e) => {
//POPOVER LOGIC
try {
if (e.target.closest("svg").hasAttribute(`${this.prefix}-info-btn`)) {
const btnPop = e.target.closest("svg");
//toggle curr popover
const popover = btnPop.parentElement.querySelector(
`[${this.prefix}-info-popover]`
);
popover.classList.toggle("hidden");
//get a btn att if none
if (btnPopoverAtt === "")
btnPopoverAtt = btnPop.getAttribute(`${this.prefix}-info-btn`);
//compare prev btn and curr
//hide prev popover if not the same
if (
btnPopoverAtt !== "" &&
btnPopoverAtt !== btnPop.getAttribute(`${this.prefix}-info-btn`)
) {
const prevPopover = document.querySelector(
`[${this.prefix}-info-popover="${btnPopoverAtt}"]`
);
prevPopover.classList.add("hidden");
btnPopoverAtt = btnPop.getAttribute(`${this.prefix}-info-btn`);
}
//hide popover after an amount of time
popoverCount++;
const currCount = popoverCount;
setTimeout(() => {
//if another click on same infoBtn, restart hidden
if (currCount === popoverCount) popover.classList.add("hidden");
}, 3000);
if (e.target.closest("svg").hasAttribute(`popover-btn`)) {
this.showPopover(e.target);
}
} catch (err) {}
});
window.addEventListener("pointerout", (e) => {
//POPOVER LOGIC
try {
if (e.target.closest("svg").hasAttribute(`popover-btn`)) {
this.hidePopover(e.target);
}
} catch (err) {}
});
}
showPopover(el) {
const btn = el.closest("svg");
//toggle curr popover
const popover = btn.parentElement.querySelector(`[popover-content]`);
popover.classList.remove("hidden");
}
hidePopover(el) {
const btn = el.closest("svg");
//toggle curr popover
const popover = btn.parentElement.querySelector(`[popover-content]`);
popover.classList.add("hidden");
}
}
class Tabs {
constructor(container, prefix) {
this.prefix = prefix;
this.container = container;
this.tabsContainer = document.querySelector(`${this.container}`);
//DESKTOP
this.desktopBtns = document.querySelectorAll(
`[${this.prefix}-tabs-desktop] button`
);
//MOBILE
this.mobileBtn = document.querySelector(`[${this.prefix}-mobile-select]`);
this.mobileBtnTxt = this.mobileBtn.querySelector(`span`);
this.mobileBtnSVG = document.querySelector(
`[${this.prefix}-mobile-chevron]`
);
this.mobileDropdown = document.querySelector(
`[${this.prefix}-mobile-dropdown]`
);
this.mobileDropdownEls = this.mobileDropdown.querySelectorAll(`button`);
this.mobileBtn.addEventListener(`click`, this.toggleDropdown.bind(this));
//FORM
this.settingContainers = document.querySelectorAll(`[${this.prefix}-item]`);
this.generalSettings = document.querySelector(
`[${this.prefix}-item='general']`
);
this.initTabs();
this.initDisplay();
constructor() {
this.init();
}
initTabs() {
//show first element
window.addEventListener("load", () => {
try {
document.querySelector("button[services-item-handler]").click();
document.querySelector("button[services-mobile-item-handler]").click();
} catch (err) {}
});
this.tabsContainer.addEventListener("click", (e) => {
//MOBILE TABS LOGIC
init() {
window.addEventListener("click", (e) => {
try {
if (
!e.target.hasAttribute(`${this.prefix}-mobile-info-btn`) &&
e.target.hasAttribute(`${this.prefix}-mobile-item-handler`)
e.target.closest("button").hasAttribute("tab-handler") ||
e.target.closest("button").hasAttribute("tab-handler-mobile")
) {
//change text to select btn
console.log("tab clicked");
//get needed data
const tab = e.target.closest("button");
const tabAtt = tab.getAttribute(`${this.prefix}-mobile-item-handler`);
this.mobileBtnTxt.textContent = tab.childNodes[0].textContent;
//reset all tabs style
this.resetMobTabStyle();
//highlight chosen one
this.highlightMobClicked(tab);
//show settings
this.showRightSetting(tabAtt);
//close dropdown
this.toggleDropdown();
}
} catch (err) {}
//DESKTOP TABS LOGIC
try {
if (
!e.target.hasAttribute(`${this.prefix}-info-btn`) &&
e.target.closest("button").hasAttribute(`${this.prefix}-item-handler`)
) {
const tab = e.target.closest("button");
const tabAtt = tab.getAttribute(`${this.prefix}-item-handler`);
//style
this.resetDeskStyle();
tab.classList.add("brightness-90", "z-10");
const tabAtt =
tab.getAttribute("tab-handler") ||
tab.getAttribute("tab-handler-mobile");
const container = tab.closest("div[service-content]");
// change style
this.resetTabsStyle(container);
this.highlightClicked(container, tabAtt);
//show content
this.showRightSetting(tabAtt);
this.hideAllSettings(container);
this.showSettingClicked(container, tabAtt);
//close dropdown and change btn textcontent on mobile
this.setDropBtnText(container, tabAtt);
this.closeDropdown(container);
}
} catch (err) {}
try {
if (e.target.closest("button").hasAttribute("tab-dropdown-btn")) {
const dropBtn = e.target.closest("button");
const container = dropBtn.closest("div[service-content]");
this.toggleDropdown(container);
}
} catch (err) {}
});
}
resetDeskStyle() {
this.desktopBtns.forEach((tab) => {
resetTabsStyle(container) {
//reset desktop style
const tabsDesktop = container.querySelectorAll("button[tab-handler]");
tabsDesktop.forEach((tab) => {
tab.classList.remove("brightness-90", "z-10");
});
}
resetMobTabStyle() {
this.mobileDropdownEls.forEach((tab) => {
//reset mobile style
const tabsMobile = container.querySelectorAll("button[tab-handler-mobile]");
tabsMobile.forEach((tab) => {
tab.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
tab.classList.remove(
"dark:bg-primary",
@ -147,45 +99,52 @@ class Tabs {
});
}
highlightMobClicked(tabEl) {
tabEl.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
tabEl.classList.remove("bg-white", "dark:bg-slate-700", "text-gray-700");
highlightClicked(container, tabAtt) {
//desktop case
const tabDesktop = container.querySelector(
`button[tab-handler='${tabAtt}']`
);
tabDesktop.classList.add("brightness-90", "z-10");
//mobile case
const tabMobile = container.querySelector(
`button[tab-handler-mobile='${tabAtt}']`
);
tabMobile.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
tabMobile.classList.remove(
"bg-white",
"dark:bg-slate-700",
"text-gray-700"
);
}
initDisplay() {
//show general setting or
//first setting list if doesn't exist (like in services)
//on mobile and desktop
if (this.generalSettings === null) {
//desktop
document
.querySelector(
`[${this.prefix}-tabs-desktop] [${this.prefix}-item-handler]`
)
.click();
//mobile
document
.querySelector(
`[${this.prefix}-tabs-mobile] [${this.prefix}-mobile-item-handler]`
)
.click();
this.toggleDropdown();
}
}
showRightSetting(tabAtt) {
this.settingContainers.forEach((container) => {
if (container.getAttribute(`${this.prefix}-item`) === tabAtt)
container.classList.remove("hidden");
if (container.getAttribute(`${this.prefix}-item`) !== tabAtt)
container.classList.add("hidden");
hideAllSettings(container) {
const plugins = container.querySelectorAll("[plugin-item]");
plugins.forEach((plugin) => {
plugin.classList.add("hidden");
});
}
toggleDropdown() {
this.mobileDropdown.classList.toggle("hidden");
this.mobileDropdown.classList.toggle("flex");
this.mobileBtnSVG.classList.toggle("rotate-180");
showSettingClicked(container, tabAtt) {
const plugin = container.querySelector(`[plugin-item='${tabAtt}']`);
plugin.classList.remove("hidden");
}
setDropBtnText(container, tabAtt) {
const dropBtn = container.querySelector("[tab-dropdown-btn]");
dropBtn.querySelector("span").textContent = tabAtt;
}
closeDropdown(container) {
const dropdown = container.querySelector("[tab-dropdown]");
dropdown.classList.add("hidden");
dropdown.classList.remove("flex");
}
toggleDropdown(container) {
const dropdown = container.querySelector("[tab-dropdown]");
dropdown.classList.toggle("hidden");
dropdown.classList.toggle("flex");
}
}
@ -202,4 +161,86 @@ class FormatValue {
}
}
export { Popover, Tabs, FormatValue };
class FilterSettings {
constructor(inputID, container) {
this.input = document.querySelector(`input#${inputID}`);
//DESKTOP
this.container = document.querySelector(container);
this.deskTabs = this.container.querySelectorAll(`[tab-handler]`);
this.init();
}
init() {
this.input.addEventListener("input", () => {
this.resetFilter();
//get inp format
const inpValue = this.input.value.trim().toLowerCase();
//loop all tabs
this.deskTabs.forEach((tab) => {
//get settings of tabs except multiples
const settings = this.getSettingsFromTab(tab);
//compare total count to currCount to determine
//if tabs need to be hidden
const settingCount = settings.length;
let hiddenCount = 0;
settings.forEach((setting) => {
try {
const title = setting
.querySelector("h5")
.textContent.trim()
.toLowerCase();
if (!title.includes(inpValue)) {
setting.classList.add("hidden");
hiddenCount++;
}
} catch (err) {}
});
//case no setting match, hidden tab and content
if (settingCount === hiddenCount) {
const tabName = tab.getAttribute(`tab-handler`);
//hide mobile and desk tabs
tab.classList.add("hidden");
this.container
.querySelector(`[tab-handler-mobile="${tabName}"]`)
.classList.add("hidden");
this.container
.querySelector(`[plugin-item=${tabName}]`)
.querySelector("[setting-header]")
.classList.add("hidden");
}
});
});
}
resetFilter() {
this.deskTabs.forEach((tab) => {
const tabName = tab.getAttribute(`tab-handler`);
//hide mobile and desk tabs
tab.classList.remove("hidden");
this.container
.querySelector(`[tab-handler-mobile="${tabName}"]`)
.classList.remove("hidden");
this.container
.querySelector(`[plugin-item=${tabName}]`)
.querySelector("[setting-header]")
.classList.remove("hidden");
const settings = this.getSettingsFromTab(tab);
settings.forEach((setting) => {
setting.classList.remove("hidden");
});
});
}
getSettingsFromTab(tabEl) {
const tabName = tabEl.getAttribute(`tab-handler`);
const settingContainer = this.container
.querySelector(`[plugin-item="${tabName}"]`)
.querySelector(`[plugin-settings]`);
const settings = settingContainer.querySelectorAll("[setting-container]");
return settings;
}
}
export { Popover, Tabs, FormatValue, FilterSettings };

View File

@ -129,7 +129,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<!-- services folder -->
{% if child['type'] == "folder" and current_endpoint == "configs" and loop.depth != 1 %}
<svg class="absolute left-3 top-5 h-10 w-10 fill-primary dark:brightness-150" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM344 152c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24zm96-24c0 13.3-10.7 24-24 24s-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zM344 408c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24zm104-24c0 13.3-10.7 24-24 24s-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24z"/></svg>
<svg class="absolute left-3 top-5 h-10 w-10 fill-primary dark:brightness-150" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M64 32C28.7 32 0 60.7 0 96v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V96c0-35.3-28.7-64-64-64H64zM344 152c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24zm96-24c0 13.3-10.7 24-24 24s-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24zM64 288c-35.3 0-64 28.7-64 64v64c0 35.3 28.7 64 64 64H448c35.3 0 64-28.7 64-64V352c0-35.3-28.7-64-64-64H64zM344 408c-13.3 0-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24s-10.7 24-24 24zm104-24c0 13.3-10.7 24-24 24s-24-10.7-24-24s10.7-24 24-24s24 10.7 24 24z"/></svg>
{% endif %}
<!-- end services folder-->
<!-- services files -->
@ -137,7 +137,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<svg
class="absolute left-3 top-5 h-10 w-10 fill-primary dark:brightness-150"
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><!--! Font Awesome Pro 6.2.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2022 Fonticons, Inc. --><path d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z"/></svg>
xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512"><path d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z"/></svg>
{% endif %}
<!-- end services files-->
<!-- cache folder-->

View File

@ -1,59 +1,62 @@
{% extends "base.html" %} {% block content %} {% set global_config =
config["CONFIG"].get_config() %}
<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 {{current_endpoint}}-tabs-header class="flex justify-start 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 col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3">
<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>
<!-- form global conf -->
<form
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"
<div 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"
>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<!-- general container -->
{% include "settings_general.html" %}
<!-- end general container -->
<!-- plugin item -->
{% include "settings_plugins.html" %}
<!-- end plugin item -->
<!-- submit -->
<div class="flex w-full justify-center mt-8 mb-2">
<button
type="submit"
class="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 text-md ease-in shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md"
>
SAVE
</button>
<div {{current_endpoint}}-tabs-header class="flex justify-start 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 col-span-12 sm:col-span-6 lg:col-span-4 3xl:col-span-3">
<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>
<!-- end submit -->
</form>
<!--end form global conf -->
{% endblock %}
<!-- tabs -->
{% include "settings_tabs.html" %}
<!-- end tabs-->
</div>
<!-- form global conf -->
<form
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"
>
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
<!-- general container -->
{% include "settings_general.html" %}
<!-- end general container -->
<!-- plugin item -->
{% include "settings_plugins.html" %}
<!-- end plugin item -->
<!-- submit -->
<div class="flex w-full justify-center mt-8 mb-2">
<button
type="submit"
class="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 text-md ease-in shadow-xs bg-150 bg-x-25 hover:-translate-y-px active:opacity-85 hover:shadow-md"
>
SAVE
</button>
</div>
<!-- end submit -->
</form>
<!--end form global conf -->
</div>
</div>
{% endblock %}

View File

@ -144,7 +144,7 @@ url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
/>
<svg
checkbox-handler="live-update"
class="cursor-pointer absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
class="-z-10 cursor-pointer absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>

View File

@ -100,7 +100,7 @@
/>
<svg
checkbox-handler="newsletter-check"
class="cursor-pointer absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
class="-z-10 cursor-pointer absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>

View File

@ -1,5 +1,6 @@
<!-- modal -->
<div
service-content="settings"
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"
>
@ -62,6 +63,7 @@
<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" />
{% include "settings_plugins.html" %}
<!-- action button -->

View File

@ -4,7 +4,7 @@
{% set plugins = config["CONFIG"].get_plugins() %}
{% set global_config = config["CONFIG"].get_config() %}
<!-- general container -->
<div {{current_endpoint}}-item="general" class="w-full">
<div plugin-item="general" class="w-full">
<!-- general conf -->
<div class="col-span-12" setting-header>
<h5 class="transition duration-300 ease-in-out dark:opacity-90 ml-2 font-bold text-md uppercase dark:text-white mb-0">GENERAL</h5>
@ -13,7 +13,7 @@
</div>
</div>
<div
{{current_endpoint}}-settings
plugin-settings
class="w-full grid grid-cols-12"
id="edit-{{current_endpoint}}-general"
>
@ -28,7 +28,7 @@
<div class="flex items-center my-1 relative">
<h5 class="transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300">{{value["label"]}}</h5>
<svg
{{current_endpoint}}-info-btn="{{ value["label"] }}"
popover-btn="{{ value["label"] }}"
class="cursor-pointer fill-blue-500 h-5 w-5 ml-2 hover:brightness-75"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
@ -39,7 +39,7 @@
</svg>
<!-- popover -->
<div class="hidden transition z-50 rounded-md p-3 left-0 -translate-y-7 bottom-0 absolute bg-blue-500"
{{current_endpoint}}-info-popover="{{ value["label"] }}"
popover-content="{{ value["label"] }}"
>
<p class="transition duration-300 ease-in-out dark:opacity-90 font-bold text-sm text-white m-0" > {{value['help']}}
</p>
@ -74,7 +74,7 @@
<!-- select -->
{% if value["type"] == "select" %}
<!-- default hidden-->
<select id="{{setting}}" name="{{setting}}" {{current_endpoint}}-setting-select-default="{{value['id']}}" type="form-select" id="{{setting}}" name="{{setting}}"
<select id="{{setting}}" name="{{setting}}" select-default="{{value['id']}}" type="form-select" id="{{setting}}" name="{{setting}}"
class="hidden">
{% for item in value['select'] %}
<option value="{{item}}" {% if global_config[setting]['value'] and global_config[setting]['value'] == item or not global_config[setting]['value'] and value['default'] == item %} selected{% endif %}>{{item}}</option>
@ -83,22 +83,26 @@
<!-- end default hidden-->
<!--custom-->
<div class="relative">
<div select-container class="relative">
{% for item in value['select'] %}
{% if global_config[setting]['value'] and global_config[setting]['value'] == item %}
<button
default-value="{{value['default']}}"
{{current_endpoint}}-setting-select="{{value['id']}}"
setting-select="{{value['id']}}"
value="{{global_config[setting]['value']}}"
type="button"
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %} 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-primary 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 md:py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %} 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-primary 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 md:py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
>
{{global_config[setting]['value']}}
<span
setting-select-text="{{value['id']}}"
value="{{global_config[setting]['value']}}"
>{{global_config[setting]['value']}}</span
>
<!-- chevron -->
<svg
{{current_endpoint}}-setting-select="{{value['id']}}"
class="transition-transform h-4 w-4 fill-gray-500"
setting-select="{{value['id']}}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
@ -110,16 +114,20 @@
</button>
{% elif not global_config[setting]['value'] and value['default'] == item %}
<button
{{current_endpoint}}-setting-select="{{value['id']}}"
setting-select="{{value['id']}}"
value="{{value['default']}}"
default-value="{{value['default']}}"
type="button"
class="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 md:py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
>
{{value['default']}}
<!-- chevron -->
<span
setting-select-text="{{value['id']}}"
value="{{value['default']}}"
>{{value['default']}}</span
> <!-- chevron -->
<svg
{{current_endpoint}}-setting-select="{{value['id']}}"
class="transition-transform h-4 w-4 fill-gray-500"
setting-select="{{value['id']}}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
@ -134,15 +142,15 @@
<!-- dropdown-->
<div
{{current_endpoint}}-setting-select-dropdown="{{value['id']}}"
setting-select-dropdown="{{value['id']}}"
class="hidden z-[20] absolute h-full flex-col w-full mt-2 "
>
{% for item in value['select'] %}
{% if global_config[setting]['value'] and global_config[setting]['value'] == item or not global_config[setting]['value'] and value['default'] == item %}
<button type="button" value="{{item}}" {{current_endpoint}}-setting-select-dropdown-btn="{{value['id']}}" type="button" class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 bg-primary dark:bg-primary 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 text-gray-300"
<button type="button" value="{{item}}" setting-select-dropdown-btn="{{value['id']}}" type="button" class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 bg-primary dark:bg-primary 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 text-gray-300"
>{{item}}</button>
{% else %}
<button type="button" value="{{item}}" {{current_endpoint}}-setting-select-dropdown-btn="{{value['id']}}" type="button" class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} 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"
<button type="button" value="{{item}}" setting-select-dropdown-btn="{{value['id']}}" type="button" class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} 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"
>{{item}}</button>
{% endif %}
{% endfor %}
@ -156,16 +164,13 @@
<!-- checkbox -->
{% if value["type"] == "check" %}
<div checkbox-handler="{{value['id']}}" class="relative mb-7 md:mb-0">
<input id="{{setting}}" name="{{setting}}"
default-method="{{global_config[setting]['method']}}" default-value="{{value['default']}}"
{% if global_config[setting]['method'] != 'ui' or global_config[setting]['method'] != 'default' %} disabled {% endif %}
{% if global_config[setting]['value'] and global_config[setting]['value'] == 'yes' or not global_config[setting]['value'] and value['default'] == 'yes' %} checked {% endif %} id="checkbox-{{value['id']}}"
<input id="{{setting}}" name="{{setting}}" default-method="{{global_config[setting]['method']}}" default-value="{{value['default']}}" {% if global_config[setting]['method'] != 'ui' or global_config[setting]['method'] != 'default' %} disabled {% endif %} {% if global_config[setting]['value'] and global_config[setting]['value'] == 'yes' or not global_config[setting]['value'] and value['default'] == 'yes' %} checked {% endif %} id="checkbox-{{value['id']}}"
class="relative {% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} dark:border-slate-600 dark:bg-slate-700 z-10 checked:z-0 w-5 h-5 ease text-base rounded-1.4 checked:bg-primary checked:border-primary dark:checked:bg-primary dark:checked:border-primary duration-250 float-left mt-1 appearance-none border border-gray-300 bg-white bg-contain bg-center bg-no-repeat align-top transition-all disabled:bg-gray-400 disabled:border-gray-400 dark:disabled:bg-gray-800 dark:disabled:border-gray-800 disabled:text-gray-700 dark:disabled:text-gray-300"
type="checkbox"
pattern="{{value['regex']|safe}}"
value="{% if global_config[setting]['value'] %} {{global_config[setting]['value']}} {% else %} {{value['default']}} {% endif %}"
/>
<svg checkbox-handler="{{value['id']}}" class="{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3 " xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<svg checkbox-handler="{{value['id']}}" class="pointer-events-none {% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3 " xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M470.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L192 338.7 425.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"></path>
</svg>
</div>

View File

@ -27,7 +27,7 @@
{% for plugin in plugins %}
<div
{{current_endpoint}}-item="{{plugin['id']}}"
plugin-item="{{plugin['id']}}"
id="{{plugin['id']}}"
class="hidden w-full"
>
@ -45,8 +45,8 @@
</div>
</div>
<!-- end title and desc -->
<!-- plugin settings not multiple -->
<div {{current_endpoint}}-settings class="w-full grid grid-cols-12">
<div plugin-settings class="w-full grid grid-cols-12">
<!-- plugin settings not multiple -->
{% for setting, value in plugin["settings"].items() %}{% if current_endpoint
== "global-config" and value['context'] == "global" and not value['multiple'] or current_endpoint ==
"services" and value['context'] == "multisite" and not value['multiple'] %}
@ -62,7 +62,7 @@
{{value["label"]}}
</h5>
<svg
{{current_endpoint}}-info-btn="{{ value["label"] }}"
popover-btn="{{ value["label"] }}"
class="cursor-pointer fill-blue-500 h-5 w-5 ml-2 hover:brightness-75"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
@ -73,7 +73,7 @@
</svg>
<!-- popover -->
<div class="hidden transition z-50 rounded-md p-3 left-0 -translate-y-7 bottom-0 absolute bg-blue-500"
{{current_endpoint}}-info-popover="{{ value["label"] }}"
popover-content="{{ value["label"] }}"
>
<p class="transition duration-300 ease-in-out dark:opacity-90 font-bold text-sm text-white m-0" >{{value['help']}}
</p>
@ -110,7 +110,7 @@
{% if value["type"] == "select" %}
<!-- default hidden-->
<select default-method="{{global_config[setting]['method']}}" default-value="{{value['default']}}"
id="{{setting}}" name="{{setting}}" {{current_endpoint}}-setting-select-default="{{value['id']}}" type="form-select" id="{{setting}}" name="{{setting}}"
id="{{setting}}" name="{{setting}}" setting-select-default="{{value['id']}}" type="form-select" id="{{setting}}" name="{{setting}}"
class="hidden">
{% for item in value['select'] %}
<option value="{{item}}" {% if global_config[setting]['value'] and global_config[setting]['value'] == item or not global_config[setting]['value'] and value['default'] == item %} selected{% endif %}>{{item}}</option>
@ -119,9 +119,9 @@
<!-- end default hidden-->
<!--custom-->
<div class="relative">
<div select-container class="relative">
<button
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %} {{current_endpoint}}-setting-select="{{value['id']}}"
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %} setting-select="{{value['id']}}"
default-value="{{global_config[setting]['value']}}"
type="button"
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-primary 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 md:py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
@ -129,20 +129,20 @@
{% for item in value['select'] %} {% if global_config[setting]['value'] and
global_config[setting]['value'] == item %}
<span
{{current_endpoint}}-setting-select-text="{{value['id']}}"
setting-select-text="{{value['id']}}"
value="{{global_config[setting]['value']}}"
>{{global_config[setting]['value']}}</span
>
{% elif not global_config[setting]['value'] and value['default'] == item %}
<span
{{current_endpoint}}-setting-select-text="{{value['id']}}"
setting-select-text="{{value['id']}}"
value="{{value['default']}}"
>{{value['default']}}</span
>
{% endif %} {% endfor %}
<!-- chevron -->
<svg
{{current_endpoint}}-setting-select="{{value['id']}}"
setting-select="{{value['id']}}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
@ -155,7 +155,7 @@
</button>
<!-- dropdown-->
<div
{{current_endpoint}}-setting-select-dropdown="{{value['id']}}"
setting-select-dropdown="{{value['id']}}"
class="hidden z-[20] absolute h-full flex-col w-full mt-2"
>
{% for item in value['select'] %} {% if global_config[setting]['value'] and
@ -164,7 +164,7 @@
<button
type="button"
value="{{item}}"
{{current_endpoint}}-setting-select-dropdown-btn="{{value['id']}}"
setting-select-dropdown-btn="{{value['id']}}"
type="button"
class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} border-b border-l border-r border-gray-300 hover:brightness-90 bg-primary text-white 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"
>
@ -174,7 +174,7 @@
<button
type="button"
value="{{item}}"
{{current_endpoint}}-setting-select-dropdown-btn="{{value['id']}}"
setting-select-dropdown-btn="{{value['id']}}"
type="button"
class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} border-b border-l border-r border-gray-300 hover:bg-gray-100 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"
>
@ -184,9 +184,6 @@
</div>
<!-- end dropdown-->
</div>
<!-- end custom-->
{% endif %}
@ -200,10 +197,10 @@
!= 'default' %} disabled {% endif %} {% if global_config[setting]['value'] and
global_config[setting]['value'] == 'yes' or not
global_config[setting]['value'] and value['default'] == 'yes' %} checked {%
endif %} id="checkbox-{{value['id']}}" class="relative {% if
endif %} id="checkbox-{{value['id']}}" class="{% if
global_config[setting]['method'] != 'ui' and global_config[setting]['method']
!= 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %}
dark:border-slate-600 dark:bg-slate-700 z-10 checked:z-0 w-5 h-5 ease
relative dark:border-slate-600 dark:bg-slate-700 z-10 checked:z-0 w-5 h-5 ease
text-base rounded-1.4 checked:bg-primary checked:border-primary
dark:checked:bg-primary dark:checked:border-primary duration-250 float-left
mt-1 appearance-none border border-gray-300 bg-white bg-contain bg-center
@ -216,7 +213,7 @@
%}" />
<svg
checkbox-handler="{{value['id']}}"
class="{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
class="pointer-events-none {% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
@ -286,7 +283,7 @@
{{value["label"]}}
</h5>
<svg
{{current_endpoint}}-info-btn="{{ value["label"] }}"
popover-btn="{{ value["label"] }}"
class="cursor-pointer fill-blue-500 h-5 w-5 ml-2 hover:brightness-75"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
@ -297,7 +294,7 @@
</svg>
<!-- popover -->
<div class="hidden transition z-50 rounded-md p-3 left-0 -translate-y-7 bottom-0 absolute bg-blue-500"
{{current_endpoint}}-info-popover="{{ value["label"] }}"
popover-content="{{ value["label"] }}"
>
<p class="transition duration-300 ease-in-out dark:opacity-90 font-bold text-sm text-white m-0" >{{value['help']}}
</p>
@ -333,7 +330,7 @@
{% if value["type"] == "select" %}
<!-- default hidden-->
<select default-method="{{global_config[setting]['method']}}" default-value="{{value['default']}}"
id="{{setting}}_SCHEMA" name="{{setting}}_SCHEMA" {{current_endpoint}}-setting-select-default="{{value['id']}}" type="form-select" id="{{setting}}" name="{{setting}}"
id="{{setting}}_SCHEMA" name="{{setting}}_SCHEMA" select-default="{{value['id']}}" type="form-select" id="{{setting}}" name="{{setting}}"
class="hidden">
{% for item in value['select'] %}
<option value="{{item}}" {% if global_config[setting]['value'] and global_config[setting]['value'] == item or not global_config[setting]['value'] and value['default'] == item %} selected{% endif %}>{{item}}</option>
@ -342,30 +339,31 @@
<!-- end default hidden-->
<!--custom-->
<div class="relative">
<div select-container class="relative">
<button
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %} {{current_endpoint}}-setting-select="{{value['id']}}"
default-value="{{global_config[setting]['value']}}"
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %}
setting-select="{{value['id']}}"
default-value="{{global_config[setting]['value']}}"
type="button"
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-primary 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 md:py-2 font-normal text-gray-700 transition-all placeholder:text-gray-500"
>
{% for item in value['select'] %} {% if global_config[setting]['value'] and
global_config[setting]['value'] == item %}
<span
{{current_endpoint}}-setting-select-text="{{value['id']}}"
setting-select-text="{{value['id']}}"
value="{{global_config[setting]['value']}}"
>{{global_config[setting]['value']}}</span
>
{% elif not global_config[setting]['value'] and value['default'] == item %}
<span
{{current_endpoint}}-setting-select-text="{{value['id']}}"
setting-select-text="{{value['id']}}"
value="{{value['default']}}"
>{{value['default']}}</span
>
{% endif %} {% endfor %}
<!-- chevron -->
<svg
{{current_endpoint}}-setting-select="{{value['id']}}"
setting-select="{{value['id']}}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
@ -378,7 +376,7 @@
</button>
<!-- dropdown-->
<div
{{current_endpoint}}-setting-select-dropdown="{{value['id']}}"
setting-select-dropdown="{{value['id']}}"
class="hidden z-[20] absolute h-full flex-col w-full mt-2"
>
{% for item in value['select'] %} {% if global_config[setting]['value'] and
@ -387,7 +385,7 @@
<button
type="button"
value="{{item}}"
{{current_endpoint}}-setting-select-dropdown-btn="{{value['id']}}"
setting-select-dropdown-btn="{{value['id']}}"
type="button"
class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} border-b border-l border-r border-gray-300 hover:brightness-90 bg-primary text-white 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"
>
@ -397,7 +395,7 @@
<button
type="button"
value="{{item}}"
{{current_endpoint}}-setting-select-dropdown-btn="{{value['id']}}"
setting-select-dropdown-btn="{{value['id']}}"
type="button"
class="{% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} border-b border-l border-r border-gray-300 hover:bg-gray-100 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"
>
@ -436,7 +434,7 @@
%}" />
<svg
checkbox-handler="{{value['id']}}"
class="{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
class="pointer-events-none {% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} pointer-events-none {% else %} cursor-pointer {% endif %} absolute fill-white dark:fill-gray-300 left-0 top-0 translate-x-1 translate-y-2 h-3 w-3"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>

View File

@ -29,7 +29,7 @@
{% if current_endpoint != "services" %}
<!-- general tab -->
<button
{{current_endpoint}}-item-handler="general"
tab-handler="general"
type="button"
class="brightness-90 border-primary dark:hover:bg-slate-800 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 border my-1 relative inline-block 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"
>
@ -37,21 +37,20 @@
<!-- text and icon -->
<span class="text-primary transition duration-300 ease-in-out dark:opacity-90 pl-3 pr-2 dark:text-gray-300">general</span>
<svg
{{current_endpoint}}-info-btn="general"
class="fill-blue-500 h-5 w-5 mr-2 hover:brightness-95"
popover-btn="general"
class="z-20 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
{{current_endpoint}}-info-btn="general"
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 -->
<div
{{current_endpoint}}-info-popover="general"
class="hidden transition z-50 rounded-md p-3 left-0 absolute translate-y-6 bg-blue-500"
popover-content="general"
class="hidden transition z-50 rounded-md p-3 left-0 absolute translate-y-12 bg-blue-500"
>
<p class="font-bold text-sm text-white m-0">
General config : HTTP, DNS, LOG, API...
@ -67,29 +66,28 @@
and check_settings(plugin["settings"], "multisite") or current_endpoint == "global-config" and plugin["settings"]
and check_settings(plugin["settings"], "global") %}
<button
{{current_endpoint}}-item-handler="{{ plugin['id'] }}"
tab-handler="{{ plugin['id'] }}"
type="button"
class="z-1000 {% if current_endpoint == 'service' and loop.first %} brightness-90 {% endif %} border-primary dark:hover:bg-slate-800 dark:border-slate-600 dark:bg-slate-700 border my-1 relative inline-block 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"
>
<div clas="w-full flex justify-between items-center">
<div class="w-full flex justify-between items-center">
<!-- text and icon -->
<span class="text-primary transition duration-300 ease-in-out dark:opacity-90 pl-3 pr-2 dark:text-gray-300"> {{ plugin["name"] }} </span>
<svg
{{current_endpoint}}-info-btn="{{ plugin["name"] }}"
class=" fill-blue-500 h-5 w-5 mr-2 hover:brightness-95"
popover-btn="{{ plugin["name"] }}"
class=" z-20 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 {{current_endpoint}}-info-btn="{{ plugin["name"] }}"
<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 -->
<div
{{current_endpoint}}-info-popover="{{ plugin["name"] }}"
class="min-w-[150px] dark:brightness-90 bg-blue-500 hidden transition z-50 rounded-md p-3 left-0 absolute translate-y-6 "
popover-content="{{ plugin["name"] }}"
class="min-w-[150px] dark:brightness-90 bg-blue-500 hidden transition z-50 rounded-md p-3 left-0 absolute translate-y-12 "
>
<p class="font-bold text-sm text-white m-0">{{ plugin['description'] }}</p>
</div>
@ -101,16 +99,16 @@
</div>
<!-- end desktop tabs -->
<!-- mobile tabs -->
<div {{current_endpoint}}-tabs-mobile class="md:hidden relative col-span-12 h-full">
<div class="md:hidden relative col-span-12 h-full">
<button
{{current_endpoint}}-mobile-select
tab-dropdown-btn
type="button"
class="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"
>
<span class="transition duration-300 ease-in-out dark:opacity-90 dark:text-gray-300 text-primary">{% if current_endpoint == "global-config" %}general {% endif %} </span>
<!-- chevron -->
<svg
{{current_endpoint}}-mobile-chevron
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"
@ -123,37 +121,19 @@
</button>
<!-- dropdown-->
<div
{{current_endpoint}}-mobile-dropdown
tab-dropdown
class="hidden z-100 absolute flex-col w-full overflow-hidden overflow-y-auto max-h-90"
>
{% if current_endpoint != "services" %}
<!-- general button-->
<button
{{current_endpoint}}-mobile-item-handler="general"
tab-handler-mobile="general"
type="button"
data-select="false"
class="flex justify-between border-t rounded-t border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 bg-primary dark:bg-primary 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 text-gray-300" >
General
<svg
{{current_endpoint}}-info-btn="general"
class="fill-blue-500 h-5 w-5 mr-2 hover:brightness-75"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path {{current_endpoint}}-info-btn="general"
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>
<!-- popover -->
<div
{{current_endpoint}}-info-popover="general-mobile"
class="hidden transition z-50 rounded-md p-3 top-0 right-0 absolute -translate-y-12 bg-blue-500"
>
<p class="transition duration-300 ease-in-out dark:opacity-90 font-bold text-sm text-white m-0">General config : HTTP, DNS, LOG, API...</p>
</div>
<!-- end popover -->
</button>
<!-- end general button-->
{% endif %}
@ -162,31 +142,13 @@
and check_settings(plugin["settings"], "multisite") or current_endpoint == "global-config" and plugin["settings"]
and check_settings(plugin["settings"], "global") %}
<button
{{current_endpoint}}-mobile-item-handler="{{ plugin['id'] }}"
tab-handler-mobile="{{ plugin['id'] }}"
type="button"
data-select="false"
id="edit-{{current_endpoint}}-{{ plugin['id'] }}-tab"
class="flex justify-between {% if loop.index == 1 %} border-t rounded-t {% endif %} {% if loop.index == loop.length %}rounded-b {% endif %} 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">
{{ plugin["name"] }}
<svg
{{current_endpoint}}-info-btn="{{ plugin["name"] }}-mobile"
class="fill-blue-500 h-5 w-5 mr-2 hover:brightness-75"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512"
>
<path {{current_endpoint}}-info-btn="{{ plugin["name"] }}-mobile"
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>
<!-- popover -->
<div
{{current_endpoint}}-info-popover="{{ plugin["name"] }}-mobile"
class="hidden transition z-50 rounded-md p-3 top-0 right-0 absolute -translate-y-12 bg-blue-500"
>
<p class="transition duration-300 ease-in-out dark:opacity-90 font-bold text-sm text-white m-0">{{ plugin['description'] }}</p>
</div>
<!-- end popover -->
</button>
{% endif %} {% endfor %}
</div>