web: move account nav to header
This commit is contained in:
parent
2285277060
commit
11663830e3
|
|
@ -125,6 +125,7 @@
|
||||||
.workspace-layout {
|
.workspace-layout {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: minmax(220px, 280px) minmax(0, 1fr);
|
grid-template-columns: minmax(220px, 280px) minmax(0, 1fr);
|
||||||
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: 100dvh;
|
height: 100dvh;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
|
@ -135,7 +136,69 @@
|
||||||
.workspace-layout.sidebar-collapsed {
|
.workspace-layout.sidebar-collapsed {
|
||||||
grid-template-columns: max-content minmax(0, 1fr);
|
grid-template-columns: max-content minmax(0, 1fr);
|
||||||
}
|
}
|
||||||
|
.workspace-topbar {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 3.25rem;
|
||||||
|
padding: 0 var(--space-5);
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
background: color-mix(in srgb, var(--bg-raised) 88%, transparent);
|
||||||
|
backdrop-filter: blur(14px);
|
||||||
|
}
|
||||||
|
.workspace-topbar-title {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
color: var(--text-strong);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
.workspace-topbar-logo {
|
||||||
|
display: inline-grid;
|
||||||
|
width: 1.8rem;
|
||||||
|
height: 1.8rem;
|
||||||
|
place-items: center;
|
||||||
|
border-radius: var(--radius-soft);
|
||||||
|
background: var(--interactive-hover);
|
||||||
|
color: var(--text-strong);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 800;
|
||||||
|
}
|
||||||
|
.workspace-topbar-actions {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
.topbar-icon-button {
|
||||||
|
display: inline-flex;
|
||||||
|
width: 2.35rem;
|
||||||
|
height: 2.35rem;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
border-radius: 999px;
|
||||||
|
color: var(--text-muted);
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
.topbar-icon-button:hover,
|
||||||
|
.topbar-icon-button:focus-visible {
|
||||||
|
background: var(--interactive-hover);
|
||||||
|
color: var(--text-muted);
|
||||||
|
}
|
||||||
|
.topbar-icon {
|
||||||
|
width: 1.1rem;
|
||||||
|
height: 1.1rem;
|
||||||
|
fill: none;
|
||||||
|
stroke: currentColor;
|
||||||
|
stroke-width: 2;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
.shell {
|
.shell {
|
||||||
|
grid-column: 2;
|
||||||
|
grid-row: 2;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
gap: var(--space-6);
|
gap: var(--space-6);
|
||||||
|
|
@ -160,6 +223,7 @@
|
||||||
@media (max-width: 760px) {
|
@media (max-width: 760px) {
|
||||||
.workspace-layout {
|
.workspace-layout {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
|
grid-template-rows: auto auto 1fr;
|
||||||
width: 100vw;
|
width: 100vw;
|
||||||
height: auto;
|
height: auto;
|
||||||
min-height: 100dvh;
|
min-height: 100dvh;
|
||||||
|
|
@ -168,7 +232,18 @@
|
||||||
.workspace-layout.sidebar-collapsed {
|
.workspace-layout.sidebar-collapsed {
|
||||||
grid-template-columns: 1fr;
|
grid-template-columns: 1fr;
|
||||||
}
|
}
|
||||||
|
.workspace-sidebar {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1;
|
||||||
|
}
|
||||||
|
.workspace-topbar {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 2;
|
||||||
|
padding: 0 var(--space-4);
|
||||||
|
}
|
||||||
.shell {
|
.shell {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 3;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
padding: var(--space-5) var(--space-4);
|
padding: var(--space-5) var(--space-4);
|
||||||
}
|
}
|
||||||
|
|
@ -176,6 +251,8 @@
|
||||||
}
|
}
|
||||||
@layer components {
|
@layer components {
|
||||||
.workspace-sidebar {
|
.workspace-sidebar {
|
||||||
|
grid-column: 1;
|
||||||
|
grid-row: 1 / 3;
|
||||||
align-self: stretch;
|
align-self: stretch;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
|
|
|
||||||
|
|
@ -385,8 +385,14 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
||||||
new URL("../auth/api.ts", import.meta.url),
|
new URL("../auth/api.ts", import.meta.url),
|
||||||
);
|
);
|
||||||
const rootLayout = await Deno.readTextFile(
|
const rootLayout = await Deno.readTextFile(
|
||||||
|
new URL("./../../../routes/+layout.svelte", import.meta.url),
|
||||||
|
);
|
||||||
|
const rootLayoutLoad = await Deno.readTextFile(
|
||||||
new URL("./../../../routes/+layout.ts", import.meta.url),
|
new URL("./../../../routes/+layout.ts", import.meta.url),
|
||||||
);
|
);
|
||||||
|
const sidebar = await Deno.readTextFile(
|
||||||
|
new URL("../sidebar/WorkspaceSidebar.svelte", import.meta.url),
|
||||||
|
);
|
||||||
|
|
||||||
assert(
|
assert(
|
||||||
accountPage.includes("registerPasskey") &&
|
accountPage.includes("registerPasskey") &&
|
||||||
|
|
@ -412,7 +418,16 @@ Deno.test("Account UI owns browser passkey session state without workspace autho
|
||||||
"Auth model should stay on Backend auth APIs rather than workspace authorization APIs",
|
"Auth model should stay on Backend auth APIs rather than workspace authorization APIs",
|
||||||
);
|
);
|
||||||
assert(
|
assert(
|
||||||
rootLayout.includes('"/account"') && rootLayout.includes('"/login/device"'),
|
rootLayout.includes("workspace-topbar") &&
|
||||||
|
rootLayout.includes("topbar-icon-button") &&
|
||||||
|
rootLayout.includes('href="/account"') &&
|
||||||
|
rootLayout.includes("Open Account") &&
|
||||||
|
!sidebar.includes("accountHref") &&
|
||||||
|
!sidebar.includes("Open Account"),
|
||||||
|
"Account navigation should live in the global layout header, not the workspace sidebar",
|
||||||
|
);
|
||||||
|
assert(
|
||||||
|
rootLayoutLoad.includes('"/account"') && rootLayoutLoad.includes('"/login/device"'),
|
||||||
"Root layout should not redirect account and device-login public routes to a workspace",
|
"Root layout should not redirect account and device-login public routes to a workspace",
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@
|
||||||
|
|
||||||
let workspaceId = $derived(workspace?.workspace_id ?? '');
|
let workspaceId = $derived(workspace?.workspace_id ?? '');
|
||||||
let homeHref = $derived(workspaceId ? workspaceRoute(workspaceId) : '/');
|
let homeHref = $derived(workspaceId ? workspaceRoute(workspaceId) : '/');
|
||||||
let accountHref = '/account';
|
|
||||||
let settingsHref = $derived(workspaceId ? workspaceRoute(workspaceId, '/settings') : '/settings');
|
let settingsHref = $derived(workspaceId ? workspaceRoute(workspaceId, '/settings') : '/settings');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
@ -88,17 +87,6 @@
|
||||||
/>
|
/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
<a
|
|
||||||
class="sidebar-icon-button"
|
|
||||||
href={accountHref}
|
|
||||||
aria-label="Open Account"
|
|
||||||
title="Account"
|
|
||||||
>
|
|
||||||
<svg class="sidebar-icon" aria-hidden="true" viewBox="0 0 24 24">
|
|
||||||
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
|
|
||||||
<circle cx="12" cy="7" r="4" />
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a
|
<a
|
||||||
class="sidebar-icon-button"
|
class="sidebar-icon-button"
|
||||||
href={settingsHref}
|
href={settingsHref}
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,20 @@
|
||||||
collapsed={sidebarCollapsed}
|
collapsed={sidebarCollapsed}
|
||||||
onToggleCollapsed={() => (sidebarCollapsed = !sidebarCollapsed)}
|
onToggleCollapsed={() => (sidebarCollapsed = !sidebarCollapsed)}
|
||||||
/>
|
/>
|
||||||
|
<header class="workspace-topbar">
|
||||||
|
<div class="workspace-topbar-title" aria-label="Yoi Workspace">
|
||||||
|
<span class="workspace-topbar-logo" aria-hidden="true">Y</span>
|
||||||
|
<span>Yoi</span>
|
||||||
|
</div>
|
||||||
|
<nav class="workspace-topbar-actions" aria-label="Global navigation">
|
||||||
|
<a class="topbar-icon-button" href="/account" aria-label="Open Account" title="Account">
|
||||||
|
<svg class="topbar-icon" aria-hidden="true" viewBox="0 0 24 24">
|
||||||
|
<path d="M19 21v-2a4 4 0 0 0-4-4H9a4 4 0 0 0-4 4v2" />
|
||||||
|
<circle cx="12" cy="7" r="4" />
|
||||||
|
</svg>
|
||||||
|
</a>
|
||||||
|
</nav>
|
||||||
|
</header>
|
||||||
<main class="shell">
|
<main class="shell">
|
||||||
{@render children()}
|
{@render children()}
|
||||||
</main>
|
</main>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user