fix(web): hide nav auth links on public pages (/welcome, /invite)
CI / Lint / Test / Vet (push) Successful in 11s
CI / Build & Import (push) Successful in 10s

Logged-out visitors on /welcome and /invite should not see Account or
Log out. Split Layout into Layout (authenticated, full nav) and
PublicLayout (public, brand-only header). WelcomePage + InvitePage
variants now use PublicLayout.
This commit is contained in:
2026-06-03 23:25:24 +02:00
parent 72bf8a5553
commit 070491261d
2 changed files with 761 additions and 676 deletions
+28 -4
View File
@@ -31,12 +31,36 @@ templ Layout(title string) {
</html> </html>
} }
// PublicLayout is the shell for unauthenticated pages (/welcome, /invite).
// Same structure as Layout but without the nav auth links — a visitor who is not
// logged in should not see "Account" or "Log out".
templ PublicLayout(title string) {
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<title>{ title }</title>
<script src="/static/htmx.min.js" defer></script>
@templ.Raw(styleTag)
</head>
<body>
<header>
<a href="/" class="brand">Tapir</a>
</header>
<main>
{ children... }
</main>
</body>
</html>
}
// WelcomePage is the public landing page (served at /welcome, outside the auth // WelcomePage is the public landing page (served at /welcome, outside the auth
// guard — ADR-012). Logged out: the tapir mascot, a one-line tagline, and a // guard — ADR-012). Logged out: the tapir mascot, a one-line tagline, and a
// single "Get Started" CTA into the shared Dex flow (sign-in and sign-up are the // single "Get Started" CTA into the shared Dex flow (sign-in and sign-up are the
// same URL). Logged in: a greeting plus links back into the app and to log out. // same URL). Logged in: a greeting plus links back into the app and to log out.
templ WelcomePage(user User, loggedIn bool) { templ WelcomePage(user User, loggedIn bool) {
@Layout("Tapir — Watch less, know more") { @PublicLayout("Tapir — Watch less, know more") {
<section class="welcome"> <section class="welcome">
<div class="welcome-hero"> <div class="welcome-hero">
<pre aria-hidden="true">@templ.Raw(welcomeHero)</pre> <pre aria-hidden="true">@templ.Raw(welcomeHero)</pre>
@@ -314,7 +338,7 @@ templ RegisterPage(email, errMsg string) {
// chrome (header nav) is appropriate — the visitor has no session yet — but the // chrome (header nav) is appropriate — the visitor has no session yet — but the
// shared Layout keeps the look consistent. // shared Layout keeps the look consistent.
templ InvitePage(email, token, errMsg string) { templ InvitePage(email, token, errMsg string) {
@Layout("Tapir — Set your password") { @PublicLayout("Tapir — Set your password") {
<article class="register"> <article class="register">
<h1>Set up your Tapir account</h1> <h1>Set up your Tapir account</h1>
<p class="meta">Invitation for { email }.</p> <p class="meta">Invitation for { email }.</p>
@@ -344,7 +368,7 @@ templ InvitePage(email, token, errMsg string) {
// InviteInvalidPage is shown when an invite token is missing, expired, or already // InviteInvalidPage is shown when an invite token is missing, expired, or already
// used — a dead-end with no form, so a stale or replayed link reads clearly. // used — a dead-end with no form, so a stale or replayed link reads clearly.
templ InviteInvalidPage() { templ InviteInvalidPage() {
@Layout("Tapir — Invitation") { @PublicLayout("Tapir — Invitation") {
<article class="register"> <article class="register">
<h1>This invite link is no longer valid</h1> <h1>This invite link is no longer valid</h1>
<p>This invitation has expired or has already been used. Ask for a fresh invite link, or log in if you already have an account.</p> <p>This invitation has expired or has already been used. Ask for a fresh invite link, or log in if you already have an account.</p>
@@ -358,7 +382,7 @@ templ InviteInvalidPage() {
// dev "deployed-only" degrade). showLogin adds a log-in CTA where that is the // dev "deployed-only" degrade). showLogin adds a log-in CTA where that is the
// natural next step. // natural next step.
templ InviteNoticePage(message string, showLogin bool) { templ InviteNoticePage(message string, showLogin bool) {
@Layout("Tapir — Invitation") { @PublicLayout("Tapir — Invitation") {
<article class="register"> <article class="register">
<h1>Invitation</h1> <h1>Invitation</h1>
<p>{ message }</p> <p>{ message }</p>
File diff suppressed because it is too large Load Diff