309 lines
8.6 KiB
TypeScript
309 lines
8.6 KiB
TypeScript
"use client";
|
|
|
|
import Image from "next/image";
|
|
import Link from "next/link";
|
|
import { useTranslations } from "next-intl";
|
|
import { useActionState, useState } from "react";
|
|
import { register } from "@/actions/register";
|
|
|
|
interface RegisterFormProps {
|
|
hotelName: string;
|
|
captcha: { provider: string; siteKey?: string };
|
|
error?: string;
|
|
}
|
|
|
|
export function RegisterForm({ hotelName, captcha, error }: RegisterFormProps) {
|
|
const t = useTranslations("pages.register");
|
|
const showCaptcha = captcha.provider !== "none" && !!captcha.siteKey;
|
|
const [serverError, formAction, isPending] = useActionState(register, null);
|
|
const [termsAccepted, setTermsAccepted] = useState(false);
|
|
|
|
return (
|
|
<div className="mx-auto w-full max-w-[800px]">
|
|
{/* Header Banner */}
|
|
<div
|
|
className="relative overflow-hidden bg-center bg-cover rounded-t-lg"
|
|
style={{
|
|
backgroundImage: "url(/assets/images/mybobba_banner.png)",
|
|
height: "180px",
|
|
maxHeight: "150px",
|
|
}}
|
|
>
|
|
<div className="absolute inset-0 z-10 bg-black/30" />
|
|
<div className="relative flex items-center justify-center w-full h-full z-20">
|
|
<div className="relative" style={{ width: "140px", height: "140px" }}>
|
|
<div
|
|
className="absolute inset-0 rounded-full overflow-hidden"
|
|
style={{ zIndex: -1 }}
|
|
>
|
|
<div
|
|
className="w-full h-full"
|
|
style={{
|
|
background: "black",
|
|
filter: "blur(8px)",
|
|
transform: "scale(1.2)",
|
|
opacity: 0.6,
|
|
}}
|
|
/>
|
|
</div>
|
|
<Image
|
|
src="/assets/images/FrankwithBag.gif"
|
|
alt=""
|
|
fill
|
|
className="!static"
|
|
style={{
|
|
objectFit: "contain",
|
|
objectPosition: "center",
|
|
width: "100%",
|
|
height: "100%",
|
|
}}
|
|
/>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Form */}
|
|
<form
|
|
action={formAction}
|
|
className="p-4 flex flex-col gap-6"
|
|
style={{
|
|
backgroundColor:
|
|
"color-mix(in srgb, var(--color-background) 50%, var(--color-surface))",
|
|
borderRadius: "0 0 8px 8px",
|
|
}}
|
|
>
|
|
{error && (
|
|
<div
|
|
className="p-3 rounded-md text-sm"
|
|
style={{ backgroundColor: "#ef4444", color: "white" }}
|
|
>
|
|
{error}
|
|
</div>
|
|
)}
|
|
{serverError && (
|
|
<div
|
|
className="p-3 rounded-md text-sm"
|
|
style={{ backgroundColor: "#ef4444", color: "white" }}
|
|
>
|
|
{serverError}
|
|
</div>
|
|
)}
|
|
|
|
{/* Username & Email */}
|
|
<div className="w-full flex flex-col md:flex-row gap-4">
|
|
<div className="w-full md:w-1/2">
|
|
<fieldset className="flex flex-col w-full gap-1">
|
|
<label
|
|
htmlFor="username"
|
|
className="block font-semibold text-sm"
|
|
style={{
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
}}
|
|
>
|
|
{t("username")}
|
|
</label>
|
|
<input
|
|
id="username"
|
|
name="username"
|
|
type="text"
|
|
placeholder={t("usernamePlaceholder")}
|
|
autoComplete="username"
|
|
className="focus:ring-0 border-4 rounded w-full text-sm px-3 py-2 mt-1"
|
|
style={{
|
|
backgroundColor: "var(--color-background)",
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
|
|
}}
|
|
required
|
|
/>
|
|
</fieldset>
|
|
</div>
|
|
<div className="w-full md:w-1/2">
|
|
<fieldset className="flex flex-col w-full gap-1">
|
|
<label
|
|
htmlFor="mail"
|
|
className="block font-semibold text-sm"
|
|
style={{
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
}}
|
|
>
|
|
{t("email")}
|
|
</label>
|
|
<input
|
|
id="mail"
|
|
name="mail"
|
|
type="email"
|
|
placeholder={t("emailPlaceholder")}
|
|
autoComplete="email"
|
|
className="focus:ring-0 border-4 rounded w-full text-sm px-3 py-2 mt-1"
|
|
style={{
|
|
backgroundColor: "var(--color-background)",
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
|
|
}}
|
|
/>
|
|
</fieldset>
|
|
</div>
|
|
<p
|
|
className="text-xs mt-1"
|
|
style={{
|
|
color:
|
|
"var(--color-text-muted-readable, var(--color-text-muted))",
|
|
}}
|
|
>
|
|
Geen e-mail? Je kunt later via Discord verifiëren.
|
|
</p>
|
|
</div>
|
|
|
|
{/* Password & Confirm */}
|
|
<div className="w-full flex flex-col md:flex-row gap-4">
|
|
<div className="w-full md:w-1/2">
|
|
<fieldset className="flex flex-col w-full gap-1">
|
|
<label
|
|
htmlFor="password"
|
|
className="block font-semibold text-sm"
|
|
style={{
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
}}
|
|
>
|
|
{t("password")}
|
|
</label>
|
|
<input
|
|
id="password"
|
|
name="password"
|
|
type="password"
|
|
placeholder={t("passwordPlaceholder")}
|
|
autoComplete="new-password"
|
|
className="focus:ring-0 border-4 rounded w-full text-sm px-3 py-2 mt-1"
|
|
style={{
|
|
backgroundColor: "var(--color-background)",
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
|
|
}}
|
|
required
|
|
/>
|
|
</fieldset>
|
|
</div>
|
|
<div className="w-full md:w-1/2">
|
|
<fieldset className="flex flex-col w-full gap-1">
|
|
<label
|
|
htmlFor="password_confirmation"
|
|
className="block font-semibold text-sm"
|
|
style={{
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
}}
|
|
>
|
|
{t("confirmPassword")}
|
|
</label>
|
|
<input
|
|
id="password_confirmation"
|
|
name="password_confirmation"
|
|
type="password"
|
|
placeholder={t("confirmPasswordPlaceholder")}
|
|
autoComplete="new-password"
|
|
className="focus:ring-0 border-4 rounded w-full text-sm px-3 py-2 mt-1"
|
|
style={{
|
|
backgroundColor: "var(--color-background)",
|
|
color: "var(--color-text-readable, var(--color-text))",
|
|
borderColor:
|
|
"color-mix(in srgb, var(--color-text-muted) 25%, transparent)",
|
|
}}
|
|
required
|
|
/>
|
|
</fieldset>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Terms */}
|
|
<div
|
|
className="rounded-md p-3 flex flex-col gap-3"
|
|
style={{
|
|
backgroundColor:
|
|
"color-mix(in srgb, var(--color-background) 50%, var(--color-surface))",
|
|
}}
|
|
>
|
|
<div className="flex items-center gap-3 text-sm">
|
|
<button
|
|
type="button"
|
|
role="checkbox"
|
|
aria-checked={termsAccepted}
|
|
onClick={() => setTermsAccepted(!termsAccepted)}
|
|
className="w-5 h-5 rounded border-2 flex items-center justify-center shrink-0 transition-colors"
|
|
style={{
|
|
borderColor: termsAccepted
|
|
? "#27a44d"
|
|
: "color-mix(in srgb, var(--color-text-muted) 30%, transparent)",
|
|
backgroundColor: termsAccepted ? "#27a44d" : "transparent",
|
|
}}
|
|
>
|
|
{termsAccepted && (
|
|
<svg
|
|
className="w-3 h-3 text-white"
|
|
fill="none"
|
|
stroke="currentColor"
|
|
viewBox="0 0 24 24"
|
|
>
|
|
<path
|
|
strokeLinecap="round"
|
|
strokeLinejoin="round"
|
|
strokeWidth={3}
|
|
d="M5 13l4 4L19 7"
|
|
/>
|
|
</svg>
|
|
)}
|
|
</button>
|
|
<input
|
|
type="hidden"
|
|
name="terms"
|
|
value={termsAccepted ? "1" : ""}
|
|
/>
|
|
<span
|
|
className="font-semibold cursor-pointer"
|
|
style={{ color: "var(--color-text-readable, var(--color-text))" }}
|
|
onClick={() => setTermsAccepted(!termsAccepted)}
|
|
>
|
|
{t("termsAccept", { hotel: hotelName })}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Captcha */}
|
|
{showCaptcha && captcha.provider === "turnstile" && (
|
|
<div className="cf-turnstile" data-sitekey={captcha.siteKey} />
|
|
)}
|
|
{showCaptcha && captcha.provider === "recaptcha" && (
|
|
<div className="g-recaptcha" data-sitekey={captcha.siteKey} />
|
|
)}
|
|
|
|
{/* Submit */}
|
|
<button
|
|
type="submit"
|
|
disabled={isPending}
|
|
className="w-full rounded p-2 text-white font-semibold transition-all duration-200 text-base"
|
|
style={{
|
|
backgroundColor: "#27a44d",
|
|
border: "2px solid #34c661",
|
|
}}
|
|
>
|
|
{isPending ? t("creatingAccount") : t("createAccount")}
|
|
</button>
|
|
|
|
<div className="text-center">
|
|
<Link
|
|
href="/login"
|
|
className="text-sm font-semibold hover:underline"
|
|
style={{
|
|
color: "var(--color-primary-readable, var(--color-primary))",
|
|
}}
|
|
>
|
|
{t("alreadyHaveAccount")}
|
|
</Link>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
);
|
|
}
|