Files
data-entry-app/frontend/src/routes/+layout.svelte
T

195 lines
4.0 KiB
Svelte
Raw Normal View History

2026-04-25 20:43:37 +12:00
<script lang="ts">
import { page } from '$app/state';
import { operatorSession } from '$lib/session';
const links = [
{ href: '/', label: 'Home' },
{ href: '/raw-materials', label: 'Raw Materials' },
{ href: '/mixes', label: 'Mix Master' },
{ href: '/products', label: 'Products' },
{ href: '/scenarios', label: 'Scenarios' }
];
</script>
<svelte:head>
<title>Data Entry App</title>
</svelte:head>
<div class="shell">
<header class="topbar">
<div class="brand-block">
<a class="brand" href="/">Data Entry App</a>
<p>Operator costing workflow</p>
</div>
<nav class="topnav" aria-label="Primary navigation">
{#each links as link}
<a class:active={page.url.pathname === link.href} href={link.href}>{link.label}</a>
{/each}
</nav>
<div class="session-panel">
{#if $operatorSession}
<div>
<span>Signed in</span>
<strong>{$operatorSession.name}</strong>
</div>
<button type="button" onclick={() => operatorSession.clear()}>Sign out</button>
{:else}
<a class="login-link" href="/">Operator login</a>
{/if}
</div>
</header>
<main class="content">
<slot />
</main>
</div>
<style>
:global(:root) {
--canvas: #f5efe4;
--canvas-strong: #fffaf1;
--ink: #20170f;
--muted: #695746;
--line: rgba(74, 53, 31, 0.14);
--brand: #8f4f1f;
--brand-deep: #5a2d18;
--accent: #d9a441;
--shadow: 0 18px 50px rgba(56, 38, 19, 0.12);
}
:global(body) {
margin: 0;
color: var(--ink);
font-family: "Segoe UI", "Helvetica Neue", sans-serif;
background:
radial-gradient(circle at top right, rgba(217, 164, 65, 0.22), transparent 24rem),
radial-gradient(circle at left center, rgba(143, 79, 31, 0.1), transparent 28rem),
linear-gradient(180deg, #f7f1e7 0%, #efe5d3 100%);
}
:global(*) {
box-sizing: border-box;
}
.shell {
min-height: 100vh;
}
.topbar {
position: sticky;
top: 0;
z-index: 10;
display: grid;
grid-template-columns: auto 1fr auto;
gap: 1rem;
align-items: center;
padding: 1rem 2rem;
backdrop-filter: blur(16px);
background: rgba(247, 241, 231, 0.88);
border-bottom: 1px solid var(--line);
}
.brand-block {
display: flex;
flex-direction: column;
gap: 0.15rem;
}
.brand {
color: var(--brand-deep);
text-decoration: none;
font-size: 1.25rem;
font-weight: 700;
letter-spacing: 0.02em;
}
.brand-block p,
.session-panel span {
margin: 0;
color: var(--muted);
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.08em;
}
.topnav {
display: flex;
justify-content: center;
flex-wrap: wrap;
gap: 0.65rem;
}
.topnav a,
.login-link,
button {
border: 1px solid transparent;
border-radius: 999px;
padding: 0.75rem 1rem;
color: var(--brand-deep);
text-decoration: none;
background: transparent;
font: inherit;
cursor: pointer;
transition:
border-color 160ms ease,
background-color 160ms ease,
transform 160ms ease;
}
.topnav a:hover,
.topnav a.active,
.login-link:hover,
button:hover {
background: rgba(143, 79, 31, 0.08);
border-color: rgba(143, 79, 31, 0.18);
transform: translateY(-1px);
}
.topnav a.active {
background: linear-gradient(135deg, rgba(143, 79, 31, 0.14), rgba(217, 164, 65, 0.18));
font-weight: 600;
}
.session-panel {
display: flex;
align-items: center;
gap: 0.9rem;
}
.session-panel strong {
display: block;
}
button {
background: var(--brand-deep);
color: #fff7ef;
box-shadow: var(--shadow);
}
button:hover {
background: #472213;
}
.content {
padding: 2rem;
}
@media (max-width: 980px) {
.topbar {
grid-template-columns: 1fr;
justify-items: start;
padding: 1rem 1rem 0.9rem;
}
.topnav {
justify-content: flex-start;
}
.content {
padding: 1rem;
}
}
</style>