Accounts Phase 1c: sign-in UI, magic-link landing, auth store

- Shared reactive auth store (auth.user) + postJSON helper (sends the cookie).
- SignIn modal: email -> "check your inbox" (calm, no password); Google slots in
  here in Phase 2.
- /auth/verify route exchanges the magic-link token for a session, then home.
- Header shows "Sign in" or an account avatar; the You sheet gains "Signed in as
  …" + Sign out (or a Sign in row). Anonymous browsing is unchanged.
This commit is contained in:
jay
2026-06-03 01:19:30 +00:00
parent d2ae56dc65
commit 9237180608
6 changed files with 310 additions and 2 deletions
@@ -0,0 +1,54 @@
<script>
import { onMount } from 'svelte';
import { goto } from '$app/navigation';
import { verifyToken } from '$lib/auth.svelte.js';
let status = $state('verifying'); // verifying | error
let error = $state('');
onMount(async () => {
const token = new URLSearchParams(window.location.search).get('token');
if (!token) {
status = 'error';
error = 'This sign-in link is missing its token.';
return;
}
try {
await verifyToken(token);
goto('/', { replaceState: true }); // signed in — back home
} catch (e) {
status = 'error';
error = e?.message || 'This sign-in link is invalid or has expired.';
}
});
</script>
<main class="container verify">
{#if status === 'verifying'}
<p class="muted">Signing you in…</p>
{:else}
<h1>Couldn't sign you in</h1>
<p class="muted">{error}</p>
<a class="back" href="/">← Back to Upbeat Bytes</a>
{/if}
</main>
<style>
.verify {
min-height: 60vh;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
gap: 12px;
padding: 60px 20px;
}
h1 {
font-size: 1.8rem;
}
.back {
color: var(--accent-deep);
text-decoration: underline;
}
</style>