import { redirect, Form } from "react-router"; import { auth } from "~/lib/auth.server"; import { findUserById, findUserByUsername, updateUser, USERNAME_RE } from "~/models/user"; import type { Route } from "./+types/onboarding"; import { Button } from "~/components/ui/button"; import { Input } from "~/components/ui/input"; import { Label } from "~/components/ui/label"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "~/components/ui/card"; export function meta(): Route.MetaDescriptors { return [{ title: "Choose a Username - Brackt" }]; } export function suggestUsername(displayName: string | null): string { if (!displayName) return ""; const cleaned = displayName .toLowerCase() .replace(/[^a-z0-9_-]/g, "") .slice(0, 20); return cleaned.length >= 3 ? cleaned : ""; } export function safeRedirectTo(value: string | null): string { if (!value) return "/"; return value.startsWith("/") && !value.startsWith("//") ? value : "/"; } export async function loader({ request }: Route.LoaderArgs) { const session = await auth.api.getSession({ headers: request.headers }); if (!session) return redirect("/login"); const user = await findUserById(session.user.id); if (!user) return redirect("/login"); if (user.username) return redirect("/"); const redirectTo = safeRedirectTo(new URL(request.url).searchParams.get("redirectTo")); return { suggestion: suggestUsername(user.displayName), redirectTo }; } export async function action({ request }: Route.ActionArgs) { const session = await auth.api.getSession({ headers: request.headers }); if (!session) return redirect("/login"); const formData = await request.formData(); const username = (formData.get("username") as string | null)?.trim() ?? ""; const redirectTo = safeRedirectTo(formData.get("redirectTo") as string | null); if (!USERNAME_RE.test(username)) { return { error: "Username must be 3–30 characters and contain only letters, numbers, underscores, or hyphens.", redirectTo, }; } const existing = await findUserByUsername(username); if (existing && existing.id !== session.user.id) { return { error: "That username is already taken.", redirectTo }; } try { await updateUser(session.user.id, { username }); } catch { return { error: "That username is already taken.", redirectTo }; } return redirect(redirectTo); } export default function OnboardingPage({ loaderData, actionData }: Route.ComponentProps) { const redirectTo = actionData && "redirectTo" in actionData ? actionData.redirectTo : loaderData.redirectTo; return (