v1.3 - client and admin scaffolding
This commit is contained in:
@@ -1,194 +1,19 @@
|
||||
<script lang="ts">
|
||||
import { page } from '$app/state';
|
||||
import { operatorSession } from '$lib/session';
|
||||
import AdminShell from '$lib/components/AdminShell.svelte';
|
||||
import ClientShell from '$lib/components/ClientShell.svelte';
|
||||
|
||||
const links = [
|
||||
{ href: '/', label: 'Home' },
|
||||
{ href: '/raw-materials', label: 'Raw Materials' },
|
||||
{ href: '/mixes', label: 'Mix Master' },
|
||||
{ href: '/products', label: 'Products' },
|
||||
{ href: '/scenarios', label: 'Scenarios' }
|
||||
];
|
||||
let { children } = $props();
|
||||
|
||||
const isAdminRoute = $derived(page.url.pathname === '/admin' || page.url.pathname.startsWith('/admin/'));
|
||||
</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>
|
||||
{#if isAdminRoute}
|
||||
<AdminShell>
|
||||
{@render children()}
|
||||
</AdminShell>
|
||||
{:else}
|
||||
<ClientShell>
|
||||
{@render children()}
|
||||
</ClientShell>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user