132 lines
9.4 KiB
HTML
132 lines
9.4 KiB
HTML
{% import "macros/ui.html" as ui %}
|
|
<!doctype html>
|
|
<html lang="{{ lang | default(value='sk') }}" data-theme="dark">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>{% block title %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock title %}</title>
|
|
<meta name="description" content="{% block meta_description %}{{ t(key="meta-description", lang=lang | default(value='sk')) }}{% endblock meta_description %}">
|
|
<link rel="icon" type="image/x-icon" href="/static/favicon/favicon.ico">
|
|
<link rel="icon" type="image/png" sizes="32x32" href="/static/favicon/favicon-32x32.png">
|
|
<link rel="icon" type="image/png" sizes="16x16" href="/static/favicon/favicon-16x16.png">
|
|
<link rel="apple-touch-icon" sizes="180x180" href="/static/favicon/apple-touch-icon.png">
|
|
<link rel="manifest" href="/static/favicon/site.webmanifest">
|
|
<script>
|
|
function applyTheme(t) {
|
|
var dark = t === 'dark'
|
|
|| (t === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
document.documentElement.setAttribute('data-theme', dark ? 'dark' : 'light');
|
|
}
|
|
function setTheme(t) {
|
|
localStorage.setItem('theme', t);
|
|
applyTheme(t);
|
|
document.dispatchEvent(new CustomEvent('theme:changed', { detail: t }));
|
|
}
|
|
function currentTheme() { return localStorage.getItem('theme') || 'dark'; }
|
|
applyTheme(currentTheme());
|
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', function () {
|
|
if (currentTheme() === 'system') applyTheme('system');
|
|
});
|
|
function markActiveNav() {
|
|
var path = location.pathname;
|
|
document.querySelectorAll('a[data-nav]').forEach(function (a) {
|
|
var h = a.getAttribute('data-nav');
|
|
var on = h === path || (h !== '/' && path.indexOf(h) === 0);
|
|
if (on) a.setAttribute('aria-current', 'page');
|
|
else a.removeAttribute('aria-current');
|
|
});
|
|
}
|
|
document.addEventListener('DOMContentLoaded', markActiveNav);
|
|
document.addEventListener('htmx:afterSwap', markActiveNav);
|
|
</script>
|
|
<link href="/static/css/app.css?v=2026-06-16" rel="stylesheet" type="text/css">
|
|
{% block head %}{% endblock head %}
|
|
<script src="/static/vendor/htmx/htmx-1.9.12.min.js"></script>
|
|
<script defer src="/static/vendor/alpine/alpinejs-3.14.9.min.js"></script>
|
|
</head>
|
|
<body
|
|
x-data="{ showSidebar: false }"
|
|
class="min-h-screen bg-surface text-on-surface antialiased dark:bg-surface-dark dark:text-on-surface-dark">
|
|
|
|
<!-- dark overlay for the open sidebar on small screens -->
|
|
<div x-cloak x-show="showSidebar" x-transition.opacity aria-hidden="true"
|
|
@click="showSidebar = false"
|
|
class="fixed inset-0 z-30 bg-black/50 md:hidden"></div>
|
|
|
|
<!-- sidebar -->
|
|
<nav aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
|
|
x-bind:class="showSidebar ? 'translate-x-0' : '-translate-x-60'"
|
|
class="fixed inset-y-0 left-0 z-40 flex w-60 flex-col border-r border-outline bg-surface-alt transition-transform duration-300 md:translate-x-0 dark:border-outline-dark dark:bg-surface-dark-alt">
|
|
|
|
{# Sidebar nav links — adapted from the vendored Penguin UI component
|
|
penguinui-components/sidebar/simple-sidebar.html: Penguin's link
|
|
treatment (hover:bg-primary/5, focus-visible:underline) with the active
|
|
state (bg-primary/10 + text-on-surface-strong) mapped onto our
|
|
data-nav / aria-current so markActiveNav() keeps driving it. #}
|
|
<a href="/admin/dashboard"
|
|
class="flex h-16 items-center gap-2 border-b border-outline px-6 text-lg font-bold tracking-tight text-on-surface-strong dark:border-outline-dark dark:text-on-surface-dark-strong">
|
|
{{ t(key="admin-title", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
|
|
<div class="flex flex-1 flex-col gap-1 overflow-y-auto p-4">
|
|
<a href="/admin/dashboard" data-nav="/admin/dashboard"
|
|
class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
|
{{ t(key="admin-dashboard", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
<a href="/admin/catalog/products" data-nav="/admin/catalog/products"
|
|
class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
|
{{ t(key="admin-products", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
<a href="/admin/catalog/categories" data-nav="/admin/catalog/categories"
|
|
class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
|
{{ t(key="admin-categories", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
<a href="/admin/orders" data-nav="/admin/orders"
|
|
class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
|
{{ t(key="admin-orders", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
<a href="/admin/shipping" data-nav="/admin/shipping"
|
|
class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-on-surface underline-offset-2 transition hover:bg-primary/5 hover:text-on-surface-strong focus:outline-hidden focus-visible:underline aria-[current=page]:bg-primary/10 aria-[current=page]:text-on-surface-strong dark:text-on-surface-dark dark:hover:bg-primary-dark/5 dark:hover:text-on-surface-dark-strong dark:aria-[current=page]:bg-primary-dark/10 dark:aria-[current=page]:text-on-surface-dark-strong">
|
|
{{ t(key="admin-shipping", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
</div>
|
|
|
|
<div class="border-t border-outline p-4 dark:border-outline-dark">
|
|
<a href="/" class="flex items-center gap-2 rounded-radius px-2 py-1.5 text-sm font-medium text-info underline-offset-2 transition hover:bg-info/5 focus:outline-hidden focus-visible:underline">
|
|
{{ t(key="admin-exit", lang=lang | default(value='sk')) }}
|
|
</a>
|
|
<form method="post" action="/logout">
|
|
<button type="submit" class="flex w-full items-center gap-2 rounded-radius px-2 py-1.5 text-left text-sm font-medium text-danger underline-offset-2 transition hover:bg-danger/5 focus:outline-hidden focus-visible:underline">
|
|
{{ t(key="logout", lang=lang | default(value='sk')) }}
|
|
</button>
|
|
</form>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- content column -->
|
|
<div class="flex min-h-screen flex-col md:ml-60">
|
|
<header class="sticky top-0 z-20 flex h-16 items-center gap-4 border-b border-outline bg-surface/95 px-4 backdrop-blur dark:border-outline-dark dark:bg-surface-dark/95">
|
|
<!-- Penguin animated hamburger (bars ↔ X) in our ghost-square shell -->
|
|
<button type="button" @click="showSidebar = !showSidebar" :aria-expanded="showSidebar" aria-label="{{ t(key='menu', lang=lang | default(value='sk')) }}"
|
|
class="inline-flex size-9 shrink-0 items-center justify-center rounded-radius bg-transparent text-secondary transition hover:opacity-75 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-secondary active:opacity-100 active:outline-offset-0 md:hidden dark:text-secondary-dark dark:focus-visible:outline-secondary-dark">
|
|
{{ ui::icon(name="hamburger", size="size-6", attrs='x-show="!showSidebar"') }}
|
|
{{ ui::icon(name="close", size="size-6", attrs='x-cloak x-show="showSidebar"') }}
|
|
</button>
|
|
|
|
<span class="text-sm font-semibold text-on-surface-strong dark:text-on-surface-dark-strong">
|
|
{% block crumb %}{{ t(key="admin-title", lang=lang | default(value='sk')) }}{% endblock crumb %}
|
|
</span>
|
|
|
|
<!-- settings (language + theme) dropdown (self-contained Alpine state) -->
|
|
<div class="ml-auto">
|
|
{% include "partials/settings_dropdown.html" %}
|
|
</div>
|
|
</header>
|
|
|
|
<main class="mx-auto w-full max-w-5xl flex-1 px-4 py-8">
|
|
{% block content %}{% endblock content %}
|
|
</main>
|
|
</div>
|
|
</body>
|
|
</html>
|