admin-redesign #26

Merged
eising merged 9 commits from admin-redesign into main 2025-06-19 05:24:06 +00:00
21 changed files with 641 additions and 267 deletions
Showing only changes of commit b4c395f0da - Show all commits

View File

@ -14,7 +14,7 @@
</sl-breadcrumb-item>
{% if breadcrumbs %}
{% for label, url in breadcrumbs %}
<sl-breadcrumb-item>
<sl-breadcrumb-item class="page-breadcrumb">
{% if url %}
<a href="{{url}}">{{label}}</a>
{% else %}

View File

@ -11,9 +11,8 @@
<div id="clientdetails" class="w-full">
<h3 class="mb-4 text-sm italic text-gray-400 dark:text-white">Click an item to view details</h3>
</div>
{% endblock %}
{% include '/clients/partials/drawer_create.html.j2' %}
{% endblock %}
{% block local_scripts %}
<script>

View File

@ -47,85 +47,70 @@
{% endmacro %}
{% extends "/dashboard/_base.html" %} {% block content %}
{% extends 'base/master-detail-email.html.j2' %}
<div class="p-4 bg-white block sm:flex items-center justify-between border-b border-gray-200 lg:mt-1.5 dark:bg-gray-800 dark:border-gray-700">
<div class="w-full mb-1">
<div class="mb-4">
<nav class="flex mb-5" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 text-sm font-medium md:space-x-2">
<li class="inline-flex items-center">
<a href="/" class="inline-flex items-center text-gray-700 hover:text-primary-600 dark:text-gray-300 dark:hover:text-white">
<svg class="w-5 h-5 mr-2.5" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"></path></svg>
Home
</a>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"></path></svg>
<span class="ml-1 text-gray-400 md:ml-2 dark:text-gray-500" aria-current="page">Secrets</span>
</div>
</li>
</ol>
</nav>
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Secrets</h1>
</div>
<div class="grid w-full grid-cols-1 gap-4 mt-4 xl:grid-cols-3">
<div class="p-4 bg-white border border-gray-200 rounded-lg shadow-sm sm:flex dark:border-gray-700 sm:p-6 dark:bg-gray-800" id="secret-tree">
{% block title %}Secrets{% endblock %}
<div class="flex flex-1 flex-col">
<div class="h-full w-full">
<sl-tree class="tree-with-icons">
<sl-tree-item
id="secret-group-root-item"
data-type="root"
data-name="root"
{% block master %}
{% if "/" in group_path_nodes %}
expanded=""
{% endif %}
{% if selected_group == "/"%}
selected=""
{% endif %}
>
<sl-icon name="folder"> </sl-icon>
<span class="px-2">Ungrouped</span>
{% for entry in groups.ungrouped %}
{{ display_entry(entry) }}
{% endfor %}
</sl-tree-item>
{% for child in groups.groups %}
{{ display_group(child) }}
{% endfor %}
</sl-tree>
</div>
</div>
<div class="flowbite-init-target">
<div class="tree-header grid grid-cols-2 place-content-between mb-6">
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Secrets</h1>
</div>
<div id="secret-tree">
<sl-tree class="tree-with-icons">
<sl-tree-item
id="secret-group-root-item"
data-type="root"
data-name="root"
</div>
<div class="2xl:col-span-2 xl:col-span-2 p-4 mb-4 bg-white border border-gray-200 rounded-lg shadow-sm 2xl:col-span-2 dark:border-gray-700 sm:p-6 dark:bg-gray-800">
{% if group_page | default(false) %}
<div class="w-full" id="secretdetails">
{% include '/secrets/partials/group_detail.html.j2' %}
</div>
{% elif root_group_page | default(false) %}
<div class="w-full" id="secretdetails">
{% include '/secrets/partials/edit_root.html.j2' %}
</div>
{% elif secret_page | default(false) %}
<div class="w-full" id="secretdetails">
{% include '/secrets/partials/tree_detail.html.j2' %}
</div>
{% else %}
{% include '/secrets/partials/default_detail.html.j2' %}
{% if "/" in group_path_nodes %}
expanded=""
{% endif %}
</div>
</div>
{% if selected_group == "/"%}
selected=""
{% endif %}
>
<sl-icon name="folder"> </sl-icon>
<span class="px-2">Ungrouped</span>
{% for entry in groups.ungrouped %}
{{ display_entry(entry) }}
{% endfor %}
</sl-tree-item>
{% for child in groups.groups %}
{{ display_group(child) }}
{% endfor %}
</sl-tree>
</div>
</div>
{% endblock %}
{% block detail %}
{% if group_page | default(false) %}
<div class="w-full" id="secretdetails">
{% include '/secrets/partials/group_detail.html.j2' %}
</div>
{% elif root_group_page | default(false) %}
<div class="w-full" id="secretdetails">
{% include '/secrets/partials/edit_root.html.j2' %}
</div>
{% elif secret_page | default(false) %}
<div class="w-full" id="secretdetails">
{% include '/secrets/partials/tree_detail.html.j2' %}
</div>
{% else %}
{% include '/secrets/partials/default_detail.html.j2' %}
{% endif %}
{% endblock %}
{% block local_scripts %}
<script>
{% include '/secrets/partials/tree_event.js' %}
</script>
{% endblock %}

View File

@ -1,4 +1,61 @@
document.addEventListener("DOMContentLoaded", () => {
function createCrumb(name, url = null) {
// Create a breadcrumb
const crumb = document.createElement("sl-breadcrumb-item");
crumb.classList.add("page-breadcrumb");
if (url) {
var crumbChild = document.createElement("a");
crumbChild.setAttribute("href", url);
const crumbChildText = document.createTextNode(name);
crumbChild.appendChild(crumbChildText);
} else {
var crumbChild = document.createTextNode(name);
}
crumb.appendChild(crumbChild);
return crumb;
}
function setGroupBreadcrumbs(name, path, secret = null) {
// Set breadcrumbs for a whole group.
const breadcrumbs = document.getElementById("breadcrumbs");
// First, remove all existing page breadcrumbs
console.log(`setGroupBreadcrumbs: ${name} ${path}`);
let pageCrumbs = document.getElementsByClassName("page-breadcrumb");
for (let i = 0; i < pageCrumbs.length; i++) {
breadcrumbs.removeChild(pageCrumbs[i]);
}
// Re-create the breadcrumbs
const newcrumbs = [
["Secrets", "/secrets/"],
["Groups", "/secrets/groups/"],
];
if (path) {
const pathnodes = path.split("/");
for (let i = 0; i < pathnodes.length; i++) {
let pathnode = pathnodes[i];
let nextnode = i + 1;
let groupPathNodes = pathnodes.slice(0, nextnode);
let groupPath = groupPathNodes.join("/");
newcrumbs.push([pathnode, `/secrets/groups/${groupPath}`]);
}
} else {
newcrumbs.push(["Ungrouped", "/secrets/groups/"]);
}
if (secret) {
newcrumbs.push([secret, `/secrets/secret/${secret}`]);
}
for (let i = 0; i < newcrumbs.length; i++) {
let crumbParam = newcrumbs[i];
let newcrumb = createCrumb(crumbParam[0], crumbParam[1]);
breadcrumbs.appendChild(newcrumb);
}
}
function addTreeListener() {
const tree = document.querySelector("sl-tree");
if (!tree) return;
@ -33,4 +90,12 @@ document.addEventListener("DOMContentLoaded", () => {
});
}
});
}
document.addEventListener("DOMContentLoaded", () => {
addTreeListener();
});
document.addEventListener("htmx:afterSwap", () => {
addTreeListener();
});

View File

@ -63,12 +63,14 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
current_user: Annotated[LocalUserInfo, Depends(dependencies.get_user_info)],
):
breadcrumbs = [("secrets", "/secrets/")]
groups = await admin.get_secret_groups()
return templates.TemplateResponse(
request,
"secrets/index.html.j2",
{
"groups": groups,
"breadcrumbs": breadcrumbs,
"user": current_user,
"selected_group": None,
"group_path_nodes": ["/"],
@ -83,8 +85,15 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
):
"""Show the root path."""
clients = await admin.get_clients()
breadcrumbs = [
("secrets", "/secrets/"),
("groups", "/secrets/groups/"),
("Ungrouped", "/secrets/groups/"),
]
context: dict[str, Any] = {
"clients": clients,
"breadcrumbs": breadcrumbs,
"root_group_page": True,
}
headers: dict[str, str] = {}
@ -119,11 +128,20 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
)
clients = await admin.get_clients()
breadcrumbs = [("secrets", "/secrets/"), ("groups", "/secrets/groups/")]
path_nodes = group.path.split("/")
for x in range(len(path_nodes)):
next_node = x + 1
group_path = "/".join(path_nodes[:next_node])
crumb_path = os.path.join("/secrets", group_path)
breadcrumbs.append((path_nodes[x], crumb_path))
headers: dict[str, str] = {}
context: dict[str, Any] = {
"group_page": True,
"group": group,
"clients": clients,
"breadcrumbs": breadcrumbs,
}
if request.headers.get("HX-Request"):
# This is a HTMX request.