import { redirect, type ActionFunctionArgs, type LoaderFunctionArgs, type MetaFunction, } from "@remix-run/node"; import { Form, useNavigation } from "@remix-run/react"; import { Inbox, Loader, Mail } from "lucide-react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { LoginPageLayout } from "~/components/layout/LoginPageLayout"; import { Button } from "~/components/ui"; import { Fieldset } from "~/components/ui/Fieldset"; import { FormButtons } from "~/components/ui/FormButtons"; import { Header1 } from "~/components/ui/Headers"; import { Input } from "~/components/ui/input"; import { Paragraph } from "~/components/ui/Paragraph"; import { TextLink } from "~/components/ui/TextLink"; import { authenticator } from "~/services/auth.server"; import { getUserId } from "~/services/session.server"; import { commitSession, getUserSession, } from "~/services/sessionStorage.server"; import { env } from "~/env.server"; export const meta: MetaFunction = ({ matches }) => { const parentMeta = matches .flatMap((match) => match.meta ?? []) .filter((meta) => { if ("title" in meta) return false; if ("name" in meta && meta.name === "viewport") return false; return true; }); return [ ...parentMeta, { title: `Login to C.O.R.E.` }, { name: "viewport", content: "width=device-width,initial-scale=1", }, ]; }; export async function loader({ request }: LoaderFunctionArgs): Promise { if (env.NODE_ENV !== "development") { return typedjson({ isDevelopment: false }); } const userId = await getUserId(request); if (userId) return redirect("/"); const session = await getUserSession(request); const error = session.get("auth:error"); let magicLinkError: string | undefined | unknown; if (error) { if ("message" in error) { magicLinkError = error.message; } else { magicLinkError = JSON.stringify(error, null, 2); } } return typedjson( { isDevelopment: true, magicLinkSent: session.has("core:magiclink"), magicLinkError, }, { headers: { "Set-Cookie": await commitSession(session) }, }, ); } export async function action({ request }: ActionFunctionArgs) { if (env.NODE_ENV !== "development") { throw new Error("Magic link login is only available in development mode"); } const clonedRequest = request.clone(); const payload = Object.fromEntries(await clonedRequest.formData()); const { action } = z .object({ action: z.enum(["send", "reset"]), }) .parse(payload); if (action === "send") { return await authenticator.authenticate("email-link", request); } else { const session = await getUserSession(request); session.unset("core:magiclink"); return redirect("/magic", { headers: { "Set-Cookie": await commitSession(session), }, }); } } export default function LoginMagicLinkPage() { const data = useTypedLoaderData(); const navigate = useNavigation(); if (!data.isDevelopment) { return ( Magic link login is only available in development mode. ); } const isLoading = (navigate.state === "loading" || navigate.state === "submitting") && navigate.formAction !== undefined && navigate.formData?.get("action") === "send"; return (
{data.magicLinkSent ? ( <> We've sent you a magic link!
We sent you an email which contains a magic link that will log you in to your account. Re-enter email } confirmButton={ } />
) : ( <> Welcome Create an account or login using email
{data.magicLinkError && <>{data.magicLinkError}}
)}
); }