admin-redesign #26

Merged
eising merged 9 commits from admin-redesign into main 2025-06-19 05:24:06 +00:00
32 changed files with 765 additions and 430 deletions

View File

@ -1,60 +1,6 @@
{% extends "/dashboard/_base.html" %} {% block content %} {% extends "/base/page.html.j2" %}
<div {% block title %}Audit{% endblock %}
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" {% block page_content %}
>
<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-800 dark:text-white"
aria-hidden="true"
xmlns="http://www.w3.org/2000/svg"
width="24"
height="24"
fill="none"
viewBox="0 0 24 24"
>
<path
stroke="currentColor"
stroke-linecap="round"
stroke-linejoin="round"
stroke-width="2"
d="M9 8h6m-6 4h6m-6 4h6M6 3v18l2-2 2 2 2-2 2 2 2-2 2 2V3l-2 2-2-2-2 2-2-2-2 2-2-2Z"
/>
<span class="ml-1 text-gray-400 md:ml-2 dark:text-gray-500" aria-current="page">Audit Log</span>
</svg>
</div>
</li>
</ol>
</nav>
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Audit Log</h1>
</div>
</div>
</div>
<div id="auditContent"> <div id="auditContent">
{% include 'audit/inner.html.j2' %} {% include 'audit/inner.html.j2' %}
</div> </div>

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en" class="dark">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>{% block title %}Sshecret Admin{% endblock %}</title>
{% block head %}
{% include 'base/partials/stylesheets.html.j2' %}
{% endblock %}
</head>
<body class="bg-gray-50 text-gray-900 dark:bg-gray-900 min-h-screen flex flex-col">
<!-- Layout Container -->
<div class="flex flex-1 h-full overflow-hidden">
<!-- Sidebar -->
<aside class="hidden md:flex md:w-64 flex-col h-full min-h-screen bg-white border-r border-gray-300 dark:bg-gray-800 dark:border-gray-700" id="sidebar" aria-label="sidebar">
{% include "base/partials/sidebar.html.j2" %}
</aside>
<!-- Main Panel -->
<div class="flex-1 flex flex-col overflow-hidden">
<!-- Topbar -->
<header class="bg-white border-b px-4 py-3 border-gray-300 dark:bg-gray-800 dark:border-gray-700">
{% include "base/partials/navbar.html.j2" %}
</header>
<!-- Main Content Area -->
<main id="content" class="flex-1 overflow-y-auto" hx-target="this" hx-swap="innerHTML">
{% block breadcrumbs %}
{% endblock %}
<div class="" id="maincontent">
{% block content %}{% endblock %}
</div>
</main>
</div>
</div>
{% block scripts %}
{% include 'base/partials/scripts.html.j2' %}
{% endblock %}
{% block local_scripts %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,26 @@
{% extends 'base/page.html.j2' %}
{% block page_content %}
<!-- Master-Detail Split View -->
<div class="flex h-[calc(100vh-8rem)] overflow-hidden">
<!-- Master Pane -->
<aside id="master-pane"
class="md:w-80 w-full shrink-0 border-r overflow-y-auto bg-white md:block border-gray-200 p-4 dark:bg-gray-800 dark:border-gray-700">
{% block master %}
<p class="p-4 text-gray-500">Master view (e.g. list/tree)</p>
{% endblock %}
</aside>
<!-- Detail Pane -->
<section id="detail-pane"
class="flex-1 flex overflow-y-auto bg-white p-4 hidden md:block dark:bg-gray-800">
{% block detail %}
<p class="p-4 text-gray-500 dark:text-gray-200">Select an item to view details</p>
{% endblock %}
</section>
</div>
{% endblock %}

View File

@ -0,0 +1,27 @@
{% extends 'base/page.html.j2' %}
{% block title %}Clients{% endblock %}
{% block page_content %}
<!-- Master-Detail Layout -->
<div class="grid grid-cols-1 md:grid-cols-[300px_1fr] gap-4">
<!-- Master (e.g., tree or list) -->
<div id="master-pane">
{% block master %}
<p>Master list goes here</p>
{% endblock %}
</div>
<!-- Detail (loaded by HTMX or inline) -->
<div id="detail-pane" class="bg-white rounded shadow p-4">
{% block detail %}
<p>Select an item from the list to view details.</p>
{% endblock %}
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,40 @@
{% extends "/base/base.html.j2" %}
{% block breadcrumbs %}
<div class="p-4 bg-white block sm:flex items-center justify-between border-b border-gray-200 dark:bg-gray-800 dark:border-gray-700">
<nav class="text-sm text-gray-500" aria-label="Breadcrumb">
<sl-breadcrumb id="breadcrumbs">
<sl-breadcrumb-item>
<sl-icon slot="prefix" name="house"></sl-icon>
<a href="/">Home</a>
</sl-breadcrumb-item>
{% if breadcrumbs %}
{% for label, url in breadcrumbs %}
<sl-breadcrumb-item class="page-breadcrumb">
{% if url %}
<a href="{{url}}">{{label}}</a>
{% else %}
{{ label }}
{% endif %}
</sl-breadcrumb-item>
{% endfor %}
{% endif %}
</sl-breadcrumb>
</nav>
</div>
{% endblock %}
{% block content %}
<!-- Breadcrumbs -->
<!-- Page Content -->
<section class="bg-white dark:bg-gray-800">
{% block page_content %}
<p>This is a generic page.</p>
{% endblock %}
</section>
{% endblock %}

View File

@ -0,0 +1,68 @@
<header class="flex items-center justify-between">
<!-- Left: Sidebar toggle (for mobile) + Title -->
<div class="flex items-center space-x-4">
<!-- Mobile sidebar toggle -->
<button
id="sidebar-toggle"
aria-expanded="true"
aria-controls="mobile-sidebar"
class="md:hidden text-gray-600 hover:text-gray-900 focus:outline-none"
aria-label="Toggle sidebar"
>
<sl-icon name="list" class="text-xl"></sl-icon>
</button>
<!-- Page title or logo -->
{% if page_title %}
<h1 class="text-xl flex ml-2 md:mr-24 font-semibold text-gray-900 sm:text-2xl dark:text-white">{{page_title}}</h1>
{% endif %}
</div>
<!-- Right: User menu -->
<div class="relative">
<button
type="button"
class="flex text-sm bg-gray-800 rounded-full focus:ring-4 focus:ring-gray-300 dark:focus:ring-gray-600"
id="user-menu-button-2"
aria-expanded="false"
data-dropdown-toggle="dropdown-2"
>
<span class="sr-only">Open user menu</span>
<sl-avatar label="User avatar"></sl-avatar>
</button>
<!-- Dropdown placeholder -->
<div
class="z-50 hidden my-4 text-base list-none bg-white divide-y divide-gray-100 rounded shadow dark:bg-gray-700 dark:divide-gray-600"
id="dropdown-2"
>
<div class="px-4 py-3" role="none">
<p class="text-sm text-gray-900 dark:text-white" role="none">
{{ user.display_name }}
</p>
</div>
<ul class="py-1" role="none">
{% if user.local %}
<li>
<a
href="/password"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem"
>Change Password</a
>
</li>
{% endif %}
<li>
<a
href="/logout"
class="block px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 dark:text-gray-300 dark:hover:bg-gray-600 dark:hover:text-white"
role="menuitem"
>Logout</a
>
</li>
</ul>
</div>
<!-- You can later replace this with a Flowbite dropdown or Shoelace menu -->
</div>
</header>

View File

@ -0,0 +1,26 @@
{# <script src="{{ url_for('static', path='js/sidebar.js') }}"></script> #}
<script async defer src="https://buttons.github.io/buttons.js"></script>
<script src="https://cdn.jsdelivr.net/npm/simple-datatables@9.0.3"></script>
<script src="https://cdn.jsdelivr.net/npm/flowbite@3.1.2/dist/flowbite.min.js"></script>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.1/cdn/shoelace-autoloader.js"></script>
<script type="text/javascript" src="{{ url_for('static', path="js/prism.js") }}"></script>
<script>
const sidebarToggle = document.getElementById('sidebar-toggle');
const sidebarDrawer = document.getElementById('sidebar');
sidebarToggle?.addEventListener('click', () => {
sidebarDrawer.classList.toggle("hidden");
});
document.body.addEventListener("htmx:afterSwap", (e) => {
const swappedEl = e.target;
const initTargets = swappedEl.querySelectorAll(".flowbite-init-target");
if (initTargets.length > 0 && typeof window.initFlowbite === "function") {
window.initFlowbite();
}
});
</script>

View File

@ -0,0 +1,44 @@
<!-- Sidebar Container -->
<!-- Top: Brand -->
<div class="px-4 py-6">
<a href="/" class="text-xl font-semibold text-gray-800 dark:text-gray-100">
<sl-icon src="{{ url_for('static', path='logo.svg') }}"></sl-icon>
Sshecret
</a>
</div>
<nav class="flex-1 overflow-y-auto px-4" aria-label="navigation">
<ul class="space-y-">
<li>
<a href="/" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700">
<sl-icon name="house"></sl-icon>
Dashboard
</a>
</li>
<li>
<a href="/clients/" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700">
<sl-icon name="person-fill-lock"> </sl-icon>
Clients
</a>
</li>
<li>
<a href="/secrets" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700">
<sl-icon name="database-lock"></sl-icon>
Secrets
</a>
</li>
<li>
<a href="/audit" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100 dark:text-gray-200 dark:hover:bg-gray-700">
<sl-icon name="card-list"></sl-icon>
Audit Log
</a>
</li>
</ul>
</nav>

View File

@ -0,0 +1,50 @@
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap"
rel="stylesheet"
/>
<link
rel="stylesheet"
href="{{ url_for('static', path='css/main.css') }}"
type="text/css"
/>
<link
rel="stylesheet"
href="{{ url_for('static', path='css/prism.css') }}"
type="text/css"
/>
<link
rel="stylesheet"
href="{{ url_for('static', path='css/style.css') }}"
type="text/css"
/>
<link
href="https://cdn.jsdelivr.net/npm/remixicon@3.5.0/fonts/remixicon.css"
rel="stylesheet"
/>
<link
rel="stylesheet"
media="(prefers-color-scheme:light)"
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.1/cdn/themes/light.css"
/>
<link
rel="stylesheet"
media="(prefers-color-scheme:dark)"
href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.20.1/cdn/themes/dark.css"
onload="document.documentElement.classList.add('sl-theme-dark');"
/>
<script>
// On page load or when changing themes, best to add inline in `head` to avoid FOUC
if (
localStorage.getItem("color-theme") === "dark" ||
(!("color-theme" in localStorage) &&
window.matchMedia("(prefers-color-scheme: dark)").matches)
) {
document.documentElement.classList.add("dark");
} else {
document.documentElement.classList.remove("dark");
}
</script>

View File

@ -1,5 +1,9 @@
{% extends "/dashboard/_base.html" %} {% block content %} {% extends "/base/page.html.j2" %}
<div class="min-h-screen bg-gray-100 flex items-center justify-center p-4"> {% block title %}Change Password{% endblock %}
{% block page_content %}
<div class="h-[calc(100vh-8rem)] bg-gray-100 flex items-center justify-center p-4">
<div class="w-full max-w-xl p-6 space-y-8 bg-white rounded-lg shadow sm:p-8 dark:bg-gray-800"> <div class="w-full max-w-xl p-6 space-y-8 bg-white rounded-lg shadow sm:p-8 dark:bg-gray-800">
<h2 class="text-2xl font-bold text-gray-900 dark:text-white"> <h2 class="text-2xl font-bold text-gray-900 dark:text-white">
Change Password Change Password

View File

@ -1,5 +1,8 @@
{% extends "/dashboard/_base.html" %} {% block content %}
<div class="min-h-screen bg-gray-100 flex items-center justify-center p-4"> {% extends "/base/page.html.j2" %}
{% block page_content %}
<div class="h-[calc(100vh-8rem)] bg-gray-100 flex items-center justify-center p-4">
<div class="text-center xl:max-w-4xl"> <div class="text-center xl:max-w-4xl">
<h1 class="mb-3 text-2xl font-bold leading-tight text-gray-900 sm:text-4xl lg:text-5xl dark:text-white">Password Changed</h1> <h1 class="mb-3 text-2xl font-bold leading-tight text-gray-900 sm:text-4xl lg:text-5xl dark:text-white">Password Changed</h1>
<p class="mb-5 text-base font-normal text-gray-500 md:text-lg dark:text-gray-400">Your password was changed sucessfully. Next time you log in, use your new password.</p> <p class="mb-5 text-base font-normal text-gray-500 md:text-lg dark:text-gray-400">Your password was changed sucessfully. Next time you log in, use your new password.</p>
@ -9,4 +12,4 @@
</a> </a>
</div> </div>
</div> </div>
{% endblock content %} {% endblock %}

View File

@ -1,46 +1,24 @@
{% 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"> {% block title %}Client {{ client.name }}{% endblock %}
<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">Clients</span>
</div>
</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">View</span>
</div>
</li>
</ol>
</nav>
</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 h-full" id="client-tree">
{% include '/clients/partials/tree.html.j2' %}
</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">
<div class="w-full" id="clientdetails">
{% include '/clients/partials/client_details.html.j2' %}
</div>
</div>
</div>
</div>
</div>
{% include '/clients/partials/drawer_create.html.j2' %}
{% block master %}
{% include '/clients/partials/tree.html.j2' %}
{% endblock %} {% endblock %}
{% block detail %}
<div id="clientdetails" class="w-full">
{% include '/clients/partials/client_details.html.j2' %}
</div>
{% endblock %}
{% include '/clients/partials/drawer_create.html.j2' %}
{% block local_scripts %}
<script>
{% include '/clients/partials/tree_event.js' %}
</script>
{% endblock local_scripts %}

View File

@ -1,41 +1,21 @@
{% 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"> {% block title %}Clients{% endblock %}
<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">Clients</span>
</div>
</li>
</ol>
</nav>
</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 h-full" id="client-tree">
{% include '/clients/partials/tree.html.j2' %}
</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">
<div class="w-full" id="clientdetails">
<h3 class="mb-4 text-sm italic text-gray-400 dark:text-white">Click an item to view details</h3>
</div>
</div>
</div>
</div>
</div>
{% include '/clients/partials/drawer_create.html.j2' %}
{% block master %}
{% include '/clients/partials/tree.html.j2' %}
{% endblock %} {% endblock %}
{% block detail %}
<div id="clientdetails" class="w-full bg-white dark:bg-gray-800">
<h3 class="mb-4 text-sm italic text-gray-400 dark:text-white">Click an item to view details</h3>
</div>
{% include '/clients/partials/drawer_create.html.j2' %}
{% endblock %}
{% block local_scripts %}
<script>
{% include '/clients/partials/tree_event.js' %}
</script>
{% endblock local_scripts %}

View File

@ -38,7 +38,7 @@
</ul> </ul>
</div> </div>
</div> </div>
<sl-tab-group placement="end"> <sl-tab-group >
<sl-tab slot="nav" panel="client_data">Client Data</sl-tab> <sl-tab slot="nav" panel="client_data">Client Data</sl-tab>
<sl-tab slot="nav" panel="events">Events</sl-tab> <sl-tab slot="nav" panel="events">Events</sl-tab>
@ -47,37 +47,37 @@
<div id="client_details"> <div id="client_details">
<div class="w-full p-2"> <div class="w-full p-2">
<div class="px-4 sm:px-0"> <div class="px-4 sm:px-0">
<h3 class="text-base/7 font-semibold text-gray-900">{{client.name}}</h3> <h3 class="text-base/7 font-semibold text-gray-900 dark:text-gray-50">{{client.name}}</h3>
{% if client.description %} {% if client.description %}
<p class="mt-1 max-w-2xl text-sm/6 text-gray-500">{{ client.description }}</p> <p class="mt-1 max-w-2xl text-sm/6 text-gray-500 dark:text-gray-100">{{ client.description }}</p>
{% endif %} {% endif %}
</div> </div>
<div class="mt-6 border-t border-gray-100"> <div class="mt-6 border-t border-gray-100">
<dl class="divide-y divide-gray-100"> <dl class="divide-y divide-gray-100">
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm/6 font-medium text-gray-900">Client ID</dt> <dt class="text-sm/6 font-medium text-gray-900 dark:text-gray-200">Client ID</dt>
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{client.id}}</dd> <dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 dark:text-gray-300">{{client.id}}</dd>
</div> </div>
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm/6 font-medium text-gray-900">Client Description</dt> <dt class="text-sm/6 font-medium text-gray-900 dark:text-gray-200">Client Description</dt>
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{client.description}}</dd> <dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 dark:text-gray-300">{{client.description}}</dd>
</div> </div>
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm/6 font-medium text-gray-900">Client Version</dt> <dt class="text-sm/6 font-medium text-gray-900 dark:text-gray-200">Client Version</dt>
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{client.version}}</dd> <dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 dark:text-gray-300">{{client.version}}</dd>
</div> </div>
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm/6 font-medium text-gray-900">Public Key</dt> <dt class="text-sm/6 font-medium text-gray-900 dark:text-gray-200">Public Key</dt>
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 truncate">{{client.public_key}}</dd> <dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 dark:text-gray-300 truncate">{{client.public_key}}</dd>
</div> </div>
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm/6 font-medium text-gray-900">Assigned Secrets</dt> <dt class="text-sm/6 font-medium text-gray-900 dark:text-gray-200">Assigned Secrets</dt>
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{client.secrets|length}}</dd> <dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 dark:text-gray-300">{{client.secrets|length}}</dd>
</div> </div>
<div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0"> <div class="px-4 py-6 sm:grid sm:grid-cols-3 sm:gap-4 sm:px-0">
<dt class="text-sm/6 font-medium text-gray-900">Allowed sources</dt> <dt class="text-sm/6 font-medium text-gray-900 dark:text-gray-200">Allowed sources</dt>
<dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0">{{client.policies|join(', ')}}</dd> <dd class="mt-1 text-sm/6 text-gray-700 sm:col-span-2 sm:mt-0 dark:text-gray-300">{{client.policies|join(', ')}}</dd>
</div> </div>
</dl> </dl>
</div> </div>
@ -86,7 +86,7 @@
</sl-tab-panel> </sl-tab-panel>
<sl-tab-panel name="events"> <sl-tab-panel name="events">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events"> <table class="min-w-full lg:table-fixed divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events">
<thead class="bg-gray-50 dark:bg-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700">
<tr> <tr>
<th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Timestamp</th> <th scope="col" class="p-4 text-xs font-medium tracking-wider text-left text-gray-500 uppercase dark:text-white">Timestamp</th>

View File

@ -1,67 +1,64 @@
<div class="flex flex-1 flex-col justify-between h-full flowbite-init-target"> {# This is the master block #}
<div class="grid grid-cols-2 place-content-between mb-6">
<div> <div class="flowbite-init-target">
<h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Clients</h1> <div class="tree-header grid grid-cols-2 place-content-between mb-6">
<div class="flex"> <h1 class="text-xl font-semibold text-gray-900 sm:text-2xl dark:text-white">Client List</h1>
<div <div class="flex">
class="htmx-indicator mt-2" <div
id="client-spinner"> class="htmx-indicator mt-2"
<div role="status"> id="client-spinner">
<svg aria-hidden="true" class="inline w-6 h-6 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg"> <div role="status">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/> <svg aria-hidden="true" class="inline w-6 h-6 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/> <path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
</svg> <path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
<span class="sr-only">Loading...</span> </svg>
<span class="sr-only">Loading...</span>
</div>
</div>
<div
class="flex w-full justify-end"
>
<sl-icon-button
name="plus-square"
label="Add Client"
data-drawer-target="drawer-create-client-default"
data-drawer-show="drawer-create-client-default"
aria-controls="drawer-create-client-default"
data-drawer-placement="right"
></sl-icon-button>
</div>
</div>
<div class="col-span-full">
<div class="relative">
<div class="border-b border-gray-200 py-2 mb-6">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
</svg>
</div>
<input
type="search"
id="client-search"
name="query"
class="block w-full p-2.5 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-gray-900 focus:border-gray-900 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-gray-900 dark:focus:border-gray-900"
placeholder="Search..."
required
hx-post="/clients/query"
hx-trigger="input changed delay:500ms, keyup[key=='Enter']"
hx-target="#client-tree-items"
hx-indicator="#client-spinner"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div
class="flex justify-end px-4"
>
<sl-icon-button
name="plus-square"
label="Add Client"
data-drawer-target="drawer-create-client-default"
data-drawer-show="drawer-create-client-default"
aria-controls="drawer-create-client-default"
data-drawer-placement="right"
></sl-icon-button>
</div>
</div> </div>
<div class="flex-1 overflow-auto"> <div id="client-tree-items">
<div class="relative w-full "> {% include '/clients/partials/tree_items.html.j2' %}
<div class="border-b border-gray-200 py-2 mb-6">
<label for="default-search" class="mb-2 text-sm font-medium text-gray-900 sr-only dark:text-white">Search</label>
<div class="relative">
<div class="absolute inset-y-0 start-0 flex items-center ps-3 pointer-events-none">
<svg class="w-4 h-4 text-gray-500 dark:text-gray-400" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 20 20">
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
</svg>
</div>
<input
type="search"
id="client-search"
name="query"
class="block w-full p-2.5 ps-10 text-sm text-gray-900 border border-gray-300 rounded-lg bg-gray-50 focus:ring-gray-900 focus:border-gray-900 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-gray-900 dark:focus:border-gray-900"
placeholder="Search..."
required
hx-post="/clients/query"
hx-trigger="input changed delay:500ms, keyup[key=='Enter']"
hx-target="#client-tree-items"
hx-indicator="#client-spinner"
/>
</div>
</div>
</div>
<div id="client-tree-items">
{% include '/clients/partials/tree_items.html.j2' %}
</div>
</div> </div>
</div> </div>
<script>
{% include '/clients/partials/tree_event.js' %}
</script>

View File

@ -1,3 +1,16 @@
function setBreadcrumb(name) {
// Set the current client name as the final breadcrumb
const breadcrumbs = document.getElementById("breadcrumbs");
const existingNode = document.getElementById("bc-dynamic-client");
if (existingNode) {
breadcrumbs.removeChild(existingNode);
}
const newCrumb = document.createElement("sl-breadcrumb-item");
newCrumb.setAttribute("id", "bc-dynamic-client");
const bcTitle = document.createTextNode(name);
newCrumb.appendChild(bcTitle);
breadcrumbs.appendChild(newCrumb);
}
function addTreeListener() { function addTreeListener() {
const tree = document.querySelector("sl-tree"); const tree = document.querySelector("sl-tree");
@ -8,6 +21,8 @@ function addTreeListener() {
if (!selectedEl) return; if (!selectedEl) return;
const masterPane = document.getElementById("master-pane");
const detailPane = document.getElementById("detail-pane");
const type = selectedEl.dataset.nodeType; const type = selectedEl.dataset.nodeType;
const clientId = selectedEl.dataset.clientId; const clientId = selectedEl.dataset.clientId;
const name = selectedEl.dataset.clientName; const name = selectedEl.dataset.clientName;
@ -17,11 +32,17 @@ function addTreeListener() {
let url = `/clients/client/${encodeURIComponent(clientId)}`; let url = `/clients/client/${encodeURIComponent(clientId)}`;
if (url) { if (url) {
htmx.ajax("GET", url, { htmx
target: "#clientdetails", .ajax("GET", url, {
//swap: 'OuterHTML', target: "#clientdetails",
indicator: "#client-spinner", //swap: 'OuterHTML',
}); indicator: "#client-spinner",
})
.then(() => {
masterPane.classList.add("hidden");
detailPane.classList.remove("hidden");
setBreadcrumb(name);
});
} }
}); });
} }

View File

@ -38,5 +38,4 @@
{% include 'clients/partials/pagination.html.j2' %} {% include 'clients/partials/pagination.html.j2' %}
</div> </div>
{% endif %} {% endif %}
</div> </div>

View File

@ -1,4 +1,4 @@
{% extends "/dashboard/_base.html" %} {% block content %} {% extends "/base/base.html.j2" %} {% block content %}
<div class="px-4 pt-6"> <div class="px-4 pt-6">
<div class="py-8 px-4 mt-4 mx-auto max-w-screen-xl text-center lg:py-16"> <div class="py-8 px-4 mt-4 mx-auto max-w-screen-xl text-center lg:py-16">

View File

@ -13,7 +13,7 @@
{% endif %} {% endif %}
<div id="main-content" class="relative w-full h-full overflow-y-auto bg-gray-50 lg:ml-64 dark:bg-gray-900 flex flex-col md:flex-row flex-grow"> <div id="main-content" class="relative w-full h-full overflow-y-auto bg-gray-50 lg:ml-64 dark:bg-gray-900 flex flex-col md:flex-row flex-grow">
<main class="flex-grow p-4 order-2 md:order-1"> <main>
{% block content %} {% block content %}
{% endblock %} {% endblock %}
</main> </main>

View File

@ -1,4 +1,4 @@
{% extends "/shared/_base.html" %} {% block content %} {% if login_error %} {% extends "/base/bare.html.j2" %} {% block content %} {% if login_error %}
<div class="flex bg-gray-100"> <div class="flex bg-gray-100">
<div <div
@ -88,7 +88,5 @@
</a> </a>
</div> </div>
</div> </div>
{% endif %} {% endif %} {% endblock %}
{% endblock %}
</div> </div>

View File

@ -47,85 +47,67 @@
{% endmacro %} {% 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"> {% block title %}Secrets{% endblock %}
<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">
<div class="flex flex-1 flex-col"> {% block master %}
<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"
{% if "/" in group_path_nodes %} <div class="flowbite-init-target">
expanded="" <div id="secret-tree">
{% endif %} <sl-tree class="tree-with-icons">
{% if selected_group == "/"%} <sl-tree-item
selected="" id="secret-group-root-item"
{% endif %} data-type="root"
> data-name="root"
<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> {% if "/" in group_path_nodes %}
<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"> expanded=""
{% 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 %} {% endif %}
{% if selected_group == "/"%}
</div> selected=""
</div> {% 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> </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> <script>
{% include '/secrets/partials/tree_event.js' %} {% include '/secrets/partials/tree_event.js' %}
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,4 +1,4 @@
<div class="w-full my-2"> <div class="w-full my-2 dark:text-white">
<ul class="w-48 text-sm font-medium text-gray-900 bg-white dark:bg-gray-700 dark:text-white" id="secretclientlist"> <ul class="w-48 text-sm font-medium text-gray-900 bg-white dark:bg-gray-700 dark:text-white" id="secretclientlist">
{% include '/secrets/partials/client_list_inner.html.j2' %} {% include '/secrets/partials/client_list_inner.html.j2' %}
</ul> </ul>

View File

@ -1,4 +1,4 @@
<div class="w-full"> <div class="w-full dark:text-white">
<sl-details summary="Create secret"> <sl-details summary="Create secret">
<form <form
hx-post="/secrets/create/root" hx-post="/secrets/create/root"

View File

@ -1,4 +1,4 @@
<div class="w-full"> <div class="w-full dark:text-white">
<div class="mb-4"> <div class="mb-4">
<h3 class="text-xl font-semibold dark:text-white">Group {{group.group_name}}</h3> <h3 class="text-xl font-semibold dark:text-white">Group {{group.group_name}}</h3>
{% if description %} {% if description %}

View File

@ -1,33 +1,33 @@
<div class="w-full flowbite-init-target" id="secretdetails"> <div class="w-full flowbite-init-target dark:text-white" id="secretdetails">
<!-- menu --> <!-- menu -->
<div class="flex justify-end px-4"> <div class="flex justify-end px-4">
<button id="secret-menu-button" data-dropdown-toggle="secret-edit-menu" class="inline-block text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-4 focus:outline-none focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-1.5" type="button"> <button id="secret-menu-button" data-dropdown-toggle="secret-edit-menu" class="inline-block text-gray-500 dark:text-gray-400 hover:bg-gray-100 dark:hover:bg-gray-700 focus:ring-4 focus:outline-none focus:ring-gray-200 dark:focus:ring-gray-700 rounded-lg text-sm p-1.5" type="button">
<span class="sr-only">Open dropdown</span> <span class="sr-only">Open dropdown</span>
<svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 3"> <svg class="w-5 h-5" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 3">
<path d="M2 0a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm6.041 0a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM14 0a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z"/> <path d="M2 0a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Zm6.041 0a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM14 0a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3Z"/>
</svg> </svg>
</button> </button>
<!-- Dropdown menu --> <!-- Dropdown menu -->
<div id="secret-edit-menu" class="z-10 hidden text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow-sm w-44 dark:bg-gray-700"> <div id="secret-edit-menu" class="z-10 hidden text-base list-none bg-white divide-y divide-gray-100 rounded-lg shadow-sm w-44 dark:bg-gray-700">
<ul class="py-2" aria-labelledby="secret-menu-button"> <ul class="py-2" aria-labelledby="secret-menu-button">
<li> <li>
<a <a
href="#" href="#"
class="block px-4 py-2 text-sm text-red-600 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white" class="block px-4 py-2 text-sm text-red-600 hover:bg-gray-100 dark:hover:bg-gray-600 dark:text-gray-200 dark:hover:text-white"
hx-delete="/secrets/{{secret.name}}" hx-delete="/secrets/{{secret.name}}"
hx-target="#secretdetails" hx-target="#secretdetails"
hx-swap="OuterHTML" hx-swap="OuterHTML"
hx-indicator=".secret-spinner" hx-indicator=".secret-spinner"
hx-confirm="Really delete this secret?" hx-confirm="Really delete this secret?"
> >
Delete Delete
</a> </a>
</li> </li>
</ul> </ul>
</div>
</div> </div>
</div>
<h3 class="mb-4 text-xl font-semibold dark:text-white">{{secret.name}}</h3> <h3 class="mb-4 text-xl font-semibold dark:text-white">{{secret.name}}</h3>
@ -53,65 +53,65 @@
</div> </div>
</sl-details> </sl-details>
{% if secret.secret %} {% if secret.secret %}
<sl-details summary="Read/Update Secret"> <sl-details summary="Read/Update Secret">
<div id="secretvalue"> <div id="secretvalue">
<div class="mb-6"> <div class="mb-6">
<label for="secret-value" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Value</label> <label for="secret-value" class="block mb-2 text-sm font-medium text-gray-900 dark:text-white">Value</label>
</div> </div>
<div class="flex w-full">
<div class="relative w-full">
<input type="text" id="disabled-input" aria-label="disabled input" class="mb-6 bg-gray-100 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 cursor-not-allowed dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="••••••••" disabled>
</div>
<div class="px-2.5 mb-2">
<button
type="button"
class="text-gray-900 hover:text-blue-700 border border-gray-200 hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800"
hx-get="/secrets/partial/{{ secret.name }}/viewsecret"
hx-target="#secretvalue"
hx-trigger="click"
hx-indicator="#secretupdatespinner"
>
View
</button>
</div>
</div>
</div>
<div class="htmx-indicator" id="secretupdatespinner">
<div role="status">
<svg aria-hidden="true" class="inline w-4 h-4 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
<span class="sr-only">Loading...</span>
</div>
</div>
</sl-details>
{% if groups.groups %}
<sl-details summary="Group">
<form
hx-put="/secrets/set-group/{{ secret.name }}"
hx-target="#secretdetails"
hx-swap="OuterHTML"
hx-indicator=".secret-spinner"
>
<div class="flex w-full"> <div class="flex w-full">
<div class="relative w-full"> <div class="relative w-full">
<select id="group_name" name="group_name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500"> <input type="text" id="disabled-input" aria-label="disabled input" class="mb-6 bg-gray-100 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 cursor-not-allowed dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-gray-400 dark:focus:ring-blue-500 dark:focus:border-blue-500" placeholder="••••••••" disabled>
<option value="__ROOT">Ungrouped</option>
{% for group in groups.groups %}
<option value="{{ group.group_name }}" {% if group.name == secret.group -%}selected{% endif %}>{{ group.path }}</option>
{% endfor %}
</select>
</div> </div>
<div class="px-2.5 mb-2"> <div class="px-2.5 mb-2">
<button type="Submit" class="text-gray-900 hover:text-blue-700 border border-gray-200 hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800">Update</button> <button
type="button"
class="text-gray-900 hover:text-blue-700 border border-gray-200 hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800"
hx-get="/secrets/partial/{{ secret.name }}/viewsecret"
hx-target="#secretvalue"
hx-trigger="click"
hx-indicator="#secretupdatespinner"
>
View
</button>
</div> </div>
</div> </div>
</form> </div>
<div class="htmx-indicator" id="secretupdatespinner">
<div role="status">
<svg aria-hidden="true" class="inline w-4 h-4 text-gray-200 animate-spin dark:text-gray-600 fill-blue-600" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
<path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
</svg>
<span class="sr-only">Loading...</span>
</div>
</div>
</sl-details> </sl-details>
{% if groups.groups %}
<sl-details summary="Group">
<form
hx-put="/secrets/set-group/{{ secret.name }}"
hx-target="#secretdetails"
hx-swap="OuterHTML"
hx-indicator=".secret-spinner"
>
<div class="flex w-full">
<div class="relative w-full">
<select id="group_name" name="group_name" class="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white dark:focus:ring-blue-500 dark:focus:border-blue-500">
<option value="__ROOT">Ungrouped</option>
{% for group in groups.groups %}
<option value="{{ group.group_name }}" {% if group.name == secret.group -%}selected{% endif %}>{{ group.path }}</option>
{% endfor %}
</select>
</div>
<div class="px-2.5 mb-2">
<button type="Submit" class="text-gray-900 hover:text-blue-700 border border-gray-200 hover:bg-gray-100 focus:ring-4 focus:outline-none focus:ring-blue-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center me-2 mb-2 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-500 dark:focus:ring-blue-800">Update</button>
</div>
</div>
</form>
</sl-details>
{% endif %}
{% endif %} {% endif %}
{% endif %} <sl-details summary="Events" class="dark:text-white">
<sl-details summary="Events">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events"> <table class="min-w-full divide-y divide-gray-200 dark:divide-gray-600" id="last-audit-events">
<thead class="bg-gray-50 dark:bg-gray-700"> <thead class="bg-gray-50 dark:bg-gray-700">
<tr> <tr>

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"); const tree = document.querySelector("sl-tree");
if (!tree) return; if (!tree) return;
@ -33,4 +90,12 @@ document.addEventListener("DOMContentLoaded", () => {
}); });
} }
}); });
}
document.addEventListener("DOMContentLoaded", () => {
addTreeListener();
});
document.addEventListener("htmx:afterSwap", () => {
addTreeListener();
}); });

View File

@ -42,6 +42,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
page=page, limit=per_page, total=audit_log.total, offset=offset page=page, limit=per_page, total=audit_log.total, offset=offset
) )
operations = list(Operation) operations = list(Operation)
breadcrumbs = [("Audit", "/audit/")]
if request.headers.get("HX-Request"): if request.headers.get("HX-Request"):
return templates.TemplateResponse( return templates.TemplateResponse(
request, request,
@ -56,7 +57,8 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
request, request,
"audit/index.html.j2", "audit/index.html.j2",
{ {
"page_title": "Audit", "page_title": "Audit Log",
"breadcrumbs": breadcrumbs,
"entries": audit_log.results, "entries": audit_log.results,
"user": current_user, "user": current_user,
"page_info": page_info, "page_info": page_info,

View File

@ -93,11 +93,14 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
page=page, limit=per_page, total=results.total_results, offset=offset page=page, limit=per_page, total=results.total_results, offset=offset
) )
breadcrumbs = [("clients", "/clients/")]
LOG.info("Results %r", results) LOG.info("Results %r", results)
return templates.TemplateResponse( return templates.TemplateResponse(
request, request,
"clients/index.html.j2", "clients/index.html.j2",
{ {
"breadcrumbs": breadcrumbs,
"page_title": "Clients", "page_title": "Clients",
"offset": offset, "offset": offset,
"pages": paginate, "pages": paginate,
@ -166,6 +169,11 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
) )
template = "clients/client.html.j2" template = "clients/client.html.j2"
breadcrumbs = [
("clients", "/clients/"),
(results.client.name, request.url.path),
]
headers: dict[str, str] = {} headers: dict[str, str] = {}
if request.headers.get("HX-Request"): if request.headers.get("HX-Request"):
headers["HX-Push-Url"] = request.url.path headers["HX-Push-Url"] = request.url.path
@ -176,6 +184,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
template, template,
{ {
"page_title": f"Client {results.client.name}", "page_title": f"Client {results.client.name}",
"breadcrumbs": breadcrumbs,
"pages": results.pages, "pages": results.pages,
"clients": results.results.clients, "clients": results.results.clients,
"client": results.client, "client": results.client,

View File

@ -81,7 +81,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
request, request,
"dashboard.html", "dashboard.html",
{ {
"page_title": "sshecret", "page_title": "Dashboard",
"user": current_user, "user": current_user,
"stats": stats, "stats": stats,
"last_login_events": last_login_events, "last_login_events": last_login_events,

View File

@ -63,12 +63,15 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)], admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
current_user: Annotated[LocalUserInfo, Depends(dependencies.get_user_info)], current_user: Annotated[LocalUserInfo, Depends(dependencies.get_user_info)],
): ):
breadcrumbs = [("secrets", "/secrets/")]
groups = await admin.get_secret_groups() groups = await admin.get_secret_groups()
return templates.TemplateResponse( return templates.TemplateResponse(
request, request,
"secrets/index.html.j2", "secrets/index.html.j2",
{ {
"page_title": "Secrets",
"groups": groups, "groups": groups,
"breadcrumbs": breadcrumbs,
"user": current_user, "user": current_user,
"selected_group": None, "selected_group": None,
"group_path_nodes": ["/"], "group_path_nodes": ["/"],
@ -83,8 +86,15 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
): ):
"""Show the root path.""" """Show the root path."""
clients = await admin.get_clients() clients = await admin.get_clients()
breadcrumbs = [
("secrets", "/secrets/"),
("groups", "/secrets/groups/"),
("Ungrouped", "/secrets/groups/"),
]
context: dict[str, Any] = { context: dict[str, Any] = {
"clients": clients, "clients": clients,
"breadcrumbs": breadcrumbs,
"root_group_page": True, "root_group_page": True,
} }
headers: dict[str, str] = {} headers: dict[str, str] = {}
@ -95,6 +105,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
else: else:
groups = await admin.get_secret_groups() groups = await admin.get_secret_groups()
template_name = "secrets/index.html.j2" template_name = "secrets/index.html.j2"
context["page_title"] = "Secrets"
context["user"] = current_user context["user"] = current_user
context["groups"] = groups context["groups"] = groups
context["group_path_nodes"] = ["/"] context["group_path_nodes"] = ["/"]
@ -119,11 +130,20 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
) )
clients = await admin.get_clients() 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] = {} headers: dict[str, str] = {}
context: dict[str, Any] = { context: dict[str, Any] = {
"group_page": True, "group_page": True,
"group": group, "group": group,
"clients": clients, "clients": clients,
"breadcrumbs": breadcrumbs,
} }
if request.headers.get("HX-Request"): if request.headers.get("HX-Request"):
# This is a HTMX request. # This is a HTMX request.
@ -133,6 +153,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
template_name = "secrets/index.html.j2" template_name = "secrets/index.html.j2"
groups = await admin.get_secret_groups() groups = await admin.get_secret_groups()
context["page_title"] = "Secrets"
context["user"] = current_user context["user"] = current_user
context["groups"] = groups context["groups"] = groups
context["group_path_nodes"] = group.path.split("/") context["group_path_nodes"] = group.path.split("/")
@ -180,6 +201,7 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
group_path = group.path.split("/") group_path = group.path.split("/")
template_name = "secrets/index.html.j2" template_name = "secrets/index.html.j2"
context["page_title"] = "Secrets"
context["user"] = current_user context["user"] = current_user
context["groups"] = groups context["groups"] = groups
context["group_path_nodes"] = group_path context["group_path_nodes"] = group_path

View File

@ -382,9 +382,6 @@
.order-1 { .order-1 {
order: 1; order: 1;
} }
.order-2 {
order: 2;
}
.col-span-2 { .col-span-2 {
grid-column: span 2 / span 2; grid-column: span 2 / span 2;
} }
@ -457,9 +454,6 @@
.ms-3 { .ms-3 {
margin-inline-start: calc(var(--spacing) * 3); margin-inline-start: calc(var(--spacing) * 3);
} }
.ms-auto {
margin-inline-start: auto;
}
.me-2 { .me-2 {
margin-inline-end: calc(var(--spacing) * 2); margin-inline-end: calc(var(--spacing) * 2);
} }
@ -682,15 +676,18 @@
.h-\[12px\] { .h-\[12px\] {
height: 12px; height: 12px;
} }
.h-\[16px\] {
height: 16px;
}
.h-\[32px\] {
height: 32px;
}
.h-\[36rem\] { .h-\[36rem\] {
height: 36rem; height: 36rem;
} }
.h-\[calc\(100vh-4rem\)\] {
height: calc(100vh - 4rem);
}
.h-\[calc\(100vh-8rem\)\] {
height: calc(100vh - 8rem);
}
.h-\[calc\(100vh-10rem\)\] {
height: calc(100vh - 10rem);
}
.h-full { .h-full {
height: 100%; height: 100%;
} }
@ -1245,9 +1242,6 @@
.border-blue-300 { .border-blue-300 {
border-color: var(--color-blue-300); border-color: var(--color-blue-300);
} }
.border-blue-700 {
border-color: var(--color-blue-700);
}
.border-gray-100 { .border-gray-100 {
border-color: var(--color-gray-100); border-color: var(--color-gray-100);
} }
@ -1332,9 +1326,6 @@
.bg-blue-600 { .bg-blue-600 {
background-color: var(--color-blue-600); background-color: var(--color-blue-600);
} }
.bg-blue-700 {
background-color: var(--color-blue-700);
}
.bg-emerald-500 { .bg-emerald-500 {
background-color: var(--color-emerald-500); background-color: var(--color-emerald-500);
} }
@ -2175,13 +2166,6 @@
} }
} }
} }
.hover\:bg-blue-800 {
&:hover {
@media (hover: hover) {
background-color: var(--color-blue-800);
}
}
}
.hover\:bg-gray-50 { .hover\:bg-gray-50 {
&:hover { &:hover {
@media (hover: hover) { @media (hover: hover) {
@ -2774,11 +2758,6 @@
inset: calc(var(--spacing) * 0); inset: calc(var(--spacing) * 0);
} }
} }
.md\:order-1 {
@media (width >= 48rem) {
order: 1;
}
}
.md\:order-2 { .md\:order-2 {
@media (width >= 48rem) { @media (width >= 48rem) {
order: 2; order: 2;
@ -2854,6 +2833,21 @@
height: 100vh; height: 100vh;
} }
} }
.md\:w-64 {
@media (width >= 48rem) {
width: calc(var(--spacing) * 64);
}
}
.md\:w-72 {
@media (width >= 48rem) {
width: calc(var(--spacing) * 72);
}
}
.md\:w-80 {
@media (width >= 48rem) {
width: calc(var(--spacing) * 80);
}
}
.md\:w-\[calc\(100\%-256px\)\] { .md\:w-\[calc\(100\%-256px\)\] {
@media (width >= 48rem) { @media (width >= 48rem) {
width: calc(100% - 256px); width: calc(100% - 256px);
@ -2879,6 +2873,11 @@
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
} }
} }
.md\:grid-cols-\[300px_1fr\] {
@media (width >= 48rem) {
grid-template-columns: 300px 1fr;
}
}
.md\:flex-row { .md\:flex-row {
@media (width >= 48rem) { @media (width >= 48rem) {
flex-direction: row; flex-direction: row;
@ -3104,6 +3103,11 @@
width: auto; width: auto;
} }
} }
.lg\:table-fixed {
@media (width >= 64rem) {
table-layout: fixed;
}
}
.lg\:grid-cols-2 { .lg\:grid-cols-2 {
@media (width >= 64rem) { @media (width >= 64rem) {
grid-template-columns: repeat(2, minmax(0, 1fr)); grid-template-columns: repeat(2, minmax(0, 1fr));
@ -3459,11 +3463,6 @@
border-color: var(--color-red-800); border-color: var(--color-red-800);
} }
} }
.dark\:bg-blue-600 {
&:where(.dark, .dark *) {
background-color: var(--color-blue-600);
}
}
.dark\:bg-blue-900 { .dark\:bg-blue-900 {
&:where(.dark, .dark *) { &:where(.dark, .dark *) {
background-color: var(--color-blue-900); background-color: var(--color-blue-900);
@ -3718,15 +3717,6 @@
} }
} }
} }
.dark\:hover\:bg-blue-700 {
&:where(.dark, .dark *) {
&:hover {
@media (hover: hover) {
background-color: var(--color-blue-700);
}
}
}
}
.dark\:hover\:bg-blue-800 { .dark\:hover\:bg-blue-800 {
&:where(.dark, .dark *) { &:where(.dark, .dark *) {
&:hover { &:hover {

View File

@ -19,3 +19,11 @@ sl-details.small-details::part(header) {
sl-details.small-details::part(base) { sl-details.small-details::part(base) {
font-size: var(--sl-input-font-size-small); font-size: var(--sl-input-font-size-small);
} }
@media (prefers-color-scheme: dark) {
sl-details::part(base) {
background-color: var(--color-gray-700);
border: solid 1px var(--color-gray-500);
color: var(--color-gray-50);
}
}