feat: gate registration behind 16 March 2026 19:00 opening date

- Add REGISTRATION_OPENS_AT const in lib/opening.ts as single source of truth
- Add useRegistrationOpen hook with live 1s countdown ticker
- Add CountdownBanner component (DD/HH/MM/SS) with canvas-confetti on open
- EventRegistrationForm shows countdown instead of form while closed
- Login page hides/disables signup while closed; login always available
- WatcherForm AccountModal guards signUp call as defence-in-depth

Closes #2
This commit is contained in:
2026-03-10 13:18:30 +01:00
parent 2f0dc46469
commit cf47f25a4d
8 changed files with 305 additions and 58 deletions

View File

@@ -12,6 +12,8 @@ import {
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { authClient } from "@/lib/auth-client";
import { REGISTRATION_OPENS_AT } from "@/lib/opening";
import { useRegistrationOpen } from "@/lib/useRegistrationOpen";
import { orpc } from "@/utils/orpc";
export const Route = createFileRoute("/login")({
@@ -31,6 +33,7 @@ export const Route = createFileRoute("/login")({
function LoginPage() {
const navigate = useNavigate();
const search = Route.useSearch();
const { isOpen } = useRegistrationOpen();
const [isSignup, setIsSignup] = useState(() => search.signup === "1");
const [email, setEmail] = useState(() => search.email ?? "");
const [password, setPassword] = useState("");
@@ -204,6 +207,43 @@ function LoginPage() {
Terug naar website
</Link>
</div>
) : isSignup && !isOpen ? (
/* Signup is closed until registration opens */
<div className="space-y-4">
<div className="rounded-lg border border-white/10 bg-white/5 p-5 text-center">
<p className="mb-1 font-['Intro',sans-serif] text-white">
Registratie nog niet open
</p>
<p className="text-sm text-white/60">
Accounts aanmaken kan vanaf{" "}
<span className="text-teal-300">
{REGISTRATION_OPENS_AT.toLocaleDateString("nl-BE", {
weekday: "long",
day: "numeric",
month: "long",
})}{" "}
om{" "}
{REGISTRATION_OPENS_AT.toLocaleTimeString("nl-BE", {
hour: "2-digit",
minute: "2-digit",
})}
</span>
.
</p>
</div>
<div className="flex flex-col gap-2 text-center">
<button
type="button"
onClick={() => setIsSignup(false)}
className="text-sm text-white/60 hover:text-white"
>
Al een account? Log in
</button>
<Link to="/" className="text-sm text-white/60 hover:text-white">
Terug naar website
</Link>
</div>
</div>
) : (
<form onSubmit={handleSubmit} className="space-y-4">
{isSignup && (
@@ -271,15 +311,29 @@ function LoginPage() {
: "Inloggen"}
</Button>
<div className="flex flex-col gap-2 text-center">
<button
type="button"
onClick={() => setIsSignup(!isSignup)}
className="text-sm text-white/60 hover:text-white"
>
{isSignup
? "Al een account? Log in"
: "Nog geen account? Registreer"}
</button>
{/* Only show signup toggle when registration is open */}
{isOpen && (
<button
type="button"
onClick={() => setIsSignup(!isSignup)}
className="text-sm text-white/60 hover:text-white"
>
{isSignup
? "Al een account? Log in"
: "Nog geen account? Registreer"}
</button>
)}
{!isOpen && isSignup === false && (
<button
type="button"
onClick={() => setIsSignup(true)}
className="cursor-not-allowed text-sm text-white/30"
disabled
title="Registratie opent op 16 maart om 19:00"
>
Nog geen account? (opent 16 maart)
</button>
)}
<Link to="/" className="text-sm text-white/60 hover:text-white">
Terug naar website
</Link>