import { Form, useActionData, useNavigate, useNavigation } from '@remix-run/react'; import { json, redirect } from '@remix-run/server-runtime'; import { getPasswordStrength, getSignedInUser, sessionStorage } from '../auth.server'; import { TextInput } from '../components/TextInput'; import { createUserWithEmailAndPassword, sendVerificationEmail } from '../data/zippo.server'; import { validateFormData } from '../utils/utils'; import { z } from 'zod'; import { Button } from '../components/Button'; import { IconButton } from '../components/IconButton'; import React from 'react'; import { Eye } from '../icons/Eye'; import { EyeOff } from '../icons/EyeOff'; import { Alert } from '../components/Alert'; import type { ActionArgs, LoaderArgs, MetaFunction } from '@remix-run/node'; export const meta: MetaFunction = () => { return { title: 'Create Password | 0x', description: 'Create your password', }; }; const zodPasswordModel = z.object({ password: z.string().min(1, 'Please enter a password'), }); type ActionInput = z.TypeOf; type Errors = { general?: string } & Partial; export async function loader({ request }: LoaderArgs) { const [user, headers] = await getSignedInUser(request); if (user) { throw redirect('/apps', { headers }); } const multiformSession = await sessionStorage.getSession(request.headers.get('Cookie')); const createAccountSession = multiformSession.get('createAccount'); if (!createAccountSession) { throw redirect('/'); } return null; } export async function action({ request }: ActionArgs) { const [user, headers] = await getSignedInUser(request); if (user) { throw redirect('/apps', { headers }); } const multiformSession = await sessionStorage.getSession(request.headers.get('Cookie')); const createAccountSession = multiformSession.get('createAccount'); if (!createAccountSession) { throw redirect('/'); } const { body, errors } = validateFormData({ formData: await request.formData(), schema: zodPasswordModel, }); if (errors) { return json({ errors: errors as Errors, values: body }); } const [pwStrength, feedback] = getPasswordStrength(body.password); if (pwStrength < 4) { return json({ errors: { password: feedback.warning ? feedback.warning : feedback.suggestions?.[0] ?? 'Please enter a stronger password', // feedback.suggestions might be empty } as Errors, values: body, }); } // Password is good, we submit the user to the backend and show the email verification page const userCreated = await createUserWithEmailAndPassword({ firstName: createAccountSession.firstName, lastName: createAccountSession.lastName, email: createAccountSession.email, password: body.password, }); if (userCreated.result === 'ERROR') { return json({ errors: { password: 'There was an error creating your account. Please try again later', } as Errors, values: body, }); } // we set the email in the session so we can show it on the verify email page // as this is a signed and encrypted cookie, it acts as proof that the user // moved through the form correctly multiformSession.set('verifyEmail', { email: createAccountSession.email, }); const result = await sendVerificationEmail({ email: createAccountSession.email, userId: userCreated.data }); if (result.result === 'ERROR') { return json({ errors: { general: 'There was an error sending the verification email. Please try again later', } as Errors, values: body, }); } // we unset the createAccount session so we can't use it again multiformSession.unset('createAccount'); throw redirect('/create-account/verification-sent', { headers: { 'Set-Cookie': await sessionStorage.commitSession(multiformSession), }, }); } export default function SetPasswordPage() { const actionData = useActionData(); const navigator = useNavigate(); const navigation = useNavigation(); const [showPassword, setShowPassword] = React.useState(false); return (
{actionData?.errors?.general && ( {actionData?.errors?.general} )}

Create your password

setShowPassword(!showPassword)} color="white" className="h-11 w-11 shadow-md" size="xs" roundness="sm" type="button" aria-label={showPassword ? 'Hide password' : 'Show password'} > {showPassword ? : } } />
); }