Begin redesign

This commit is contained in:
2025-06-15 10:07:46 +02:00
parent bf1d119bd8
commit d9e0052003
11 changed files with 344 additions and 2 deletions

View File

@ -0,0 +1,50 @@
<!DOCTYPE html>
<html lang="en">
<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 min-h-screen flex flex-col">
<!-- Optional: Shoelace modals, toasts -->
<sl-alert id="global-alert" variant="primary" duration="4000" closable></sl-alert>
<sl-dialog id="global-dialog" label="Dialog"></sl-dialog>
<!-- 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" 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">
{% 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="p-4" id="maincontent">
{% block content %}{% endblock %}
</div>
</main>
</div>
</div>
{% block scripts %}
{% include 'base/partials/scripts.html.j2' %}
{% endblock %}
</body>
</html>

View File

@ -0,0 +1,41 @@
{% extends "/base/base.html.j2" %}
{% block title %}{{ title or "Page" }}{% endblock %}
{% block breadcrumbs %}
<div class="p-4 bg-white block sm:flex items-center justify-between border-b border-gray-200">
<nav class="text-sm text-gray-500" aria-label="Breadcrumb">
<sl-breadcrumb>
<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>
{% 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>
{% block page_content %}
<p>This is a generic page.</p>
{% endblock %}
</section>
{% endblock %}

View File

@ -0,0 +1,77 @@
<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 -->
<a href="/" class="flex ml-2 md:mr-24">
<img
src="{{ url_for('static', path='logo.svg') }}"
class="h-11 mr-3"
alt="Sshecret Logo"
/>
<span
class="self-center text-xl font-semibold sm:text-2xl whitespace-nowrap dark:text-white"
>Sshecret</span
>
</a>
</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,43 @@
<!-- Sidebar Container -->
<!-- Top: Brand -->
<div class="px-4 py-6">
<a href="/" class="text-xl font-semibold text-gray-800">
🐚 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">
<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">
<sl-icon name="person-fill-lock"> </sl-icon>
Clients
</a>
</li>
<li>
<a href="/admin/secrets" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100">
<sl-icon name="database-lock"></sl-icon>
Secrets
</a>
</li>
<li>
<a href="/admin/audit" class="flex items-center px-3 py-2 rounded-md text-sm font-medium text-gray-700 hover:bg-gray-100">
<sl-icon name="card-list"></sl-icon>
Audit Log
</a>
</li>
</ul>
</nav>

View File

@ -0,0 +1,37 @@
<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');"
/>

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

@ -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="py-8 px-4 mt-4 mx-auto max-w-screen-xl text-center lg:py-16">

View File

@ -13,7 +13,7 @@
{% 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">
<main class="flex-grow p-4 order-2 md:order-1">
<main>
{% block content %}
{% endblock %}
</main>

View File

@ -107,6 +107,37 @@ def create_router(dependencies: FrontendDependencies) -> APIRouter:
},
)
@app.get("/clients/new/")
async def get_new_client_tree(
request: Request,
current_user: Annotated[LocalUserInfo, Depends(dependencies.get_user_info)],
admin: Annotated[AdminBackend, Depends(dependencies.get_admin_backend)],
) -> Response:
"""Get client tree view."""
page = 1
per_page = CLIENTS_PER_PAGE
offset = 0
client_filter = ClientFilter(offset=offset, limit=per_page)
results = await admin.query_clients(client_filter)
paginate = PagingInfo(
page=page, limit=per_page, total=results.total_results, offset=offset
)
LOG.info("Results %r", results)
return templates.TemplateResponse(
request,
"clients/redesign.html.j2",
{
"page_title": "Clients",
"offset": offset,
"pages": paginate,
"clients": results.clients,
"user": current_user,
"results": results,
},
)
@app.get("/clients/page/{page}")
async def get_client_page(
request: Request,

View File

@ -2854,6 +2854,11 @@
height: 100vh;
}
}
.md\:w-64 {
@media (width >= 48rem) {
width: calc(var(--spacing) * 64);
}
}
.md\:w-\[calc\(100\%-256px\)\] {
@media (width >= 48rem) {
width: calc(100% - 256px);
@ -2879,6 +2884,11 @@
grid-template-columns: repeat(2, minmax(0, 1fr));
}
}
.md\:grid-cols-\[300px_1fr\] {
@media (width >= 48rem) {
grid-template-columns: 300px 1fr;
}
}
.md\:flex-row {
@media (width >= 48rem) {
flex-direction: row;