mirror of
https://github.com/bunkerity/bunkerweb
synced 2026-05-24 09:28:37 +00:00
update templates for simple mode
* move input types and some components on dedicated template in order to use them easily in both advanced / simple settings mode * add possibility to switch between advanced / simple mode with modal btn and attributes * start simple mode rendering logic
This commit is contained in:
parent
4da2693b1a
commit
2a11c1ccde
11 changed files with 546 additions and 471 deletions
File diff suppressed because one or more lines are too long
|
|
@ -17,16 +17,19 @@ class ServiceModal {
|
|||
"[data-services-tabs-select-header]",
|
||||
]);
|
||||
this.modalErrMsg = this.modal.querySelector(
|
||||
"[data-services-modal-error-msg]",
|
||||
"[data-services-modal-error-msg]"
|
||||
);
|
||||
this.modalCard = this.modal.querySelector("[data-services-modal-card]");
|
||||
this.switchModeBtn = this.modal.querySelector(
|
||||
"[data-toggle-settings-mode-btn]"
|
||||
);
|
||||
//modal forms
|
||||
this.formNewEdit = this.modal.querySelector("[data-services-modal-form]");
|
||||
this.formDelete = this.modal.querySelector(
|
||||
"[data-services-modal-form-delete]",
|
||||
"[data-services-modal-form-delete]"
|
||||
);
|
||||
this.submitBtn = document.querySelector(
|
||||
"button[data-services-modal-submit]",
|
||||
"button[data-services-modal-submit]"
|
||||
);
|
||||
//container
|
||||
this.container = document.querySelector("main");
|
||||
|
|
@ -88,6 +91,15 @@ class ServiceModal {
|
|||
} catch (err) {}
|
||||
});
|
||||
|
||||
this.switchModeBtn.addEventListener("click", () => {
|
||||
const currMode = this.switchModeBtn.getAttribute(
|
||||
"data-toggle-settings-mode-btn"
|
||||
);
|
||||
const switchMode = currMode === "advanced" ? "simple" : "advanced";
|
||||
|
||||
this.setSettingMode(switchMode);
|
||||
});
|
||||
|
||||
this.container.addEventListener("click", (e) => {
|
||||
//edit action
|
||||
try {
|
||||
|
|
@ -97,7 +109,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target,
|
||||
e.target
|
||||
);
|
||||
const oldServName = e.target
|
||||
.closest("[data-services-service]")
|
||||
|
|
@ -109,7 +121,7 @@ class ServiceModal {
|
|||
oldServName,
|
||||
this.formNewEdit,
|
||||
isDraft,
|
||||
method,
|
||||
method
|
||||
);
|
||||
//get service data and parse it
|
||||
//multiple type logic is launch at same time on relate class
|
||||
|
|
@ -133,7 +145,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target,
|
||||
e.target
|
||||
);
|
||||
this.setForm(
|
||||
action,
|
||||
|
|
@ -141,7 +153,7 @@ class ServiceModal {
|
|||
serviceName,
|
||||
this.formNewEdit,
|
||||
isDraft,
|
||||
method,
|
||||
method
|
||||
);
|
||||
//set default value with method default
|
||||
//get service data and parse it
|
||||
|
|
@ -173,7 +185,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target,
|
||||
e.target
|
||||
);
|
||||
this.setForm(
|
||||
action,
|
||||
|
|
@ -181,7 +193,7 @@ class ServiceModal {
|
|||
serviceName,
|
||||
this.formNewEdit,
|
||||
isDraft,
|
||||
method,
|
||||
method
|
||||
);
|
||||
//set default value with method default
|
||||
this.setSettingsDefault();
|
||||
|
|
@ -205,7 +217,7 @@ class ServiceModal {
|
|||
) {
|
||||
//set form info and right form
|
||||
const [action, serviceName, isDraft, method] = this.getActionData(
|
||||
e.target,
|
||||
e.target
|
||||
);
|
||||
this.setForm(
|
||||
action,
|
||||
|
|
@ -213,7 +225,7 @@ class ServiceModal {
|
|||
serviceName,
|
||||
this.formDelete,
|
||||
isDraft,
|
||||
method,
|
||||
method
|
||||
);
|
||||
//show modal
|
||||
this.openModal();
|
||||
|
|
@ -234,7 +246,7 @@ class ServiceModal {
|
|||
"delete-btn",
|
||||
"valid-btn",
|
||||
"edit-btn",
|
||||
"info-btn",
|
||||
"info-btn"
|
||||
);
|
||||
this.submitBtn.classList.add(btnType);
|
||||
}
|
||||
|
|
@ -296,15 +308,15 @@ class ServiceModal {
|
|||
//click the custom select dropdown to update select value
|
||||
select.parentElement
|
||||
.querySelector(
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`,
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`
|
||||
)
|
||||
.click();
|
||||
|
||||
//set state to custom visible el
|
||||
const btnCustom = document.querySelector(
|
||||
`[data-setting-select=${select.getAttribute(
|
||||
"data-setting-select-default",
|
||||
)}]`,
|
||||
"data-setting-select-default"
|
||||
)}]`
|
||||
);
|
||||
|
||||
this.setDisabledDefault(btnCustom, defaultMethod);
|
||||
|
|
@ -377,8 +389,9 @@ class ServiceModal {
|
|||
|
||||
if (action === "delete") {
|
||||
this.showDeleteForm();
|
||||
formEl.querySelector(`[data-services-modal-text]`).textContent =
|
||||
`Are you sure you want to delete ${serviceName} ?`;
|
||||
formEl.querySelector(
|
||||
`[data-services-modal-text]`
|
||||
).textContent = `Are you sure you want to delete ${serviceName} ?`;
|
||||
const nameInp = formEl.querySelector(`input[name="SERVER_NAME"]`);
|
||||
nameInp.setAttribute("value", serviceName);
|
||||
nameInp.value = serviceName;
|
||||
|
|
@ -391,7 +404,7 @@ class ServiceModal {
|
|||
disabledSaveCases() {
|
||||
window.addEventListener("DOMContentLoaded", () => {
|
||||
const serverNameInput = document.querySelector(
|
||||
'input[name="SERVER_NAME"]',
|
||||
'input[name="SERVER_NAME"]'
|
||||
);
|
||||
|
||||
window.addEventListener("click", (e) => {
|
||||
|
|
@ -409,6 +422,34 @@ class ServiceModal {
|
|||
});
|
||||
}
|
||||
|
||||
// Switch settings mode and update button
|
||||
setSettingMode(mode) {
|
||||
const elsToShow =
|
||||
mode === "advanced"
|
||||
? document.querySelectorAll("[data-advanced]")
|
||||
: document.querySelectorAll("[data-simple]");
|
||||
const elsToHide =
|
||||
mode === "advanced"
|
||||
? document.querySelectorAll("[data-simple]")
|
||||
: document.querySelectorAll("[data-advanced]");
|
||||
elsToHide.forEach((setting) => {
|
||||
setting.classList.add("!hidden");
|
||||
});
|
||||
elsToShow.forEach((setting) => {
|
||||
setting.classList.remove("!hidden");
|
||||
});
|
||||
// button
|
||||
this.switchModeBtn.setAttribute("data-toggle-settings-mode-btn", mode);
|
||||
const switchEls = this.switchModeBtn.querySelectorAll(
|
||||
"[data-toggle-settings-mode]"
|
||||
);
|
||||
switchEls.forEach((el) => {
|
||||
el.getAttribute("data-toggle-settings-mode") === mode
|
||||
? el.classList.remove("hidden")
|
||||
: el.classList.add("hidden");
|
||||
});
|
||||
}
|
||||
|
||||
checkServNameInput() {
|
||||
const serverNameInput = document.querySelector('input[name="SERVER_NAME"]');
|
||||
|
||||
|
|
@ -547,7 +588,7 @@ class ServiceModal {
|
|||
if (inp.tagName === "SELECT") {
|
||||
inp.parentElement
|
||||
.querySelector(
|
||||
`button[data-setting-select-dropdown-btn][value='${value}']`,
|
||||
`button[data-setting-select-dropdown-btn][value='${value}']`
|
||||
)
|
||||
.click();
|
||||
inp.setAttribute("data-method", method);
|
||||
|
|
@ -582,8 +623,10 @@ class ServiceModal {
|
|||
}
|
||||
|
||||
openModal() {
|
||||
//switch to first setting
|
||||
document.querySelector("button[data-tab-select-handler]").click();
|
||||
try {
|
||||
//switch to first setting
|
||||
document.querySelector("button[data-tab-select-handler]").click();
|
||||
} catch (e) {}
|
||||
//show modal el
|
||||
this.modal.classList.add("flex");
|
||||
this.modal.classList.remove("hidden");
|
||||
|
|
@ -659,7 +702,7 @@ class Multiple {
|
|||
const attName = btn.getAttribute(`data-${this.prefix}-multiple-add`);
|
||||
//get all multiple groups
|
||||
const multipleEls = document.querySelectorAll(
|
||||
`[data-${this.prefix}-settings-multiple*="${attName}"]`,
|
||||
`[data-${this.prefix}-settings-multiple*="${attName}"]`
|
||||
);
|
||||
//case no schema
|
||||
if (multipleEls.length <= 0) return;
|
||||
|
|
@ -671,7 +714,7 @@ class Multiple {
|
|||
//and keep the highest num
|
||||
multipleEls.forEach((container) => {
|
||||
const ctnrName = container.getAttribute(
|
||||
"data-services-settings-multiple",
|
||||
"data-services-settings-multiple"
|
||||
);
|
||||
const num = this.getSuffixNumOrFalse(ctnrName);
|
||||
if (!isNaN(num) && num > topNum) topNum = num;
|
||||
|
|
@ -682,7 +725,7 @@ class Multiple {
|
|||
const setNum = +currNum === 0 ? `` : `_${currNum}`;
|
||||
//the default (schema) group is the last group
|
||||
const schema = document.querySelector(
|
||||
`[data-${this.prefix}-settings-multiple="${attName}_SCHEMA"]`,
|
||||
`[data-${this.prefix}-settings-multiple="${attName}_SCHEMA"]`
|
||||
);
|
||||
//clone schema to create a group with new num
|
||||
const schemaClone = schema.cloneNode(true);
|
||||
|
|
@ -722,12 +765,12 @@ class Multiple {
|
|||
// We are not removing it really, just hiding it and update values to default
|
||||
// By setting default value, group will be send to server and delete (because a setting with default value is useless to keep)
|
||||
const multContainer = e.target.closest(
|
||||
`[data-${this.prefix}-settings-multiple]`,
|
||||
`[data-${this.prefix}-settings-multiple]`
|
||||
);
|
||||
multContainer.classList.add("hidden-multiple");
|
||||
// get setting container
|
||||
const settings = multContainer.querySelectorAll(
|
||||
`[data-setting-container]`,
|
||||
`[data-setting-container]`
|
||||
);
|
||||
settings.forEach((setting) => {
|
||||
// for regular input
|
||||
|
|
@ -754,7 +797,7 @@ class Multiple {
|
|||
// for select
|
||||
try {
|
||||
const selects = setting.querySelectorAll(
|
||||
"button[data-setting-select]",
|
||||
"button[data-setting-select]"
|
||||
);
|
||||
selects.forEach((select) => {
|
||||
const defaultVal = select.getAttribute("data-default") || "";
|
||||
|
|
@ -765,8 +808,8 @@ class Multiple {
|
|||
defaultVal;
|
||||
const dropdown = document.querySelector(
|
||||
`[data-setting-select-dropdown="${select.getAttribute(
|
||||
"data-setting-select",
|
||||
)}"]`,
|
||||
"data-setting-select"
|
||||
)}"]`
|
||||
);
|
||||
dropdown.querySelector(`button[value=${defaultVal}]`).click();
|
||||
});
|
||||
|
|
@ -791,13 +834,13 @@ class Multiple {
|
|||
? name.replace(`_${splitName[splitName.length - 1]}`, "").trim()
|
||||
: name.trim();
|
||||
const relateSetting = document.querySelector(
|
||||
`[data-setting-container=${nameSuffixLess}_SCHEMA]`,
|
||||
`[data-setting-container=${nameSuffixLess}_SCHEMA]`
|
||||
);
|
||||
const relateCtnr = relateSetting.closest(
|
||||
"[data-services-settings-multiple]",
|
||||
"[data-services-settings-multiple]"
|
||||
);
|
||||
const relateCtnrName = relateCtnr.getAttribute(
|
||||
"data-services-settings-multiple",
|
||||
"data-services-settings-multiple"
|
||||
);
|
||||
//then we sort the setting on the right container name by suffixe number
|
||||
if (!(relateCtnrName in sortMultiples)) {
|
||||
|
|
@ -815,7 +858,7 @@ class Multiple {
|
|||
addOneMultGroup() {
|
||||
const settings = document.querySelector("[data-services-modal-form]");
|
||||
const multAddBtns = settings.querySelectorAll(
|
||||
"[data-services-multiple-add]",
|
||||
"[data-services-multiple-add]"
|
||||
);
|
||||
multAddBtns.forEach((btn) => {
|
||||
//check if already one (SCHEMA exclude so length >= 2)
|
||||
|
|
@ -830,7 +873,7 @@ class Multiple {
|
|||
|
||||
showMultByAtt(att) {
|
||||
const multContainers = document.querySelectorAll(
|
||||
`[data-services-settings-multiple^=${att}]`,
|
||||
`[data-services-settings-multiple^=${att}]`
|
||||
);
|
||||
multContainers.forEach((container) => {
|
||||
if (
|
||||
|
|
@ -844,7 +887,7 @@ class Multiple {
|
|||
|
||||
toggleMultByAtt(att) {
|
||||
const multContainers = document.querySelectorAll(
|
||||
`[data-services-settings-multiple^=${att}]`,
|
||||
`[data-services-settings-multiple^=${att}]`
|
||||
);
|
||||
multContainers.forEach((container) => {
|
||||
if (
|
||||
|
|
@ -860,7 +903,7 @@ class Multiple {
|
|||
//get schema settings
|
||||
const multiples = {};
|
||||
const schemaSettings = document.querySelectorAll(
|
||||
`[data-setting-container$="SCHEMA"]`,
|
||||
`[data-setting-container$="SCHEMA"]`
|
||||
);
|
||||
// loop on every schema settings
|
||||
schemaSettings.forEach((schema) => {
|
||||
|
|
@ -886,11 +929,11 @@ class Multiple {
|
|||
setMultipleToDOM(sortMultObj, setMethodUI = false) {
|
||||
//we loop on each multiple that contains values to render to DOM
|
||||
for (const [schemaCtnrName, multGroupBySuffix] of Object.entries(
|
||||
sortMultObj,
|
||||
sortMultObj
|
||||
)) {
|
||||
//we need to access the DOM schema container
|
||||
const schemaCtnr = document.querySelector(
|
||||
`[data-services-settings-multiple="${schemaCtnrName}"]`,
|
||||
`[data-services-settings-multiple="${schemaCtnrName}"]`
|
||||
);
|
||||
//now we have to loop on each multiple settings group
|
||||
for (const [suffix, settings] of Object.entries(multGroupBySuffix)) {
|
||||
|
|
@ -906,14 +949,14 @@ class Multiple {
|
|||
for (const [name, data] of Object.entries(settings)) {
|
||||
//get setting container of clone container
|
||||
const settingContainer = schemaCtnrClone.querySelector(
|
||||
`[data-setting-container="${name}"]`,
|
||||
`[data-setting-container="${name}"]`
|
||||
);
|
||||
//replace input info and disabled state
|
||||
this.setSetting(
|
||||
data["value"],
|
||||
setMethodUI ? "ui" : data["method"],
|
||||
data["global"],
|
||||
settingContainer,
|
||||
settingContainer
|
||||
);
|
||||
}
|
||||
//send schema clone to DOM and show it
|
||||
|
|
@ -928,7 +971,7 @@ class Multiple {
|
|||
"data-services-settings-multiple",
|
||||
schemaCtnrClone
|
||||
.getAttribute("data-services-settings-multiple")
|
||||
.replace("_SCHEMA", suffix),
|
||||
.replace("_SCHEMA", suffix)
|
||||
);
|
||||
|
||||
//rename title
|
||||
|
|
@ -942,18 +985,18 @@ class Multiple {
|
|||
|
||||
//rename setting container
|
||||
const settingCtnrs = schemaCtnrClone.querySelectorAll(
|
||||
"[data-setting-container]",
|
||||
"[data-setting-container]"
|
||||
);
|
||||
settingCtnrs.forEach((settingCtnr) => {
|
||||
settingCtnr.setAttribute(
|
||||
"data-setting-container",
|
||||
settingCtnr
|
||||
.getAttribute("data-setting-container")
|
||||
.replace("_SCHEMA", suffix),
|
||||
.replace("_SCHEMA", suffix)
|
||||
);
|
||||
settingCtnr.setAttribute(
|
||||
"id",
|
||||
settingCtnr.getAttribute("id").replace("_SCHEMA", suffix),
|
||||
settingCtnr.getAttribute("id").replace("_SCHEMA", suffix)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -962,7 +1005,7 @@ class Multiple {
|
|||
labelEls.forEach((label) => {
|
||||
label.setAttribute(
|
||||
"for",
|
||||
label.getAttribute("for").replace("_SCHEMA", suffix),
|
||||
label.getAttribute("for").replace("_SCHEMA", suffix)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -971,19 +1014,19 @@ class Multiple {
|
|||
popoverBtns.forEach((popoverBtn) => {
|
||||
popoverBtn.setAttribute(
|
||||
"data-popover-btn",
|
||||
popoverBtn.getAttribute("data-popover-btn").replace("_SCHEMA", suffix),
|
||||
popoverBtn.getAttribute("data-popover-btn").replace("_SCHEMA", suffix)
|
||||
);
|
||||
});
|
||||
|
||||
const popoverContents = schemaCtnrClone.querySelectorAll(
|
||||
"[data-popover-content]",
|
||||
"[data-popover-content]"
|
||||
);
|
||||
popoverContents.forEach((popoverContent) => {
|
||||
popoverContent.setAttribute(
|
||||
"data-popover-content",
|
||||
popoverContent
|
||||
.getAttribute("data-popover-content")
|
||||
.replace("_SCHEMA", suffix),
|
||||
.replace("_SCHEMA", suffix)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -992,7 +1035,7 @@ class Multiple {
|
|||
invalidEls.forEach((invalidEl) => {
|
||||
invalidEl.setAttribute(
|
||||
"data-invalid",
|
||||
invalidEl.getAttribute("data-invalid").replace("_SCHEMA", suffix),
|
||||
invalidEl.getAttribute("data-invalid").replace("_SCHEMA", suffix)
|
||||
);
|
||||
});
|
||||
|
||||
|
|
@ -1070,15 +1113,15 @@ class Multiple {
|
|||
//click the custom select dropdown btn value to update select value
|
||||
select.parentElement
|
||||
.querySelector(
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`,
|
||||
`button[data-setting-select-dropdown-btn][value='${defaultVal}']`
|
||||
)
|
||||
.click();
|
||||
|
||||
//set state to custom visible el
|
||||
const btnCustom = document.querySelector(
|
||||
`[data-setting-select=${select.getAttribute(
|
||||
"data-setting-select-default",
|
||||
)}]`,
|
||||
"data-setting-select-default"
|
||||
)}]`
|
||||
);
|
||||
|
||||
this.setDisabledMultServ(btnCustom, method, global);
|
||||
|
|
@ -1114,10 +1157,10 @@ class Multiple {
|
|||
selects.forEach((select) => {
|
||||
const method = select.getAttribute("data-default-method");
|
||||
const name = select.getAttribute(
|
||||
"data-services-setting-select-default",
|
||||
"data-services-setting-select-default"
|
||||
);
|
||||
const selDOM = document.querySelector(
|
||||
`button[data-services-setting-select='${name}']`,
|
||||
`button[data-services-setting-select='${name}']`
|
||||
);
|
||||
if (method === "ui" || method === "default") {
|
||||
selDOM.removeAttribute("disabled", "");
|
||||
|
|
@ -1152,7 +1195,7 @@ class Multiple {
|
|||
hiddenIfNoMultiples() {
|
||||
//hide multiple btn if no multiple exist on a plugin
|
||||
const multiples = document.querySelectorAll(
|
||||
`[data-${this.prefix}-settings-multiple]`,
|
||||
`[data-${this.prefix}-settings-multiple]`
|
||||
);
|
||||
multiples.forEach((container) => {
|
||||
if (container.querySelectorAll(`[data-setting-container]`).length <= 0)
|
||||
|
|
@ -1164,7 +1207,7 @@ class Multiple {
|
|||
|
||||
removePrevMultiples() {
|
||||
const multiPlugins = document.querySelectorAll(
|
||||
`[data-${this.prefix}-settings-multiple]`,
|
||||
`[data-${this.prefix}-settings-multiple]`
|
||||
);
|
||||
multiPlugins.forEach((multiGrp) => {
|
||||
if (
|
||||
|
|
@ -1235,7 +1278,7 @@ class Dropdown {
|
|||
const btn = e.target.closest("button");
|
||||
const btnValue = btn.getAttribute("value");
|
||||
const btnSetting = btn.getAttribute(
|
||||
`data-${this.prefix}-setting-select-dropdown-btn`,
|
||||
`data-${this.prefix}-setting-select-dropdown-btn`
|
||||
);
|
||||
//stop if same value to avoid new fetching
|
||||
const isSameVal = this.isSameValue(btnSetting, btnValue);
|
||||
|
|
@ -1261,7 +1304,7 @@ class Dropdown {
|
|||
|
||||
closeAllDrop() {
|
||||
const drops = document.querySelectorAll(
|
||||
`[data-${this.prefix}-setting-select-dropdown]`,
|
||||
`[data-${this.prefix}-setting-select-dropdown]`
|
||||
);
|
||||
drops.forEach((drop) => {
|
||||
drop.classList.add("hidden");
|
||||
|
|
@ -1269,8 +1312,8 @@ class Dropdown {
|
|||
document
|
||||
.querySelector(
|
||||
`svg[data-${this.prefix}-setting-select="${drop.getAttribute(
|
||||
`data-${this.prefix}-setting-select-dropdown`,
|
||||
)}"]`,
|
||||
`data-${this.prefix}-setting-select-dropdown`
|
||||
)}"]`
|
||||
)
|
||||
.classList.remove("rotate-180");
|
||||
});
|
||||
|
|
@ -1278,7 +1321,7 @@ class Dropdown {
|
|||
|
||||
isSameValue(btnSetting, value) {
|
||||
const selectCustom = document.querySelector(
|
||||
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`,
|
||||
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`
|
||||
);
|
||||
const currVal = selectCustom.textContent;
|
||||
return currVal === value ? true : false;
|
||||
|
|
@ -1286,30 +1329,30 @@ class Dropdown {
|
|||
|
||||
setSelectNewValue(btnSetting, value) {
|
||||
const selectCustom = document.querySelector(
|
||||
`[data-${this.prefix}-setting-select="${btnSetting}"]`,
|
||||
`[data-${this.prefix}-setting-select="${btnSetting}"]`
|
||||
);
|
||||
selectCustom.querySelector(
|
||||
`[data-${this.prefix}-setting-select-text]`,
|
||||
`[data-${this.prefix}-setting-select-text]`
|
||||
).textContent = value;
|
||||
}
|
||||
|
||||
hideDropdown(btnSetting) {
|
||||
//hide dropdown
|
||||
const dropdownEl = document.querySelector(
|
||||
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
|
||||
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`
|
||||
);
|
||||
dropdownEl.classList.add("hidden");
|
||||
dropdownEl.classList.remove("flex");
|
||||
//svg effect
|
||||
const dropdownChevron = document.querySelector(
|
||||
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`,
|
||||
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`
|
||||
);
|
||||
dropdownChevron.classList.remove("rotate-180");
|
||||
}
|
||||
|
||||
changeDropBtnStyle(btnSetting, selectedBtn) {
|
||||
const dropdownEl = document.querySelector(
|
||||
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
|
||||
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`
|
||||
);
|
||||
//reset dropdown btns
|
||||
const btnEls = dropdownEl.querySelectorAll("button");
|
||||
|
|
@ -1319,7 +1362,7 @@ class Dropdown {
|
|||
"bg-primary",
|
||||
"dark:bg-primary",
|
||||
"text-gray-300",
|
||||
"text-gray-300",
|
||||
"text-gray-300"
|
||||
);
|
||||
btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
|
||||
});
|
||||
|
|
@ -1327,7 +1370,7 @@ class Dropdown {
|
|||
selectedBtn.classList.remove(
|
||||
"bg-white",
|
||||
"dark:bg-slate-700",
|
||||
"text-gray-700",
|
||||
"text-gray-700"
|
||||
);
|
||||
selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
|
||||
}
|
||||
|
|
@ -1338,10 +1381,10 @@ class Dropdown {
|
|||
.getAttribute(`data-${this.prefix}-setting-select`);
|
||||
//toggle dropdown
|
||||
const dropdownEl = document.querySelector(
|
||||
`[data-${this.prefix}-setting-select-dropdown="${attribute}"]`,
|
||||
`[data-${this.prefix}-setting-select-dropdown="${attribute}"]`
|
||||
);
|
||||
const dropdownChevron = document.querySelector(
|
||||
`svg[data-${this.prefix}-setting-select="${attribute}"]`,
|
||||
`svg[data-${this.prefix}-setting-select="${attribute}"]`
|
||||
);
|
||||
dropdownEl.classList.toggle("hidden");
|
||||
dropdownEl.classList.toggle("flex");
|
||||
|
|
@ -1397,7 +1440,7 @@ class Filter {
|
|||
setTimeout(() => {
|
||||
const value = document
|
||||
.querySelector(
|
||||
`[data-${this.prefix}-setting-select-text="state"]`,
|
||||
`[data-${this.prefix}-setting-select-text="state"]`
|
||||
)
|
||||
.textContent.trim()
|
||||
.toLowerCase();
|
||||
|
|
@ -1421,7 +1464,7 @@ class Filter {
|
|||
setTimeout(() => {
|
||||
const value = document
|
||||
.querySelector(
|
||||
`[data-${this.prefix}-setting-select-text="method"]`,
|
||||
`[data-${this.prefix}-setting-select-text="method"]`
|
||||
)
|
||||
.textContent.trim()
|
||||
.toLowerCase();
|
||||
|
|
@ -1498,7 +1541,7 @@ const setDropdown = new Dropdown();
|
|||
const setFilter = new Filter();
|
||||
const setTabsSelect = new TabsSelect(
|
||||
document.querySelector("[data-services-tabs-select]"),
|
||||
document.querySelector("[data-services-modal-form]"),
|
||||
document.querySelector("[data-services-modal-form]")
|
||||
);
|
||||
|
||||
const setPopover = new Popover();
|
||||
|
|
@ -1509,7 +1552,7 @@ const setFilterGlobal = new FilterSettings(
|
|||
"settings-filter",
|
||||
document.querySelector("[data-services-tabs-select]"),
|
||||
document.querySelector("[data-services-modal-form]"),
|
||||
"services",
|
||||
"services"
|
||||
);
|
||||
|
||||
const setMultiple = new Multiple("services");
|
||||
|
|
@ -1521,19 +1564,19 @@ const checkServiceModalKeyword = new CheckNoMatchFilter(
|
|||
.querySelector("[data-services-modal-form]")
|
||||
.querySelectorAll("[data-plugin-item]"),
|
||||
document.querySelector("[data-services-modal-form]"),
|
||||
document.querySelector("[data-services-nomatch]"),
|
||||
document.querySelector("[data-services-nomatch]")
|
||||
);
|
||||
|
||||
const checkServiceModalSelect = new CheckNoMatchFilter(
|
||||
document.querySelectorAll(
|
||||
"button[data-services-setting-select-dropdown-btn]",
|
||||
"button[data-services-setting-select-dropdown-btn]"
|
||||
),
|
||||
"select",
|
||||
document
|
||||
.querySelector("[data-services-modal-form]")
|
||||
.querySelectorAll("[data-plugin-item]"),
|
||||
document.querySelector("[data-services-modal-form]"),
|
||||
document.querySelector("[data-services-nomatch]"),
|
||||
document.querySelector("[data-services-nomatch]")
|
||||
);
|
||||
|
||||
try {
|
||||
|
|
@ -1542,16 +1585,16 @@ try {
|
|||
"input",
|
||||
document.querySelectorAll("[data-services-card]"),
|
||||
false,
|
||||
document.querySelector("[data-services-nomatch-card]"),
|
||||
document.querySelector("[data-services-nomatch-card]")
|
||||
);
|
||||
|
||||
const checkServiceCardSelect = new CheckNoMatchFilter(
|
||||
document.querySelectorAll(
|
||||
"button[data-services-setting-select-dropdown-btn]",
|
||||
"button[data-services-setting-select-dropdown-btn]"
|
||||
),
|
||||
"select",
|
||||
document.querySelectorAll("[data-services-card]"),
|
||||
false,
|
||||
document.querySelector("[data-services-nomatch-card]"),
|
||||
document.querySelector("[data-services-nomatch-card]")
|
||||
);
|
||||
} catch (e) {}
|
||||
|
|
|
|||
14
src/ui/templates/services_modal.html
vendored
14
src/ui/templates/services_modal.html
vendored
|
|
@ -31,6 +31,19 @@
|
|||
<path d="M21.721 12.752a9.711 9.711 0 0 0-.945-5.003 12.754 12.754 0 0 1-4.339 2.708 18.991 18.991 0 0 1-.214 4.772 17.165 17.165 0 0 0 5.498-2.477ZM14.634 15.55a17.324 17.324 0 0 0 .332-4.647c-.952.227-1.945.347-2.966.347-1.021 0-2.014-.12-2.966-.347a17.515 17.515 0 0 0 .332 4.647 17.385 17.385 0 0 0 5.268 0ZM9.772 17.119a18.963 18.963 0 0 0 4.456 0A17.182 17.182 0 0 1 12 21.724a17.18 17.18 0 0 1-2.228-4.605ZM7.777 15.23a18.87 18.87 0 0 1-.214-4.774 12.753 12.753 0 0 1-4.34-2.708 9.711 9.711 0 0 0-.944 5.004 17.165 17.165 0 0 0 5.498 2.477ZM21.356 14.752a9.765 9.765 0 0 1-7.478 6.817 18.64 18.64 0 0 0 1.988-4.718 18.627 18.627 0 0 0 5.49-2.098ZM2.644 14.752c1.682.971 3.53 1.688 5.49 2.099a18.64 18.64 0 0 0 1.988 4.718 9.765 9.765 0 0 1-7.478-6.816ZM13.878 2.43a9.755 9.755 0 0 1 6.116 3.986 11.267 11.267 0 0 1-3.746 2.504 18.63 18.63 0 0 0-2.37-6.49ZM12 2.276a17.152 17.152 0 0 1 2.805 7.121c-.897.23-1.837.353-2.805.353-.968 0-1.908-.122-2.805-.353A17.151 17.151 0 0 1 12 2.276ZM10.122 2.43a18.629 18.629 0 0 0-2.37 6.49 11.266 11.266 0 0 1-3.746-2.504 9.754 9.754 0 0 1 6.116-3.985Z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button data-toggle-settings-mode-btn="advanced"
|
||||
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-settings-mode="simple"
|
||||
class="hidden dark:text-gray-100 mb-0 mr-2 pointer-events-none">Simple</p>
|
||||
<p data-toggle-settings-mode="advanced"
|
||||
class="dark:text-gray-100 mb-0 mr-2 pointer-events-none">Advanced</p>
|
||||
<svg data-toggle-settings-mode="simple" class="hidden translate-y-0.5 w-5 h-5 fill-gray-700 stroke-white/0 dark:fill-gray-300 pointer-events-none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path fill-rule="evenodd" d="M7.84 1.804A1 1 0 0 1 8.82 1h2.36a1 1 0 0 1 .98.804l.331 1.652a6.993 6.993 0 0 1 1.929 1.115l1.598-.54a1 1 0 0 1 1.186.447l1.18 2.044a1 1 0 0 1-.205 1.251l-1.267 1.113a7.047 7.047 0 0 1 0 2.228l1.267 1.113a1 1 0 0 1 .206 1.25l-1.18 2.045a1 1 0 0 1-1.187.447l-1.598-.54a6.993 6.993 0 0 1-1.929 1.115l-.33 1.652a1 1 0 0 1-.98.804H8.82a1 1 0 0 1-.98-.804l-.331-1.652a6.993 6.993 0 0 1-1.929-1.115l-1.598.54a1 1 0 0 1-1.186-.447l-1.18-2.044a1 1 0 0 1 .205-1.251l1.267-1.114a7.05 7.05 0 0 1 0-2.227L1.821 7.773a1 1 0 0 1-.206-1.25l1.18-2.045a1 1 0 0 1 1.187-.447l1.598.54A6.992 6.992 0 0 1 7.51 3.456l.33-1.652ZM10 13a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
<svg data-toggle-settings-mode="advanced" class="translate-y-0.5 w-5 h-5 fill-gray-700 stroke-white/0 dark:fill-gray-300 pointer-events-none" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
|
||||
<path fill-rule="evenodd" d="M8.34 1.804A1 1 0 0 1 9.32 1h1.36a1 1 0 0 1 .98.804l.295 1.473c.497.144.971.342 1.416.587l1.25-.834a1 1 0 0 1 1.262.125l.962.962a1 1 0 0 1 .125 1.262l-.834 1.25c.245.445.443.919.587 1.416l1.473.294a1 1 0 0 1 .804.98v1.361a1 1 0 0 1-.804.98l-1.473.295a6.95 6.95 0 0 1-.587 1.416l.834 1.25a1 1 0 0 1-.125 1.262l-.962.962a1 1 0 0 1-1.262.125l-1.25-.834a6.953 6.953 0 0 1-1.416.587l-.294 1.473a1 1 0 0 1-.98.804H9.32a1 1 0 0 1-.98-.804l-.295-1.473a6.957 6.957 0 0 1-1.416-.587l-1.25.834a1 1 0 0 1-1.262-.125l-.962-.962a1 1 0 0 1-.125-1.262l.834-1.25a6.957 6.957 0 0 1-.587-1.416l-1.473-.294A1 1 0 0 1 1 10.68V9.32a1 1 0 0 1 .804-.98l1.473-.295c.144-.497.342-.971.587-1.416l-.834-1.25a1 1 0 0 1 .125-1.262l.962-.962A1 1 0 0 1 5.38 3.03l1.25.834a6.957 6.957 0 0 1 1.416-.587l.294-1.473ZM13 10a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<button class="-translate-y-1"
|
||||
aria-label="close modal"
|
||||
|
|
@ -44,6 +57,7 @@
|
|||
</div>
|
||||
|
||||
{% include "services_modal_settings_advanced.html" %}
|
||||
{% include "services_modal_settings_simple.html" %}
|
||||
{% include "services_modal_delete.html" %}
|
||||
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,17 +1,33 @@
|
|||
{% set steps = [
|
||||
{
|
||||
"name" : "STEP 1 - SERVER NAME",
|
||||
"description" : "We need this to...",
|
||||
"settings" : [
|
||||
{"plugin_id" : "general", "setting_id": "SERVER_NAME", "label" : "What is your host ?", "levels" : {"standard" : "", "advanced" : "", "high" : ""} },
|
||||
],
|
||||
"configs": [
|
||||
{"name": "my_config_1", "type": "modsec", "data": "..."},
|
||||
{"name": "my_config_2", "type": "server-http", "data": "..."}
|
||||
]
|
||||
},
|
||||
]
|
||||
%}
|
||||
|
||||
<div data-simple class="flex flex-col">
|
||||
<div class="flex flex-col w-full items-end gap-y-3 gap-x-4">
|
||||
<h2 data-simple-step></h2>
|
||||
<p data-simple-description></p>
|
||||
</div>
|
||||
<div class="w-full min-w-[300px] my-1 sm:my-0">
|
||||
<hr class="separator" />
|
||||
</div>
|
||||
</div>
|
||||
{# Get setting for each settings on steps #}
|
||||
|
||||
{% for step in steps %}
|
||||
{% for setting in step.get('settings') %}
|
||||
{% for plugin in plugins %}
|
||||
{% if setting["plugin_id"] == plugin["id"]%}
|
||||
{% if setting.update({"setting" : plugin["settings"][setting["setting_id"]]})%}{%endif%}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
{% endfor %}
|
||||
|
||||
<!-- new and edit form -->
|
||||
<form data-simple data-services-simple-modal-form
|
||||
class="w-full h-[90vh] overflow-auto flex flex-col justify-between"
|
||||
class="!hidden w-full h-[90vh] overflow-auto flex flex-col justify-between"
|
||||
id="form-simple-new"
|
||||
method="POST">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}" />
|
||||
|
|
@ -19,17 +35,30 @@
|
|||
<input type="hidden" value="" name="simple_OLD_SERVER_NAME" />
|
||||
<input type="hidden" value="no" name="simple_is_draft" />
|
||||
<input type="hidden" value="yes" name="simple_is_simple_mode" />
|
||||
{% for step in steps %}
|
||||
<div data-step="{{loop.index}}" class="flex flex-col">
|
||||
<div class="flex flex-col w-full items-start mt-2">
|
||||
<h2 class="ml-2 mr-1 text-xl transition duration-300 ease-in-out font-bold text-md uppercase dark:text-white/90 mb-0" data-simple-step>{{ step.get("name")}}</h2>
|
||||
<p class="ml-2 mr-1 text-sm dark:text-gray-300 mb-1" data-simple-description>{{ step.get("description")}}</p>
|
||||
</div>
|
||||
<div class="w-full min-w-[300px] my-1 sm:my-0">
|
||||
<hr class="separator" />
|
||||
</div>
|
||||
{% include "settings_simple.html" %}
|
||||
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
<!-- action button -->
|
||||
<div class="w-full flex-col items-center justify-center flex mt-10">
|
||||
<div class="flex justify-center">
|
||||
<button data-services-modal-close
|
||||
<button data-services-simple-modal-close
|
||||
type="button"
|
||||
class="dark:bg-slate-800 close-btn mb-4 mr-3 text-base">Close</button>
|
||||
<button data-services-modal-submit type="submit" class="mb-4 valid-btn">Save</button>
|
||||
<button data-services-simple-modal-submit type="submit" class="mb-4 valid-btn">Save</button>
|
||||
</div>
|
||||
<!-- end action button-->
|
||||
<p data-simple-services-modal-error-msg
|
||||
<p data-services-simple-modal-error-msg
|
||||
class="hidden text-red-500 font-bold dark:opacity-80 mb-0 text-center"></p>
|
||||
</div>
|
||||
</form>
|
||||
|
|
|
|||
29
src/ui/templates/setting_checkbox.html
vendored
Normal file
29
src/ui/templates/setting_checkbox.html
vendored
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{% if setting_input["type"] == "check" %}
|
||||
{% set inp_name = setting_input['name'] %}
|
||||
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
|
||||
{% set inp_id = setting_input['id'] %}
|
||||
|
||||
<div data-checkbox-handler="{{ inp_id }}"
|
||||
class="relative mb-7 md:mb-0 z-10 ">
|
||||
<label class="sr-only" for="{{ inp_name_mult }}">{{ inp_name }}</label>
|
||||
<input id="{{ inp_name_mult }}"
|
||||
name="{{ inp_name_mult }}"
|
||||
data-default-method="{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] %}mode{% else %}{{ global_config[setting]['method'] }}{% endif %}"
|
||||
data-default-value="{{ global_config[inp_name]['value'] }}"
|
||||
{% if inp_name in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] or global_config[inp_name]['method'] != 'ui' and global_config[inp_name]['method'] != 'default' %} disabled {% endif %}
|
||||
data-checked="{% if global_config[inp_name]['value'] == "yes" %}true{% else %}false{% endif %}"
|
||||
checked
|
||||
id="checkbox-{{ inp_id }}"
|
||||
class="checkbox"
|
||||
type="checkbox"
|
||||
data-pattern="{{ setting_input['regex']|safe }}"
|
||||
value="{{ global_config[inp_name]['value'] }}" />
|
||||
<svg data-checkbox-handler="{{ inp_id }}"
|
||||
class="pointer-events-none 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 class="pointer-events-none" 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>
|
||||
{% endif %}
|
||||
49
src/ui/templates/setting_header.html
vendored
Normal file
49
src/ui/templates/setting_header.html
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
{% if setting_input %}
|
||||
{% set inp_name = setting_input['name'] %}
|
||||
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
|
||||
{% set inp_name_multisite = inp_name + "-multisite_SCHEMA" if setting_input["is_multiple"] else inp_name %}
|
||||
|
||||
{% set inp_label = setting_input['label'] %}
|
||||
{% set inp_help = setting_input['help'] %}
|
||||
{% set inp_context = setting_input['context'] %}
|
||||
<!-- title and info -->
|
||||
<div class="flex items-center my-1 relative z-10">
|
||||
<h5 class="input-title">{{ inp_label }}</h5>
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ inp_name_mult }}">
|
||||
<svg class="popover-settings-svg"
|
||||
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>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ inp_name_mult }}">
|
||||
<p class="popover-settings-text">{{ inp_help }}</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
</div>
|
||||
<!-- end title and info -->
|
||||
{% if inp_context == "multisite" and current_endpoint == "global-config" %}
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ inp_name_multisite }}">
|
||||
<svg class="popover-settings-svg-multiple"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ inp_name_multisite }}">
|
||||
<p class="popover-settings-text">Multisite (apply as default to services without specific value).</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
44
src/ui/templates/setting_input.html
vendored
Normal file
44
src/ui/templates/setting_input.html
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
|
||||
{% if value["type"] != "select" and value["type"] != "check" %}
|
||||
{% set inp_name = setting_input['name'] %}
|
||||
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
|
||||
{% set inp_type = setting_input['type'] %}
|
||||
{% set inp_default = setting_input['default'] %}
|
||||
|
||||
<div class="relative flex items-center">
|
||||
<label class="sr-only" for="{{ inp_name_mult }}">{{ inp_name }}</label>
|
||||
<input {% if inp_name == "SERVER_NAME" %}required{% endif %}
|
||||
data-default-value="{{ global_config[inp_name]['value'] }}"
|
||||
data-default-method="{{ global_config[inp_name]['method'] }}"
|
||||
data-setting-input
|
||||
{% if global_config[inp_name]['method'] != 'ui' and global_config[inp_name]['method'] != 'default' %}disabled{% endif %}
|
||||
id="{{ inp_name_mult }}"
|
||||
name="{{ inp_name_mult }}"
|
||||
class="regular-input"
|
||||
value="{% if global_config[inp_name]['value'] %} {{ global_config[inp_name]['value'] }} {% else %} {{ inp_default }} {% endif %}"
|
||||
type="{{ inp_type }}"
|
||||
pattern="{{ value['regex']|safe }}" />
|
||||
{% if inp_type == "password" %}
|
||||
<div data-setting-password-container class="absolute flex right-2 h-5 w-5">
|
||||
<button type="button"
|
||||
data-setting-password="visible"
|
||||
class="h-5 w-5 flex items-center align-middle">
|
||||
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512">
|
||||
<path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button"
|
||||
data-setting-password="invisible"
|
||||
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle">
|
||||
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 640 512">
|
||||
<path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
18
src/ui/templates/setting_invalid.html
vendored
Normal file
18
src/ui/templates/setting_invalid.html
vendored
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{% if setting_input %}
|
||||
{% set inp_label = setting_input['label'] %}
|
||||
{% set inp_regex = setting_input['regex'] %}
|
||||
{% set inp_name = setting_input['name'] %}
|
||||
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
|
||||
<!-- invalid feedback -->
|
||||
<div role="alert"
|
||||
data-invalid="{{ inp_name_mult }}"
|
||||
aria-label="show when invalid input"
|
||||
class="block md:absolute hidden md:hidden text-sm text-red-500">
|
||||
Invalid value
|
||||
<span class="sr-only">
|
||||
{{ inp_label }} is invalid and must match this pattern:
|
||||
{{ inp_regex|safe }}
|
||||
</span>
|
||||
</div>
|
||||
<!--end invalid feedback-->
|
||||
{% endif %}
|
||||
84
src/ui/templates/setting_select.html
vendored
Normal file
84
src/ui/templates/setting_select.html
vendored
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
{% if value["type"] == "select" %}
|
||||
{% set inp_name = setting_input['name'] %}
|
||||
{% set inp_name_mult = inp_name + "_SCHEMA" if setting_input["is_multiple"] else inp_name %}
|
||||
{% set inp_default = setting_input['default'] %}
|
||||
{% set inp_items = setting_input['select'] %}
|
||||
{% set inp_id = setting_input['id'] %}
|
||||
|
||||
<!-- default hidden-->
|
||||
<select data-default-method="{{ global_config[inp_name]['method'] }}"
|
||||
data-default-value="{{ inp_default }}"
|
||||
id="{{ inp_name_mult }}"
|
||||
name="{{ inp_name_mult }}"
|
||||
data-setting-select-default="{{ inp_id }}"
|
||||
data-type="form-select"
|
||||
class="hidden">
|
||||
{% for item in inp_items %}
|
||||
<option {% if not item %}label="empty"{% endif %}
|
||||
value="{{ item }}"
|
||||
{% if global_config[inp_name]['value'] and global_config[inp_name]['value'] == item or not global_config[inp_name]['value'] and inp_default == item %} selected{% endif %}>
|
||||
{{ item }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<!-- end default hidden-->
|
||||
<!--custom-->
|
||||
<div data-select-container class="relative">
|
||||
<button {% if global_config[inp_name]['method'] != 'ui' and global_config[inp_name]['method'] != 'default' %}disabled{% endif %}
|
||||
data-setting-select="{{ inp_id }}"
|
||||
data-default-value="{{ global_config[inp_name]['value'] }}"
|
||||
data-default-method="{{ global_config[inp_name]['method'] }}"
|
||||
aria-controls="{{ value['id'] }}-dropdown"
|
||||
type="button"
|
||||
class="custom-select-btn">
|
||||
{% for item in inp_items %}
|
||||
{% if global_config[inp_name]['value'] and
|
||||
global_config[inp_name]['value'] == item %}
|
||||
<span data-setting-select-text="{{ inp_id }}"
|
||||
data-value="{{ global_config[inp_name]['value'] }}">{{ global_config[inp_name]['value'] }}</span>
|
||||
{% elif not global_config[inp_name]['value'] and inp_default == item %}
|
||||
<span aria-description="current value"
|
||||
data-setting-select-text="{{ inp_id }}"
|
||||
data-value="{{ inp_default }}">{{ inp_default }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- chevron -->
|
||||
<svg data-setting-select="{{ inp_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>
|
||||
<!-- end chevron -->
|
||||
</button>
|
||||
<!-- dropdown-->
|
||||
<div id="{{ inp_id }}-dropdown"
|
||||
role="listbox"
|
||||
data-setting-select-dropdown="{{ inp_id }}"
|
||||
class="hidden z-[20] fixed h-full flex-col mt-2 max-h-[200px] overflow-auto">
|
||||
{% for item in inp_items %}
|
||||
{% if global_config[inp_name]['value'] and
|
||||
global_config[inp_name]['value'] == item or not global_config[setting_input['name']]['value']
|
||||
and inp_default == item %}
|
||||
<button role="option"
|
||||
value="{{ item }}"
|
||||
data-setting-select-dropdown-btn="{{ inp_id }}"
|
||||
type="button"
|
||||
class="active custom-dropdown-btn {% if loop.index == 1 %}border-t rounded-t{% endif %} {% if loop.index == loop.length %}rounded-b{% endif %} ">
|
||||
{{ item }}
|
||||
</button>
|
||||
{% else %}
|
||||
<button role="option"
|
||||
value="{{ item }}"
|
||||
data-setting-select-dropdown-btn="{{ inp_id }}"
|
||||
type="button"
|
||||
class="custom-dropdown-btn {% if loop.index == 1 %}border-t rounded-t{% endif %} {% if loop.index == loop.length %}rounded-b{% endif %} ">
|
||||
{{ item }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end custom-->
|
||||
{% endif %}
|
||||
392
src/ui/templates/settings_plugins.html
vendored
392
src/ui/templates/settings_plugins.html
vendored
|
|
@ -52,6 +52,8 @@
|
|||
<div data-plugin-settings class="w-full grid grid-cols-12">
|
||||
<!-- plugin settings not multiple -->
|
||||
{% for setting, value in plugin["settings"].items() %}
|
||||
{% set setting_input = { "name" : setting, "context" : value.get("context"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : False} %}
|
||||
|
||||
{% if setting != "IS_DRAFT" and (current_endpoint == "global-config" and setting not in ["SERVER_NAME", "IS_LOADING"] or current_endpoint == "services" and value['context'] == "multisite") %}
|
||||
{% if value['multiple'] and value['multiple'] not in multList %}
|
||||
{% if multList.append(value['multiple']) %}{% endif %}
|
||||
|
|
@ -59,203 +61,12 @@
|
|||
{% if not value['multiple'] %}
|
||||
<div data-setting-container data-{{ current_endpoint }}-type="{{ plugin['type'] }}" data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-0 sm:mx-2 md:mx-3 lg:mx-4 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">
|
||||
<h5 class="input-title">{{ value["label"] }}</h5>
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ setting }}">
|
||||
<svg class="popover-settings-svg"
|
||||
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>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ setting }}">
|
||||
<p class="popover-settings-text">{{ value['help'] }}</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
{% if value["context"] == "multisite" and current_endpoint == "global-config" %}
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ setting }}-multiple">
|
||||
<svg class="popover-settings-svg-multiple"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ setting }}-multiple">
|
||||
<p class="popover-settings-text">Multisite (apply as default to services without specific value).</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- end title and info -->
|
||||
<!-- input -->
|
||||
{% if value["type"] != "select" and value["type"] != "check" %}
|
||||
<div class="relative flex items-center">
|
||||
<label class="sr-only" for="{{ setting }}">{{ setting }}</label>
|
||||
<input {% if setting == "SERVER_NAME" %}required{% endif %}
|
||||
data-default-value="{{ global_config[setting]['value'] }}"
|
||||
data-default-method="{{ global_config[setting]['method'] }}"
|
||||
{% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %}disabled{% endif %}
|
||||
id="{{ setting }}"
|
||||
data-setting-input
|
||||
name="{{ setting }}"
|
||||
class="regular-input"
|
||||
value="{% if global_config[setting]['value'] %} {{ global_config[setting]['value'] }} {% else %} {{ value['default'] }} {% endif %}"
|
||||
type="{{ value['type'] }}"
|
||||
pattern="{{ value['regex']|safe }}" />
|
||||
{% if value['type'] == "password" %}
|
||||
<div data-setting-password-container class="absolute flex right-2 h-5 w-5">
|
||||
<button type="button"
|
||||
data-setting-password="visible"
|
||||
class="h-5 w-5 flex items-center align-middle">
|
||||
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512">
|
||||
<path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button"
|
||||
data-setting-password="invisible"
|
||||
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle">
|
||||
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 640 512">
|
||||
<path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- end input -->
|
||||
<!-- select -->
|
||||
{% if value["type"] == "select" %}
|
||||
<!-- default hidden-->
|
||||
<select data-default-method="{{ global_config[setting]['method'] }}"
|
||||
data-default-value="{{ value['default'] }}"
|
||||
id="{{ setting }}"
|
||||
name="{{ setting }}"
|
||||
data-setting-select-default="{{ value['id'] }}"
|
||||
data-type="form-select"
|
||||
id="{{ setting }}"
|
||||
name="{{ setting }}"
|
||||
class="hidden">
|
||||
{% for item in value['select'] %}
|
||||
<option {% if not item %}label="empty"{% endif %}
|
||||
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>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<!-- end default hidden-->
|
||||
<!--custom-->
|
||||
<div data-select-container class="relative">
|
||||
<button {% if global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %}disabled{% endif %}
|
||||
data-setting-select="{{ value['id'] }}"
|
||||
data-default-value="{{ global_config[setting]['value'] }}"
|
||||
data-default-method="{{ global_config[setting]['method'] }}"
|
||||
aria-controls="{{ value['id'] }}-dropdown"
|
||||
type="button"
|
||||
class="custom-select-btn">
|
||||
{% for item in value['select'] %}
|
||||
{% if global_config[setting]['value'] and
|
||||
global_config[setting]['value'] == item %}
|
||||
<span data-setting-select-text="{{ value['id'] }}"
|
||||
data-value="{{ global_config[setting]['value'] }}">{{ global_config[setting]['value'] }}</span>
|
||||
{% elif not global_config[setting]['value'] and value['default'] == item %}
|
||||
<span aria-description="current value"
|
||||
data-setting-select-text="{{ value['id'] }}"
|
||||
data-value="{{ value['default'] }}">{{ value['default'] }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- chevron -->
|
||||
<svg data-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">
|
||||
<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>
|
||||
<!-- end chevron -->
|
||||
</button>
|
||||
<!-- dropdown-->
|
||||
<div id="{{ value['id'] }}-dropdown"
|
||||
role="listbox"
|
||||
data-setting-select-dropdown="{{ value['id'] }}"
|
||||
class="hidden z-[20] fixed h-full flex-col mt-2 max-h-[200px] overflow-auto">
|
||||
{% 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 role="option"
|
||||
value="{{ item }}"
|
||||
data-setting-select-dropdown-btn="{{ value['id'] }}"
|
||||
type="button"
|
||||
class="active custom-dropdown-btn {% if loop.index == 1 %}border-t rounded-t{% endif %} {% if loop.index == loop.length %}rounded-b{% endif %} ">
|
||||
{{ item }}
|
||||
</button>
|
||||
{% else %}
|
||||
<button role="option"
|
||||
value="{{ item }}"
|
||||
data-setting-select-dropdown-btn="{{ value['id'] }}"
|
||||
type="button"
|
||||
class="custom-dropdown-btn {% if loop.index == 1 %}border-t rounded-t{% endif %} {% if loop.index == loop.length %}rounded-b{% endif %} ">
|
||||
{{ item }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end custom-->
|
||||
{% endif %}
|
||||
<!-- checkbox -->
|
||||
{% if value["type"] == "check" %}
|
||||
<div data-checkbox-handler="{{ value['id'] }}"
|
||||
class="relative mb-7 md:mb-0 z-10 ">
|
||||
<label class="sr-only" for="{{ setting }}">{{ setting }}</label>
|
||||
<input id="{{ setting }}"
|
||||
name="{{ setting }}"
|
||||
data-default-method="{% if setting in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] %}mode{% else %}{{ global_config[setting]['method'] }}{% endif %}"
|
||||
data-default-value="{{ global_config[setting]['value'] }}"
|
||||
{% if setting in ['AUTOCONF_MODE', 'SWARM_MODE', 'KUBERNETES_MODE'] or global_config[setting]['method'] != 'ui' and global_config[setting]['method'] != 'default' %} disabled {% endif %}
|
||||
data-checked="{% if global_config[setting]['value'] == "yes" %}true{% else %}false{% endif %}"
|
||||
checked
|
||||
id="checkbox-{{ value['id'] }}"
|
||||
class="checkbox"
|
||||
type="checkbox"
|
||||
data-pattern="{{ value['regex']|safe }}"
|
||||
value="{{ global_config[setting]['value'] }}" />
|
||||
<svg data-checkbox-handler="{{ value['id'] }}"
|
||||
class="pointer-events-none 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 class="pointer-events-none" 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>
|
||||
{% endif %}
|
||||
<!-- end checkbox -->
|
||||
<!-- invalid feedback -->
|
||||
<div role="alert"
|
||||
data-invalid="{{ setting }}"
|
||||
aria-label="show when invalid input"
|
||||
class="block md:absolute hidden md:hidden text-sm text-red-500">
|
||||
Invalid value
|
||||
<span class="sr-only"> {{ value['label'] }} is invalid and must match this pattern:
|
||||
{{ value['regex']|safe }}</span>
|
||||
</div>
|
||||
<!--end invalid feedback-->
|
||||
{% include "setting_header.html" %}
|
||||
{% include "setting_input.html" %}
|
||||
{% include "setting_select.html" %}
|
||||
{% include "setting_checkbox.html" %}
|
||||
{% include "setting_invalid.html"%}
|
||||
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
|
@ -285,192 +96,19 @@
|
|||
<!-- multiple settings -->
|
||||
<div data-{{ current_endpoint }}-settings-multiple="{{ multiple }}_SCHEMA" class="bg-gray-50 dark:bg-slate-900/30 hidden w-full my-4 grid-cols-12 border dark:border-gray-700 rounded">
|
||||
{% for setting, value in plugin["settings"].items() %}
|
||||
{% set setting_input = { "name" : setting, "context" : value.get("context"), "help" : value.get("help"), "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : False} %}
|
||||
|
||||
{# render only setting that match the multiple id and context #}
|
||||
{% if value['multiple'] == multiple and (
|
||||
current_endpoint == "global-config"
|
||||
or current_endpoint == "services" and value['context'] == "multisite"
|
||||
) %}
|
||||
<div data-setting-container="{{ setting }}_SCHEMA" data-{{ current_endpoint }}-type="{{ plugin['type'] }}" data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative 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">
|
||||
<h5 class="input-title">{{ value["label"] }}</h5>
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ setting }}_SCHEMA">
|
||||
<svg class="popover-settings-svg"
|
||||
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>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ setting }}_SCHEMA">
|
||||
<p class="popover-settings-text">{{ value['help'] }}</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
{% if value["context"] == "multisite" and current_endpoint == "global-config" %}
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ setting }}-multiple_SCHEMA">
|
||||
<svg class="popover-settings-svg-multiple"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke-width="1.5"
|
||||
stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" d="M21.75 17.25v-.228a4.5 4.5 0 00-.12-1.03l-2.268-9.64a3.375 3.375 0 00-3.285-2.602H7.923a3.375 3.375 0 00-3.285 2.602l-2.268 9.64a4.5 4.5 0 00-.12 1.03v.228m19.5 0a3 3 0 01-3 3H5.25a3 3 0 01-3-3m19.5 0a3 3 0 00-3-3H5.25a3 3 0 00-3 3m16.5 0h.008v.008h-.008v-.008zm-3 0h.008v.008h-.008v-.008z" />
|
||||
</svg>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ setting }}-multiple_SCHEMA">
|
||||
<p class="popover-settings-text">Multisite (apply as default to services without specific value).</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
{% endif %}
|
||||
</div>
|
||||
<!-- end title and info -->
|
||||
<!-- input -->
|
||||
{% if value["type"] not in ["select", "check"] %}
|
||||
<div class="relative flex items-center">
|
||||
<label class="sr-only" for="{{ setting }}_SCHEMA">{{ setting }}</label>
|
||||
<input data-default-value="{{ value['default'] }}"
|
||||
data-default-method="default"
|
||||
data-setting-input
|
||||
id="{{ setting }}_SCHEMA"
|
||||
name="{{ setting }}_SCHEMA"
|
||||
class="regular-input"
|
||||
value="{{ value['default'] }}"
|
||||
type="{{ value['type'] }}"
|
||||
pattern="{{ value['regex']|safe }}" />
|
||||
{% if value['type'] == "password" %}
|
||||
<div data-setting-password-container class="absolute flex right-2 h-5 w-5">
|
||||
<button type="button"
|
||||
data-setting-password="visible"
|
||||
class="h-5 w-5 flex items-center align-middle">
|
||||
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 576 512">
|
||||
<path d="M288 32c-80.8 0-145.5 36.8-192.6 80.6C48.6 156 17.3 208 2.5 243.7c-3.3 7.9-3.3 16.7 0 24.6C17.3 304 48.6 356 95.4 399.4C142.5 443.2 207.2 480 288 480s145.5-36.8 192.6-80.6c46.8-43.5 78.1-95.4 93-131.1c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C433.5 68.8 368.8 32 288 32zM432 256c0 79.5-64.5 144-144 144s-144-64.5-144-144s64.5-144 144-144s144 64.5 144 144zM288 192c0 35.3-28.7 64-64 64c-11.5 0-22.3-3-31.6-8.4c-.2 2.8-.4 5.5-.4 8.4c0 53 43 96 96 96s96-43 96-96s-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6z" />
|
||||
</svg>
|
||||
</button>
|
||||
<button type="button"
|
||||
data-setting-password="invisible"
|
||||
class="hidden -translate-y-0.2 scale-110 h-5 w-5 items-center align-middle">
|
||||
<svg class="fill-primary pointer-events-none dark:fill-blue-500 hover:brightness-75 transition-all"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 640 512">
|
||||
<path d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zm223.1 298L373 389.9c-16.4 6.5-34.3 10.1-53 10.1c-79.5 0-144-64.5-144-144c0-6.9 .5-13.6 1.4-20.2L83.1 161.5C60.3 191.2 44 220.8 34.5 243.7c-3.3 7.9-3.3 16.7 0 24.6c14.9 35.7 46.2 87.7 93 131.1C174.5 443.2 239.2 480 320 480c47.8 0 89.9-12.9 126.2-32.5z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
<!-- end input -->
|
||||
<!-- select -->
|
||||
{% if value["type"] == "select" %}
|
||||
<!-- default hidden-->
|
||||
<select data-default-method="default"
|
||||
data-default-value="{{ value['default'] }}"
|
||||
id="{{ setting }}_SCHEMA"
|
||||
name="{{ setting }}_SCHEMA"
|
||||
data-select-default="{{ value['id'] }}"
|
||||
data-type="form-select"
|
||||
id="{{ setting }}"
|
||||
name="{{ setting }}"
|
||||
class="hidden">
|
||||
{% for item in value['select'] %}
|
||||
<option {% if not item %}label="empty"{% endif %}
|
||||
value="{{ item }}"
|
||||
{% if value['default'] == item %}selected{% endif %}>
|
||||
{{ item }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
<!-- end default hidden-->
|
||||
<!--custom-->
|
||||
<div data-select-container class="relative">
|
||||
<button data-setting-select="{{ value['id'] }}"
|
||||
data-default-value="{{ value['default'] }}"
|
||||
aria-controls="{{ value['id'] }}-dropdown"
|
||||
type="button"
|
||||
class="custom-select-btn">
|
||||
{% for item in value['select'] %}
|
||||
{% if value['default'] == item %}
|
||||
<span aria-description="current value"
|
||||
data-setting-select-text="{{ value['id'] }}"
|
||||
data-value="{{ value['default'] }}">{{ value['default'] }}</span>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- chevron -->
|
||||
<svg data-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">
|
||||
<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>
|
||||
<!-- end chevron -->
|
||||
</button>
|
||||
<!-- dropdown-->
|
||||
<div id="{{ value['id'] }}-dropdown"
|
||||
role="listbox"
|
||||
data-setting-select-dropdown="{{ value['id'] }}"
|
||||
class="hidden z-[20] fixed h-full flex-col mt-2 max-h-[200px] overflow-auto">
|
||||
{% for item in value['select'] %}
|
||||
{% if value['default'] == item %}
|
||||
<button role="option"
|
||||
value="{{ item }}"
|
||||
data-setting-select-dropdown-btn="{{ value['id'] }}"
|
||||
class="active custom-dropdown-btn {% if loop.index == 1 %}border-t rounded-t{% endif %} {% if loop.index == loop.length %}rounded-b{% endif %}">
|
||||
{{ item }}
|
||||
</button>
|
||||
{% else %}
|
||||
<button role="option"
|
||||
value="{{ item }}"
|
||||
data-setting-select-dropdown-btn="{{ value['id'] }}"
|
||||
class="custom-dropdown-btn {% if loop.index == 1 %}border-t rounded-t{% endif %} {% if loop.index == loop.length %}rounded-b{% endif %}">
|
||||
{{ item }}
|
||||
</button>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
<!-- end dropdown-->
|
||||
</div>
|
||||
<!-- end custom-->
|
||||
{% endif %}
|
||||
<!-- checkbox -->
|
||||
{% if value["type"] == "check" %}
|
||||
<div data-checkbox-handler="{{ value['id'] }}"
|
||||
class="relative mb-7 md:mb-0">
|
||||
<label class="sr-only" for="{{ setting }}_SCHEMA">{{ setting }}</label>
|
||||
<input id="{{ setting }}_SCHEMA" name="{{ setting }}_SCHEMA"
|
||||
data-default-method="default"
|
||||
data-default-value="{{ value['default'] }}" {% if value['default'] == 'yes' %} checked {%
|
||||
endif %} id="checkbox-{{ value['id'] }}"
|
||||
class="checkbox" type="checkbox" data-pattern="{{ value['regex']|safe }}"
|
||||
value="{{ value['default'] }}" />
|
||||
<svg data-checkbox-handler="{{ value['id'] }}"
|
||||
class="pointer-events-none 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>
|
||||
{% endif %}
|
||||
<!-- end checkbox -->
|
||||
<!-- invalid feedback -->
|
||||
<div role="alert"
|
||||
data-invalid="{{ setting }}_SCHEMA"
|
||||
aria-label="show when invalid input"
|
||||
class="block md:absolute hidden md:hidden text-sm text-red-500">
|
||||
Invalid value
|
||||
<span class="sr-only"> {{ value['label'] }} is invalid and must match this pattern:
|
||||
{{ value['regex']|safe }}</span>
|
||||
</div>
|
||||
<!--end invalid feedback-->
|
||||
{% include "setting_header.html" %}
|
||||
{% include "setting_input.html"%}
|
||||
{% include "setting_select.html"%}
|
||||
{% include "setting_checkbox.html"%}
|
||||
{% include "setting_invalid.html"%}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
|||
127
src/ui/templates/settings_simple.html
vendored
Normal file
127
src/ui/templates/settings_simple.html
vendored
Normal file
|
|
@ -0,0 +1,127 @@
|
|||
{% set current_endpoint = current_endpoint or url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
|
||||
<!-- plugin item -->
|
||||
|
||||
{% for plugin in plugin_simple %}
|
||||
<div data-simple id="{{ plugin['plugin_id'] }}-simple"
|
||||
class="w-full px-1">
|
||||
<!-- title and desc -->
|
||||
<div class="col-span-12" data-setting-header>
|
||||
<div class="flex justify-start items-center">
|
||||
<h5 class="my-2 transition duration-300 ease-in-out ml-2 mr-1 font-bold text-base uppercase dark:text-white/90 mb-0">
|
||||
{{ plugin['label'] }}
|
||||
</h5>
|
||||
</div>
|
||||
</div>
|
||||
{# get number of multiple groups for the plugin #}
|
||||
{% set multList = [] %}
|
||||
<!-- end title and desc -->
|
||||
<div class="w-full grid grid-cols-12">
|
||||
<!-- plugin settings not multiple -->
|
||||
{% for setting, value in plugin["setting"].items() %}
|
||||
{% set setting_input = { "name" : setting, "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : True} %}
|
||||
|
||||
{% if value['multiple'] and value['multiple'] not in multList %}
|
||||
{% if multList.append(value['multiple']) %}{% endif %}
|
||||
{% endif %}
|
||||
{% if not value['multiple'] %}
|
||||
<div data-setting-container data-{{ current_endpoint }}-type="{{ plugin['type'] }}" data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative mx-0 sm:mx-2 md:mx-3 lg:mx-4 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">
|
||||
<h5 class="input-title">{{ value["label"] }}</h5>
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ setting }}">
|
||||
<svg class="popover-settings-svg"
|
||||
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>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ setting }}">
|
||||
<p class="popover-settings-text">{{ value['help'] }}</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
</div>
|
||||
<!-- end title and info -->
|
||||
{% include "setting_input.html"%}
|
||||
{% include "setting_select.html"%}
|
||||
{% include "setting_checkbox.html"%}
|
||||
{% include "setting_invalid.html"%}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<!-- end plugin settings -->
|
||||
</div>
|
||||
<!-- end plugin settings not multiple -->
|
||||
{% if multList|length > 0 %}
|
||||
<h5 data-multiple-title
|
||||
class="transition duration-300 ease-in-out ml-2 font-bold text-[1.1rem] uppercase dark:text-white/90 mt-2 mb-0">
|
||||
multiple settings
|
||||
</h5>
|
||||
{% endif %}
|
||||
{% for multiple in multList %}
|
||||
<!-- plugin multiple handler -->
|
||||
<div data-multiple-handler="{{ multiple }}"
|
||||
class="flex items-center mx-2 mb-2 mt-5 col-span-12 ">
|
||||
<h5 class="input-title max-w-[150px] sm:max-w-[350px]">{{ multiple.replace('-', ' ').replace('_', ' ')|upper }}</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>
|
||||
<button data-{{ current_endpoint }}-multiple-toggle="{{ 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-sky-500 hover:bg-sky-500/80 focus:bg-sky-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">
|
||||
SHOW / HIDE
|
||||
</button>
|
||||
</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 my-4 grid-cols-12 border dark:border-gray-700 rounded">
|
||||
{% for setting, value in plugin["setting"].items() %}
|
||||
{% set setting_input = { "name" : setting, "label" : value.get("label"), "id" : value.get("id"), "type" : value.get("type"), "default" : value.get("default"), "select" : value.get("select"), "regex" : value.get("regex"), "is_multiple" : True} %}
|
||||
|
||||
{# render only setting that match the multiple id and context #}
|
||||
{% if value['multiple'] == multiple and (
|
||||
current_endpoint == "global-config"
|
||||
or current_endpoint == "services" and value['context'] == "multisite"
|
||||
) %}
|
||||
<div data-setting-container="{{ setting }}_SCHEMA" data-{{ current_endpoint }}-type="{{ plugin['type'] }}" data-{{ current_endpoint }}-context="{{ value['context'] }}" class="relative 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">
|
||||
<h5 class="input-title">{{ value["label"] }}</h5>
|
||||
<!-- popover -->
|
||||
<button type="button" data-popover-btn="{{ setting }}_SCHEMA">
|
||||
<svg class="popover-settings-svg"
|
||||
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>
|
||||
</button>
|
||||
<div role="alert"
|
||||
aria-description="show detail"
|
||||
class="popover-settings-container hidden"
|
||||
data-popover-content="{{ setting }}_SCHEMA">
|
||||
<p class="popover-settings-text">{{ value['help'] }}</p>
|
||||
</div>
|
||||
<!-- end popover -->
|
||||
</div>
|
||||
|
||||
{% include "setting_input.html"%}
|
||||
{% include "setting_select.html"%}
|
||||
{% include "setting_checkbox.html"%}
|
||||
{% include "setting_invalid.html"%}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<div data-multiple-delete-container
|
||||
class="col-span-12 flex justify-center my-4">
|
||||
<button data-{{ current_endpoint }}-multiple-delete="{{ plugin['name'] }}" 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-red-500 hover:bg-red-500/80 focus:bg-red-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">
|
||||
Remove
|
||||
</button>
|
||||
</div>
|
||||
<!-- end plugin settings -->
|
||||
</div>
|
||||
{% endfor %}
|
||||
<!-- end multiple settings -->
|
||||
</div>
|
||||
{% endfor %}
|
||||
<!-- end plugin item -->
|
||||
Loading…
Reference in a new issue