update configs page + fix select filter

* remove hardcode value by define value for filter select
* fix add file button enabled using breadcrumb on root
* add file manager configs filters : show only global conf, or show only path that lead to a conf
* handle new filters directly on file manager class if any
* add configs info using js script : get total configs and total global configs
This commit is contained in:
Jordan Blasenhauer 2024-02-27 11:44:08 +01:00
parent d0e73d73af
commit d1f8f84ad9
10 changed files with 629 additions and 230 deletions

File diff suppressed because one or more lines are too long

View file

@ -5,7 +5,270 @@ import {
FolderDropdown,
} from "./utils/file.manager.js";
class Dropdown {
constructor(prefix = "configs") {
this.prefix = prefix;
this.container = document.querySelector("main");
this.lastDrop = "";
this.initDropdown();
}
initDropdown() {
this.container.addEventListener("click", (e) => {
//SELECT BTN LOGIC
try {
if (
e.target
.closest("button")
.hasAttribute(`data-${this.prefix}-setting-select`) &&
!e.target.closest("button").hasAttribute(`disabled`)
) {
const btnName = e.target
.closest("button")
.getAttribute(`data-${this.prefix}-setting-select`);
if (this.lastDrop !== btnName) {
this.lastDrop = btnName;
this.closeAllDrop();
}
this.toggleSelectBtn(e);
}
} catch (err) {}
//SELECT DROPDOWN BTN LOGIC
try {
if (
e.target
.closest("button")
.hasAttribute(`data-${this.prefix}-setting-select-dropdown-btn`)
) {
const btn = e.target.closest("button");
const btnValue = btn.getAttribute("value");
const btnSetting = btn.getAttribute(
`data-${this.prefix}-setting-select-dropdown-btn`,
);
//stop if same value to avoid new fetching
const isSameVal = this.isSameValue(btnSetting, btnValue);
if (isSameVal) return this.hideDropdown(btnSetting);
//else, add new value to custom
this.setSelectNewValue(btnSetting, btnValue);
//close dropdown and change style
this.hideDropdown(btnSetting);
if (
!e.target.closest("button").hasAttribute(`data-${this.prefix}-file`)
) {
this.changeDropBtnStyle(btnSetting, btn);
}
//show / hide filter
if (btnSetting === "instances") {
this.hideFilterOnLocal(btn.getAttribute("data-_type"));
}
}
} catch (err) {}
});
}
closeAllDrop() {
const drops = document.querySelectorAll(
`[data-${this.prefix}-setting-select-dropdown]`,
);
drops.forEach((drop) => {
drop.classList.add("hidden");
drop.classList.remove("flex");
document
.querySelector(
`svg[data-${this.prefix}-setting-select="${drop.getAttribute(
`data-${this.prefix}-setting-select-dropdown`,
)}"]`,
)
.classList.remove("rotate-180");
});
}
isSameValue(btnSetting, value) {
const selectCustom = document.querySelector(
`[data-${this.prefix}-setting-select-text="${btnSetting}"]`,
);
const currVal = selectCustom.textContent;
return currVal === value ? true : false;
}
setSelectNewValue(btnSetting, value) {
const selectCustom = document.querySelector(
`[data-${this.prefix}-setting-select="${btnSetting}"]`,
);
selectCustom.querySelector(
`[data-${this.prefix}-setting-select-text]`,
).textContent = value;
}
hideDropdown(btnSetting) {
//hide dropdown
const dropdownEl = document.querySelector(
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
);
dropdownEl.classList.add("hidden");
dropdownEl.classList.remove("flex");
//svg effect
const dropdownChevron = document.querySelector(
`svg[data-${this.prefix}-setting-select="${btnSetting}"]`,
);
dropdownChevron.classList.remove("rotate-180");
}
changeDropBtnStyle(btnSetting, selectedBtn) {
const dropdownEl = document.querySelector(
`[data-${this.prefix}-setting-select-dropdown="${btnSetting}"]`,
);
//reset dropdown btns
const btnEls = dropdownEl.querySelectorAll("button");
btnEls.forEach((btn) => {
btn.classList.remove(
"bg-primary",
"dark:bg-primary",
"text-gray-300",
"text-gray-300",
);
btn.classList.add("bg-white", "dark:bg-slate-700", "text-gray-700");
});
//highlight clicked btn
selectedBtn.classList.remove(
"bg-white",
"dark:bg-slate-700",
"text-gray-700",
);
selectedBtn.classList.add("dark:bg-primary", "bg-primary", "text-gray-300");
}
toggleSelectBtn(e) {
const attribute = e.target
.closest("button")
.getAttribute(`data-${this.prefix}-setting-select`);
//toggle dropdown
const dropdownEl = document.querySelector(
`[data-${this.prefix}-setting-select-dropdown="${attribute}"]`,
);
const dropdownChevron = document.querySelector(
`svg[data-${this.prefix}-setting-select="${attribute}"]`,
);
dropdownEl.classList.toggle("hidden");
dropdownEl.classList.toggle("flex");
dropdownChevron.classList.toggle("rotate-180");
}
//hide date filter on local
hideFilterOnLocal(type) {
if (type === "local") {
this.hideInp(`input#from-date`);
this.hideInp(`input#to-date`);
}
if (type !== "local") {
this.showInp(`input#from-date`);
this.showInp(`input#to-date`);
}
}
showInp(selector) {
document.querySelector(selector).closest("div").classList.add("flex");
document.querySelector(selector).closest("div").classList.remove("hidden");
}
hideInp(selector) {
document.querySelector(selector).closest("div").classList.add("hidden");
document.querySelector(selector).closest("div").classList.remove("flex");
}
}
class Filter {
constructor(prefix = "configs") {
this.prefix = prefix;
this.container = document.querySelector(`[data-${this.prefix}-filter]`);
this.confPathOnlyValue = "false";
this.globalConfOnlyValue = "false";
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
this.setPathWithConfFilter();
this.initHandler();
});
}
setPathWithConfFilter() {
// get all .conf by path
const confs = document.querySelectorAll("[data-path*='.conf']");
// for each, get previous path and set attribute data-path-to-a-conf
confs.forEach((conf) => {
const separatePath = conf.getAttribute("data-path").split("/");
separatePath.shift();
for (let i = 0; i < separatePath.length; i++) {
// merge path to given index
const path = separatePath
.slice(0, i + 1)
.join("/")
.trim();
// get element with
const folder = document.querySelector(`[data-path="/${path}"]`);
if (!folder) continue;
// set attribute data-path-to-a-conf
folder.setAttribute("data-path-to-a-conf", "");
}
});
}
initHandler() {
this.container.addEventListener("click", (e) => {
try {
if (
e.target
.closest("button")
.getAttribute(`data-${this.prefix}-setting-select-dropdown-btn`) ===
"withconf" ||
e.target
.closest("button")
.getAttribute(`data-${this.prefix}-setting-select-dropdown-btn`) ===
"globalconf"
) {
setTimeout(() => {
// return to root to avoid conflict, filter logic is on file.manager.js
document
.querySelector('[data-configs-breadcrumb-item][data-level="0"]')
.querySelector("button")
.click();
}, 50);
}
} catch (err) {}
});
}
}
class ConfigsInfo {
constructor() {
this.init();
}
init() {
window.addEventListener("DOMContentLoaded", () => {
this.totalInfo = document.querySelector("[data-info-total-conf]");
this.globalInfo = document.querySelector("[data-info-global-conf]");
this.totalInfo.textContent = document
.querySelector("[data-configs-folders]")
.querySelectorAll("[data-_type='file']").length;
this.globalInfo.textContent = document
.querySelector("[data-configs-folders]")
.querySelectorAll("[data-_type='file'][data-level='2']").length;
});
}
}
const setConfigsInfo = new ConfigsInfo();
const setModal = new FolderModal("configs");
const setEditor = new FolderEditor();
const setFolderNav = new FolderNav("configs");
const setDropdown = new FolderDropdown("configs");
const setFilterDropdown = new Dropdown("configs");
const setFilter = new Filter();

View file

@ -93,10 +93,13 @@ class FolderNav {
//check if file/folder can be created on folder
updateActions(folder) {
// for root
if (!folder) return this.addFileEl.setAttribute("disabled", "");
//check if folder allow add file/folder
const isAddFile = folder.getAttribute("data-can-create-file");
console.log(isAddFile, "isAddFile")
isAddFile === "True" ? this.addFileEl.removeAttribute("disabled") : this.addFileEl.setAttribute("disabled", "");
const isAddFile = folder.getAttribute("data-can-create-file") || "False";
isAddFile === "True"
? this.addFileEl.removeAttribute("disabled")
: this.addFileEl.setAttribute("disabled", "");
}
showCurrentFolderEls(path, lvl) {
@ -105,6 +108,37 @@ class FolderNav {
);
for (let i = 0; i < nestedEl.length; i++) {
const el = nestedEl[i];
try {
// check filter before showing if any (case configs page)
const onlyPathWithConf = document
.querySelector('[data-configs-setting-select-text="withconf"]')
.textContent.trim()
.toLowerCase();
const onlyGlobalConf = document
.querySelector('[data-configs-setting-select-text="globalconf"]')
.textContent.trim()
.toLowerCase();
if (
onlyPathWithConf === "true" &&
!el.hasAttribute("data-path-to-a-conf")
) {
el.classList.add("hidden");
continue;
}
if (onlyGlobalConf === "true") {
const type = el.getAttribute("data-_type");
const level = el.getAttribute("data-level");
if (+level > 2) {
el.classList.add("hidden");
continue;
}
if (level === "2" && type === "folder") {
el.classList.add("hidden");
continue;
}
}
} catch (err) {}
el.setAttribute("data-current-el", "");
el.classList.remove("hidden");
}
@ -341,11 +375,14 @@ class FolderModal {
//HANDLERS
initAddConfig() {
if (this.prefix !== "configs") return;
this.addConfContainer.addEventListener("click", (e) => {
//add file
try {
if (
e.target.closest("button").hasAttribute(`data-${this.prefix}-add-file`)
e.target
.closest("button")
.hasAttribute(`data-${this.prefix}-add-file`)
) {
this.setModal(
"new",

View file

@ -118,7 +118,7 @@
<span aria-description="current filter state value"
id="bans-{{ filter['id'] }}"
data-name="bans-{{ filter['id'] }}"
data-bans-setting-select-text="{{ filter['id'] }}">all</span>
data-bans-setting-select-text="{{ filter['id'] }}">{{ filter['value']}}</span>
<!-- chevron -->
<svg data-bans-setting-select="{{ filter['id'] }}"
class="transition-transform h-4 w-4 fill-gray-500"

View file

@ -1,4 +1,112 @@
{% extends "base.html" %}
{% block content %}
{% set configs_info = [
{ "id" : "total-conf", "name" : "CONFIGS TOTAL", "data" : "unknown"},
{ "id" : "global-conf", "name" : "CONFIGS GLOBAL", "data" : "unknown"},
] %}
<div class="h-fit col-span-12 md:col-span-4 3xl:col-span-3 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<h5 class="mb-2 font-bold dark:text-white/90">INFO</h5>
{% for info in configs_info %}
<div class="mx-1 flex items-center my-4">
<p class="transition duration-300 ease-in-out font-bold mb-0 font-sans text-sm leading-normal uppercase dark:text-gray-500 dark:opacity-80">
{{ info['name'] }}
</p>
<p data-info-{{info['id']}} class="transition duration-300 ease-in-out pl-2 col-span-1 mb-0 font-sans text-sm font-semibold leading-normal uppercase dark:text-white dark:opacity-80">
{{ info['data'] }}
</p>
</div>
{% endfor %}
</div>
<!-- end info -->
<!-- filter -->
{% set filters = [
{
"type": "select",
"name": "Path with conf only",
"id": "withconf",
"value": "false",
"values": [
"false",
"true",
]
},
{
"type": "select",
"name": "Show global conf only",
"id": "globalconf",
"value": "false",
"values": [
"false",
"true"
]
},
] %}
<div data-configs-filter
class="h-fit col-span-12 md:col-span-8 lg:col-span-5 2xl:col-span-4 3xl:col-span-3 p-4 relative flex flex-col min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<h5 class="mb-2 font-bold dark:text-white/90">FILTER</h5>
<div class="mx-2 grid grid-cols-12 gap-x-4 gap-y-2">
{% for filter in filters %}
{% if filter['type'] == 'input' %}
<!-- search inpt-->
<div class="flex flex-col relative col-span-12">
<h5 class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300">
{{ filter['name'] }}
</h5>
<label for="{{ filter['id'] }}" class="sr-only">{{ filter['label'] }}</label>
<input type="text"
id="{{ filter['id'] }}"
name="{{ filter['id'] }}"
class="dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 disabled:opacity-75 focus:valid:border-green-500 focus:invalid:border-red-500 outline-none focus:border-primary text-sm leading-5.6 ease block w-full appearance-none rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-3 py-1 font-normal text-gray-700 transition-all placeholder:text-gray-500"
placeholder="{{ filter['placeholder'] }}"
pattern="{{ filter['pattern'] }}"
required />
</div>
<!-- end search inpt-->
{% endif %}
{% if filter['type'] == 'select' %}
<!-- select -->
<div class="flex flex-col relative col-span-12">
<h5 class="my-1 transition duration-300 ease-in-out dark:opacity-90 text-sm sm:text-md font-bold m-0 dark:text-gray-300">
{{ filter['name'] }}
</h5>
<button aria-controls="filter-{{ filter['id'] }}"
data-configs-setting-select="{{ filter['id'] }}"
class="disabled:opacity-75 dark:disabled:text-gray-300 disabled:text-gray-700 disabled:bg-gray-400 disabled:border-gray-400 dark:disabled:bg-gray-800 dark:disabled:border-gray-800 duration-300 ease-in-out dark:opacity-90 dark:border-slate-600 dark:bg-slate-700 dark:text-gray-300 focus:border-green-500 flex justify-between align-middle items-center text-left text-sm leading-5.6 ease w-full rounded-lg border border-solid border-gray-300 bg-white bg-clip-padding px-1.5 py-1 md:px-3 font-normal text-gray-700 transition-all placeholder:text-gray-500">
<span aria-description="current filter state value"
id="configs{{ filter['id'] }}"
data-name="configs{{ filter['id'] }}"
data-configs-setting-select-text="{{ filter['id'] }}">{{ filter['value']}}</span>
<!-- chevron -->
<svg data-configs-setting-select="{{ filter['id'] }}"
class="transition-transform h-4 w-4 fill-gray-500"
xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 512 512">
<path d="M233.4 406.6c12.5 12.5 32.8 12.5 45.3 0l192-192c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L256 338.7 86.6 169.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3l192 192z" />
</svg>
</button>
<!-- end chevron -->
<!-- dropdown-->
<div id="filter-{{ filter['id'] }}"
role="listbox"
data-configs-setting-select-dropdown="{{ filter['id'] }}"
class="hidden z-100 absolute h-full flex-col w-full translate-y-16">
{% for value in filter['values'] %}
<button role="option"
data-configs-setting-select-dropdown-btn="{{ filter['id'] }}"
value="{{ value }}"
class="{% if loop.first %}dark:bg-primary bg-primary text-gray-300 border-t rounded-t {% else %} bg-white {% endif %} {% if loop.last %}rounded-b{% endif %} border-b border-l border-r border-gray-300 dark:hover:brightness-90 hover:brightness-90 my-0 relative py-2 px-3 text-left align-middle transition-all rounded-none cursor-pointer leading-normal text-sm ease-in tracking-tight-rem dark:border-slate-600 dark:text-gray-300">
{{ value }}
</button>
{% endfor %}
</div>
<!-- end dropdown-->
</div>
<!-- end select -->
{% endif %}
{% endfor %}
</div>
</div>
<!-- end filter -->
{% include "file_manager.html" %}
{% endblock content %}

View file

@ -1,254 +1,245 @@
{% set current_endpoint = url_for(request.endpoint)[1:].split("/")[-1].strip().replace('_', '-') %}
<!-- main container -->
<div data-{{ current_endpoint }}-container class="dark:brightness-110 md:min-h-75-screen col-span-12 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div class="w-full grid-cols-12 grid">
<div class="col-span-12 md:col-span-8">
<h5 class="mb-2 font-bold dark:text-white/90">
{% if current_endpoint == "configs" %}
SERVICE MANAGER
{% else %}
FILE
MANAGER
{% endif %}
</h5>
<!--breadcrumb -->
<ul data-{{ current_endpoint }}-breadcrumb class="flex flex-wrap bg-transparent rounded-lg md:mb-8">
<li data-{{ current_endpoint }}-breadcrumb-item data-{{ current_endpoint }}-back class="mr-3 cursor-pointer text-sm capitalize leading-normal dark:text-gray-500 text-gray-600" aria-current="page">
<svg class="w-4.5 h-4.5 hover:brigthness-80"
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="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3" />
</svg>
</li>
<li data-{{ current_endpoint }}-breadcrumb-item data-level="0" data-path="{% if current_endpoint == "cache" %}/var/cache/bunkerweb{% elif current_endpoint == "configs" %}/etc/bunkerweb/{{ current_endpoint }}{% endif %}" data-name="{{ current_endpoint }}" class="leading-normal text-sm">
<button class="dark:text-gray-500 text-gray-600 after:float-right after:pl-2 after:text-gray-600 dark:after:text-gray-500 after:content-['/']">
{{ current_endpoint }}
</button>
</li>
</ul>
<!-- end breadcrumb -->
</div>
<!-- actions -->
<ul data-{{ current_endpoint }}-add-container class="col-span-12 md:col-span-4 mt-4 mb-6 md:mt-0 md:mb-3 w-full flex justify-center md:justify-end items-center">
<li data-{{ current_endpoint }}-add-folder class="rounded transition hidden flex-col items-center mx-2 p-2 md:py-4 md:px-6 relative cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-800">
<button>
<svg class="relative dark:brightness-125 h-8 w-8 lg:h-9 lg:w-9 fill-primary stroke-gray-100 dark:stroke-gray-600"
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="M5.25 14.25h13.5m-13.5 0a3 3 0 01-3-3m3 3a3 3 0 100 6h13.5a3 3 0 100-6m-16.5-3a3 3 0 013-3h13.5a3 3 0 013 3m-19.5 0a4.5 4.5 0 01.9-2.7L5.737 5.1a3.375 3.375 0 012.7-1.35h7.126c1.062 0 2.062.5 2.7 1.35l2.587 3.45a4.5 4.5 0 01.9 2.7m0 0a3 3 0 01-3 3m0 3h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008zm-3 6h.008v.008h-.008v-.008zm0-6h.008v.008h-.008v-.008z" />
</svg>
<span class="dark:text-gray-500 pt-1 mb-0 font-sans font-semibold leading-normal uppercase text-sm lg:text-md">
ADD SERVICE
</span>
</button>
</li>
<li data-{{ current_endpoint }}-add-file class="rounded transition hidden flex-col items-center mx-2 p-2 md:py-4 md:px-6 relative cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-800">
<button>
<svg class="-translate-x-1 relative dark:brightness-125 h-8 w-8 lg:h-9 lg:w-9 fill-primary stroke-gray-100 dark:stroke-gray-600"
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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
<span class="dark:text-gray-500 pt-1 mb-0 font-sans font-semibold leading-normal uppercase text-sm lg:text-md">
ADD FILE
</span>
</button>
</li>
</ul>
<!-- end actions -->
</div>
<!-- folders-->
<div data-{{ current_endpoint }}-folders class="grid grid-cols-12 gap-3">
{% for folder in folders %}
{% for child in folder['children'] recursive %}
{{ loop(child['children']) }}
<!-- folder -->
<div data-{{ current_endpoint }}-element="{{ child['name'] }}" data-name="{{ child['name'] }}" data-can-create-folder="{{ child['can_create_folders'] }}" data-can-create-file="{{ child['can_create_files'] }}" data-path="{{ child['path'] }}" data-level="{{ loop.depth }}" data-_type="{{ child['type'] }}" class="cursor-pointer {% if loop.depth != 1 %}hidden{% endif %} relative min-h-20 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-3 flex justify-center items-center h-full transition rounded bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800">
{% if child['content'] %}
<span data-value="{{ child['content'] }}" data-{{ current_endpoint }}-content class="hidden"></span>
<div data-{{ current_endpoint }}-container class="flex flex-col justify-between :brightness-110 md:min-h-75-screen col-span-12 p-4 relative min-w-0 break-words bg-white shadow-xl dark:bg-slate-850 dark:shadow-dark-xl rounded-2xl bg-clip-border">
<div class="max-h-[500px]">
<div class="w-full grid-cols-12 grid">
<div class="col-span-12 md:col-span-8">
<h5 class="mb-2 font-bold dark:text-white/90">
{% if current_endpoint == "configs" %}
SERVICE MANAGER
{% else %}
FILE
MANAGER
{% endif %}
<div class="w-full items-center grid grid-cols-12 pointer-events-none">
<!-- service root-->
{% if child['type'] == "folder" and current_endpoint == "configs" and loop.depth == 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</h5>
<!--breadcrumb -->
<ul data-{{ current_endpoint }}-breadcrumb class="flex flex-wrap bg-transparent rounded-lg md:mb-8">
<li data-{{ current_endpoint }}-breadcrumb-item data-{{ current_endpoint }}-back class="mr-3 cursor-pointer text-sm capitalize leading-normal dark:text-gray-500 text-gray-600" aria-current="page">
<svg class="w-4.5 h-4.5 hover:brigthness-80"
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="M9 15L3 9m0 0l6-6M3 9h12a6 6 0 010 12h-3" />
</svg>
</li>
<li data-{{ current_endpoint }}-breadcrumb-item data-level="0" data-path="{% if current_endpoint == "cache" %}/var/cache/bunkerweb{% elif current_endpoint == "configs" %}/etc/bunkerweb/{{ current_endpoint }}{% endif %}" data-name="{{ current_endpoint }}" class="leading-normal text-sm">
<button class="dark:text-gray-500 text-gray-600 after:float-right after:pl-2 after:text-gray-600 dark:after:text-gray-500 after:content-['/']">
{{ current_endpoint }}
</button>
</li>
</ul>
<!-- end breadcrumb -->
</div>
</div>
<!-- folders-->
<div data-{{ current_endpoint }}-folders class="grid grid-cols-12 gap-3">
{% for folder in folders %}
{% for child in folder['children'] recursive %}
{{ loop(child['children']) }}
<!-- folder -->
<div data-{{ current_endpoint }}-element="{{ child['name'] }}" data-name="{{ child['name'] }}" data-can-create-folder="{{ child['can_create_folders'] }}" data-can-create-file="{{ child['can_create_files'] }}" data-path="{{ child['path'] }}" data-level="{{ loop.depth }}" data-_type="{{ child['type'] }}" class="cursor-pointer {% if loop.depth != 1 %}hidden{% endif %} relative min-h-20 col-span-12 md:col-span-6 2xl:col-span-4 3xl:col-span-3 p-3 flex justify-center items-center h-full transition rounded bg-gray-100 hover:bg-gray-300 dark:bg-slate-700 dark:hover:bg-slate-800">
{% if child['content'] %}
<span data-value="{{ child['content'] }}" data-{{ current_endpoint }}-content class="hidden"></span>
{% endif %}
<!-- end service root-->
<!-- services folder -->
{% if child['type'] == "folder" and current_endpoint == "configs" and loop.depth != 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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>
{% endif %}
<!-- end services folder-->
<!-- services files -->
{% if child['type'] == "file" and current_endpoint == "configs" and loop.depth != 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
{% endif %}
<!-- end services files-->
<!-- cache folder-->
{% if child['type'] == "folder" and current_endpoint == "cache" and loop.depth == 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
{% endif %}
<!-- end cache folder -->
<!-- cache file -->
{% if child['type'] == "file" and current_endpoint == "cache" %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M15 13.5H9m4.06-7.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
</svg>
{% endif %}
<!-- end cache file -->
<span data-{{ current_endpoint }}-element-text class="col-span-10 pointer-events-none transition duration-300 ease-in-out dark:opacity-90 text-center mr-12 mb-0 text-sm xl:text-base text-slate-700 dark:text-gray-300">
{{ child['name'] }}
</span>
</div>
<div>
<!-- action button -->
<div data-{{ current_endpoint }}-action-button="{{ child['name'] }}" class="dark:brightness-125 dark:hover:brightness-100 flex justify-center items-center absolute h-full w-10 bg-primary fill-white first-letter:absolute top-0 -right-1 font-bold text-center text-white uppercase transition-all rounded-none rounded-r-lg cursor-pointer leading-normal text-xs ease-in tracking-tight-rem bg-150 bg-x-25 active:opacity-85">
<svg class="h-8 w-8 fill-white"
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="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
</svg>
</div>
<!-- end action button -->
</div>
<!-- dropdown actions -->
<div role="tablist" data-{{ current_endpoint }}-action-dropdown="{{ child['name'] }}" class="absolute hidden flex-col z-110 w-48 right-0 top-0 translate-y-16">
<!-- view button-->
<button role="tab" value="view" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-white 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 w-full border-t rounded-t border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-green-700 dark:brightness-125"
<div class="w-full items-center grid grid-cols-12 pointer-events-none">
<!-- service root-->
{% if child['type'] == "folder" and current_endpoint == "configs" and loop.depth == 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor">
<path d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
{% endif %}
<!-- end service root-->
<!-- services folder -->
{% if child['type'] == "folder" and current_endpoint == "configs" and loop.depth != 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
<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>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">view</span>
{% endif %}
<!-- end services folder-->
<!-- services files -->
{% if child['type'] == "file" and current_endpoint == "configs" and loop.depth != 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m2.25 0H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
{% endif %}
<!-- end services files-->
<!-- cache folder-->
{% if child['type'] == "folder" and current_endpoint == "cache" and loop.depth == 1 %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M20.25 7.5l-.625 10.632a2.25 2.25 0 01-2.247 2.118H6.622a2.25 2.25 0 01-2.247-2.118L3.75 7.5M10 11.25h4M3.375 7.5h17.25c.621 0 1.125-.504 1.125-1.125v-1.5c0-.621-.504-1.125-1.125-1.125H3.375c-.621 0-1.125.504-1.125 1.125v1.5c0 .621.504 1.125 1.125 1.125z" />
</svg>
{% endif %}
<!-- end cache folder -->
<!-- cache file -->
{% if child['type'] == "file" and current_endpoint == "cache" %}
<svg class="col-span-2 h-[2.5rem] w-[2.5rem] fill-primary stroke-gray-100 dark:stroke-gray-600 dark:brightness-150"
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="M15 13.5H9m4.06-7.19l-2.12-2.12a1.5 1.5 0 00-1.061-.44H4.5A2.25 2.25 0 002.25 6v12a2.25 2.25 0 002.25 2.25h15A2.25 2.25 0 0021.75 18V9a2.25 2.25 0 00-2.25-2.25h-5.379a1.5 1.5 0 01-1.06-.44z" />
</svg>
{% endif %}
<!-- end cache file -->
<span data-{{ current_endpoint }}-element-text class="col-span-10 pointer-events-none transition duration-300 ease-in-out dark:opacity-90 text-center mr-12 mb-0 text-sm xl:text-base text-slate-700 dark:text-gray-300">
{{ child['name'] }}
</span>
</button>
<!-- end view button-->
<!-- edit button -->
{% if child['type'] == "file" and child['can_edit'] == True or
child['type'] == "folder" and child['can_edit'] == True %}
<button role="tab" value="edit" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-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 w-full border-b border-l border-r hover:bg-gray-100">
</div>
<div>
<!-- action button -->
<div data-{{ current_endpoint }}-action-button="{{ child['name'] }}" class="dark:brightness-125 dark:hover:brightness-100 flex justify-center items-center absolute h-full w-10 bg-primary fill-white first-letter:absolute top-0 -right-1 font-bold text-center text-white uppercase transition-all rounded-none rounded-r-lg cursor-pointer leading-normal text-xs ease-in tracking-tight-rem bg-150 bg-x-25 active:opacity-85">
<svg class="h-8 w-8 fill-white"
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="M12 6.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 12.75a.75.75 0 110-1.5.75.75 0 010 1.5zM12 18.75a.75.75 0 110-1.5.75.75 0 010 1.5z" />
</svg>
</div>
<!-- end action button -->
</div>
<!-- dropdown actions -->
<div role="tablist" data-{{ current_endpoint }}-action-dropdown="{{ child['name'] }}" class="absolute hidden flex-col z-110 w-48 right-0 top-0 translate-y-16">
<!-- view button-->
<button role="tab" value="view" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-white 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 w-full border-t rounded-t border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-yellow-500"
<svg class="h-5.5 w-5.5 stroke-green-700 dark:brightness-125"
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="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
<path stroke-linecap="round" stroke-linejoin="round" d="M2.036 12.322a1.012 1.012 0 010-.639C3.423 7.51 7.36 4.5 12 4.5c4.638 0 8.573 3.007 9.963 7.178.07.207.07.431 0 .639C20.577 16.49 16.64 19.5 12 19.5c-4.638 0-8.573-3.007-9.963-7.178z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">edit</span>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">view</span>
</span>
</button>
{% endif %}
<!-- end edit button -->
<!-- download button -->
{% if child['type'] == "file" and child['can_download'] == True %}
{% if current_endpoint == "cache" %}
<button role="tab" value="download" data-{{ current_endpoint }}-download="{{ child['name'].split("/")[0] }}" data-{{ current_endpoint }}-file="{{ child['name'].split("/")[1] }}" data-{{ current_endpoint }}-setting-select-dropdown-btn="{{ child['name'].split("/")[0] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-white 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 w-full border-b border-l border-r hover:bg-gray-100">
<!-- end view button-->
<!-- edit button -->
{% if child['type'] == "file" and child['can_edit'] == True or
child['type'] == "folder" and child['can_edit'] == True %}
<button role="tab" value="edit" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-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 w-full border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-sky-500"
<svg class="h-5.5 w-5.5 stroke-yellow-500"
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="M9 12.75l3 3m0 0l3-3m-3 3v-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
<path stroke-linecap="round" stroke-linejoin="round" d="M16.862 4.487l1.687-1.688a1.875 1.875 0 112.652 2.652L10.582 16.07a4.5 4.5 0 01-1.897 1.13L6 18l.8-2.685a4.5 4.5 0 011.13-1.897l8.932-8.931zm0 0L19.5 7.125M18 14v4.75A2.25 2.25 0 0115.75 21H5.25A2.25 2.25 0 013 18.75V8.25A2.25 2.25 0 015.25 6H10" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">download</span>
</span>
</button>
{% else %}
<button role="tab" value="download" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-white 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 w-full border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-6 w-6 stroke-sky-500"
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="M9 12.75l3 3m0 0l3-3m-3 3v-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">download</span>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">edit</span>
</span>
</button>
{% endif %}
{% endif %}
<!-- end download button -->
<!-- delete button -->
{% if child['can_delete'] == True %}
<button role="tab" value="delete" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="bg-white duration-300 border-gray-300 hover:brightness-90 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 w-full border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-red-500"
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="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">delete</span>
</span>
</button>
{% endif %}
<!-- end delete button -->
<!-- end edit button -->
<!-- download button -->
{% if child['type'] == "file" and child['can_download'] == True %}
{% if current_endpoint == "cache" %}
<button role="tab" value="download" data-{{ current_endpoint }}-download="{{ child['name'].split("/")[0] }}" data-{{ current_endpoint }}-file="{{ child['name'].split("/")[1] }}" data-{{ current_endpoint }}-setting-select-dropdown-btn="{{ child['name'].split("/")[0] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-white 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 w-full border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-sky-500"
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="M9 12.75l3 3m0 0l3-3m-3 3v-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">download</span>
</span>
</button>
{% else %}
<button role="tab" value="download" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="duration-300 border-gray-300 hover:brightness-90 bg-white 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 w-full border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-6 w-6 stroke-sky-500"
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="M9 12.75l3 3m0 0l3-3m-3 3v-7.5M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">download</span>
</span>
</button>
{% endif %}
{% endif %}
<!-- end download button -->
<!-- delete button -->
{% if child['can_delete'] == True %}
<button role="tab" value="delete" data-{{ current_endpoint }}-action-dropdown-btn="{{ child['name'] }}" class="bg-white duration-300 border-gray-300 hover:brightness-90 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 w-full border-b border-l border-r hover:bg-gray-100">
<span class="flex justify-start items-center">
<svg class="h-5.5 w-5.5 stroke-red-500"
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="M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" />
</svg>
<span class="transition duration-300 ease-in-out text-gray-700 dark:text-gray-300 dark:opacity-80 ml-4 font-bold uppercase">delete</span>
</span>
</button>
{% endif %}
<!-- end delete button -->
</div>
<!-- end dropdown actions -->
</div>
<!-- end dropdown actions -->
</div>
<!-- end folder-->
{% endfor %}
</div>
{% endfor %}
<!-- end folders-->
<!-- end folder-->
{% endfor %}
</div>
{% endfor %}
<!-- end folders-->
</div>
{% if current_endpoint == "configs" %}
<!-- actions -->
<ul data-{{ current_endpoint }}-add-container class="col-span-12 md:col-span-4 mt-4 mb-6 md:mt-0 md:mb-3 w-full flex justify-center items-center">
<li class="rounded transition flex-col items-center mx-2 mt-4 relative cursor-pointer hover:bg-gray-100 dark:hover:bg-slate-800">
<button disabled class="file-manager-actions-item-btn"
data-{{ current_endpoint }}-add-file>
<svg class="-translate-x-1 relative dark:brightness-125 h-8 w-8 lg:h-9 lg:w-9 fill-primary stroke-gray-100 dark:stroke-gray-600"
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="M19.5 14.25v-2.625a3.375 3.375 0 00-3.375-3.375h-1.5A1.125 1.125 0 0113.5 7.125v-1.5a3.375 3.375 0 00-3.375-3.375H8.25m3.75 9v6m3-3H9m1.5-12H5.625c-.621 0-1.125.504-1.125 1.125v17.25c0 .621.504 1.125 1.125 1.125h12.75c.621 0 1.125-.504 1.125-1.125V11.25a9 9 0 00-9-9z" />
</svg>
<span class="dark:text-gray-500 pt-1 mb-0 font-sans font-semibold leading-normal uppercase text-sm lg:text-md">
ADD FILE
</span>
</button>
</li>
</ul>
<!-- end actions -->
{% endif %}
</div>
<!-- end main container -->
<!-- modal -->

View file

@ -99,7 +99,7 @@
<span aria-description="current filter state value"
id="jobs-{{ filter['id'] }}"
data-name="jobs-{{ filter['id'] }}"
data-jobs-setting-select-text="{{ filter['id'] }}">all</span>
data-jobs-setting-select-text="{{ filter['id'] }}">{{ filter['value']}}</span>
<!-- chevron -->
<svg data-jobs-setting-select="{{ filter['id'] }}"
class="transition-transform h-4 w-4 fill-gray-500"

View file

@ -116,7 +116,7 @@
<span aria-description="current filter state value"
id="plugins-{{ filter['id'] }}"
data-name="plugins-{{ filter['id'] }}"
data-plugins-setting-select-text="{{ filter['id'] }}">all</span>
data-plugins-setting-select-text="{{ filter['id'] }}">{{ filter['value'] }}</span>
<!-- chevron -->
<svg data-plugins-setting-select="{{ filter['id'] }}"
class="transition-transform h-4 w-4 fill-gray-500"

View file

@ -125,7 +125,7 @@
<span aria-description="current filter state value"
id="reports-{{ filter['id'] }}"
data-name="reports-{{ filter['id'] }}"
data-reports-setting-select-text="{{ filter['id'] }}">all</span>
data-reports-setting-select-text="{{ filter['id'] }}">{{ filter['value'] }}</span>
<!-- chevron -->
<svg data-reports-setting-select="{{ filter['id'] }}"
class="transition-transform h-4 w-4 fill-gray-500"

View file

@ -131,7 +131,7 @@
<span aria-description="current filter state value"
id="services-{{ filter['id'] }}"
data-name="services-{{ filter['id'] }}"
data-services-setting-select-text="{{ filter['id'] }}">all</span>
data-services-setting-select-text="{{ filter['id'] }}">{{ filter['value']}}</span>
<!-- chevron -->
<svg data-services-setting-select="{{ filter['id'] }}"
class="transition-transform h-4 w-4 fill-gray-500"