Menu
Public repository / 08dd5db33217bf8fcf9f3f0bcf7ca2f409e8c1a4
AkurAI-Mail / src/routes/(admin)/+layout.svelte
Latest change 591c6803063311fb315b42290e680cbd6786b101 - admin: replace VM Status page with link to monitoring system by Ólafur Búi Ólafsson
<script lang="ts">
import { page } from "$app/stores";
let { data, children } = $props();
let hostname = $derived(data.hostname);
let currentPath = $derived($page.url.pathname);
const navItems = [
{ href: "/", label: "Dashboard" },
{ href: "/mailboxes", label: "Mailboxes" },
{ href: "/domains", label: "Domains" },
{ href: "/antispam", label: "Anti-spam" },
{ href: "/dns", label: "DNS" },
{ href: "https://monitor.olibuijr.com/", label: "Monitoring", external: true },
{ href: "/webmail", label: "Webmail" }
];
function isActive(href: string) {
if (href === "/") return currentPath === "/";
return currentPath.startsWith(href);
}
</script>
<main class="shell">
<aside class="rail">
<a class="brand" href="/" aria-label="AkurAI Mail admin home">
<span class="mark">A</span>
<span><strong>AkurAI Mail</strong><small>{hostname}</small></span>
</a>
<nav aria-label="Primary">
{#each navItems as item}
{#if item.external}
<a href={item.href} target="_blank" rel="noopener noreferrer">{item.label} ↗</a>
{:else}
<a class:active={isActive(item.href)} href={item.href}>{item.label}</a>
{/if}
{/each}
</nav>
<a class="signout" href="/api/logout">Sign out</a>
</aside>
<section class="workspace">
{@render children()}
</section>
</main>
<style>
:global(*){box-sizing:border-box}
:global(html),:global(body){margin:0;overflow-x:hidden}
:global(body){font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#f4f6f5;color:#151d1b}
:global(button),:global(input),:global(select){font:inherit}
.shell{min-height:100dvh;display:grid;grid-template-columns:248px minmax(0,1fr)}
.rail{position:sticky;top:0;height:100dvh;background:#0d1b18;color:#edf7f3;padding:20px;display:flex;flex-direction:column;gap:20px}
.brand{display:flex;gap:12px;align-items:center;color:inherit;text-decoration:none;min-width:0}
.brand span:last-child{min-width:0}.brand strong,.brand small{display:block;overflow-wrap:anywhere}.brand small{margin-top:2px;color:#96aaa3;font-size:12px}
.mark{display:grid;place-items:center;flex:0 0 auto;width:40px;height:40px;border-radius:8px;background:#c8f05a;color:#0d1b18;font-weight:900}
nav{display:grid;gap:4px}nav a,.signout{color:#dce9e4;text-decoration:none;border-radius:6px;padding:10px 11px;min-height:44px;display:flex;align-items:center}
nav a:hover,.signout:hover,nav a.active{background:#1c332e}.signout{margin-top:auto;color:#b8c9c3}
.workspace{padding:26px;display:grid;gap:18px;min-width:0;align-content:start}
@media(max-width:760px){.shell{grid-template-columns:minmax(0,1fr)}.rail{position:static;height:auto;padding:14px 16px;gap:12px}.brand small{font-size:12px}nav{display:flex;overflow-x:auto;gap:6px;scrollbar-width:none}nav::-webkit-scrollbar{display:none}nav a{white-space:nowrap;background:rgba(255,255,255,.06)}.signout{margin-top:0}.workspace{padding:16px}}
</style>