brewlog/templates/register.html
Jon Seager fb67f0659c
feat(ui): redesign with theme tokens, dark mode, and reusable components
Overhaul the web UI with CSS custom property-based theming and
dark mode support. Extract reusable template partials (location
search, scan input) and web components (photo-capture,
searchable-select). Add version and commit info to page footer.

- Replace hardcoded amber palette with semantic theme tokens
- Add dark mode with localStorage persistence and system preference detection
- Extract <brew-photo-capture> and <searchable-select> web components
- Extract location_search and scan_input Askama macros
- Add sun/moon, timeline, database, map icons
- Display version and git commit in footer
- Improve timeline coordinate rounding and map link formatting
- Add roast_name, roaster_name, remaining to BagOptionView
2026-02-05 17:03:09 +00:00

123 lines
4.3 KiB
HTML

{% extends "base.html" %}
{% block title %}Brewlog · Register{% endblock %}
{% block head %}
<script src="/webauthn.js"></script>
{% endblock %}
{% block content %}
<div class="mx-auto max-w-md">
<div class="rounded-lg border bg-surface p-6">
<h1 class="text-2xl font-semibold text-accent">Register</h1>
<p class="mt-2 text-sm text-stone-600">
Create your account by registering a passkey. This will be used to sign in going forward.
</p>
<div id="register-error" class="mt-4 hidden rounded-md bg-red-100 border border-red-300 p-3 text-sm text-red-800"></div>
<div id="register-unsupported" class="mt-4 hidden rounded-md bg-yellow-100 border border-yellow-300 p-3 text-sm text-yellow-800">
Your browser does not support passkeys. Please use a modern browser (Chrome, Firefox, Safari, or Edge).
</div>
<div id="register-form" class="mt-6 flex flex-col gap-4">
<label class="flex flex-col gap-1 text-sm">
<span class="text-stone-700">Display Name</span>
<input
type="text"
id="display-name"
required
autofocus
class="input-field"
placeholder="Your name"
/>
</label>
<label class="flex flex-col gap-1 text-sm">
<span class="text-stone-700">Passkey Name</span>
<input
type="text"
id="passkey-name"
required
class="input-field"
placeholder="e.g. MacBook Touch ID, iPhone"
/>
</label>
<button
id="register-button"
type="button"
class="mt-2 w-full rounded-md bg-accent px-4 py-3 text-sm font-semibold text-accent-text transition hover:bg-accent-hover disabled:opacity-50 disabled:cursor-not-allowed"
>
Register Passkey
</button>
<div id="register-loading" class="mt-2 hidden text-center text-sm text-stone-500">
<p>Follow the prompts from your browser or device...</p>
</div>
</div>
<div id="register-success" class="mt-6 hidden">
<div class="rounded-md bg-green-100 border border-green-300 p-4 text-sm text-green-800">
<p class="font-semibold">Registration successful!</p>
<p class="mt-1">Your passkey has been registered and you are now signed in.</p>
</div>
<a
href="/"
class="mt-4 block w-full rounded-md bg-accent px-4 py-3 text-center text-sm font-semibold text-accent-text transition hover:bg-accent-hover"
>
Go to Brewlog
</a>
</div>
</div>
</div>
<script>
document.addEventListener("DOMContentLoaded", function () {
const token = "{{ token }}";
const button = document.getElementById("register-button");
const displayNameInput = document.getElementById("display-name");
const passkeyNameInput = document.getElementById("passkey-name");
const errorDiv = document.getElementById("register-error");
const loadingDiv = document.getElementById("register-loading");
const unsupportedDiv = document.getElementById("register-unsupported");
const formDiv = document.getElementById("register-form");
const successDiv = document.getElementById("register-success");
// Check WebAuthn support
if (!window.PublicKeyCredential) {
button.disabled = true;
unsupportedDiv.classList.remove("hidden");
return;
}
button.addEventListener("click", async function () {
const displayName = displayNameInput.value.trim();
if (!displayName) {
errorDiv.textContent = "Please enter a display name.";
errorDiv.classList.remove("hidden");
return;
}
const passkeyName = passkeyNameInput.value.trim();
if (!passkeyName) {
errorDiv.textContent = "Please enter a name for this passkey.";
errorDiv.classList.remove("hidden");
return;
}
errorDiv.classList.add("hidden");
loadingDiv.classList.remove("hidden");
button.disabled = true;
try {
await startPasskeyRegistration(token, displayName, passkeyName);
formDiv.classList.add("hidden");
successDiv.classList.remove("hidden");
} catch (err) {
errorDiv.textContent = err.message;
errorDiv.classList.remove("hidden");
loadingDiv.classList.add("hidden");
button.disabled = false;
}
});
});
</script>
{% endblock %}