diff --git a/app/routes/login.tsx b/app/routes/login.tsx index 4cc38b6..6cf1ad7 100644 --- a/app/routes/login.tsx +++ b/app/routes/login.tsx @@ -1,41 +1,62 @@ -import type { ActionArgs, LoaderArgs, MetaFunction } from '@remix-run/node' -import { json, redirect } from '@remix-run/node' -import { Form, Link, useActionData, useSearchParams } from '@remix-run/react' -import * as React from 'react' +import type { ActionArgs, LoaderArgs, MetaFunction } from '@remix-run/node'; +import { json, redirect } from '@remix-run/node'; +import { Form, useActionData, useSearchParams } from '@remix-run/react'; +import * as React from 'react'; +import { + TextInput, + Box, + Checkbox, + Group, + Button, + PasswordInput +} from '@mantine/core'; +import { AtSign, Lock } from 'react-feather'; -import { verifyLogin } from '~/models/user.server' -import { createUserSession, getUserId } from '~/session.server' -import { safeRedirect, validateEmail } from '~/utils' +import { verifyLogin } from '~/models/user.server'; +import { createUserSession, getUserId } from '~/session.server'; +import { safeRedirect, validateEmail } from '~/utils'; export async function loader({ request }: LoaderArgs) { - const userId = await getUserId(request) - if (userId) return redirect('/') - return json({}) + const userId = await getUserId(request); + if (userId) return redirect('/time-entries'); + return json({}); } export async function action({ request }: ActionArgs) { - const formData = await request.formData() - const email = formData.get('email') - const password = formData.get('password') - const redirectTo = safeRedirect(formData.get('redirectTo'), '/') - const remember = formData.get('remember') + const formData = await request.formData(); + const email = formData.get('email'); + const password = formData.get('password'); + const redirectTo = safeRedirect(formData.get('redirectTo'), '/'); + const remember = formData.get('remember'); if (!validateEmail(email)) { - return json({ errors: { email: 'Email is invalid', password: null } }, { status: 400 }) + return json( + { errors: { email: 'Email is invalid', password: null } }, + { status: 400 } + ); } if (typeof password !== 'string' || password.length === 0) { - return json({ errors: { password: 'Password is required', email: null } }, { status: 400 }) + return json( + { errors: { password: 'Password is required', email: null } }, + { status: 400 } + ); } if (password.length < 8) { - return json({ errors: { password: 'Password is too short', email: null } }, { status: 400 }) + return json( + { errors: { password: 'Password is too short', email: null } }, + { status: 400 } + ); } - const user = await verifyLogin(email, password) + const user = await verifyLogin(email, password); if (!user) { - return json({ errors: { email: 'Invalid email or password', password: null } }, { status: 400 }) + return json( + { errors: { email: 'Invalid email or password', password: null } }, + { status: 400 } + ); } return createUserSession({ @@ -43,104 +64,75 @@ export async function action({ request }: ActionArgs) { userId: user.id, remember: remember === 'on' ? true : false, redirectTo - }) + }); } export const meta: MetaFunction = () => { return { - title: 'Login' - } -} + title: 'Login | WorkTimer', + description: + 'WorkTimer is a time tracking app. Helps you track your time spent on projects.' + }; +}; export default function LoginPage() { - const [searchParams] = useSearchParams() - const redirectTo = searchParams.get('redirectTo') || '/notes' - const actionData = useActionData() - const emailRef = React.useRef(null) - const passwordRef = React.useRef(null) + const [searchParams] = useSearchParams(); + const redirectTo = searchParams.get('redirectTo') || '/time-entries'; + const actionData = useActionData(); + const emailRef = React.useRef(null); + const passwordRef = React.useRef(null); React.useEffect(() => { if (actionData?.errors?.email) { - emailRef.current?.focus() + emailRef.current?.focus(); } else if (actionData?.errors?.password) { - passwordRef.current?.focus() + passwordRef.current?.focus(); } - }, [actionData]) + }, [actionData]); return ( -
-
-
-
- -
- - {actionData?.errors?.email && ( -
- {actionData.errors.email} -
- )} -
-
+ + + } + id="email" + ref={emailRef} + required + autoFocus={true} + name="email" + type="email" + autoComplete="email" + aria-invalid={actionData?.errors?.email ? true : undefined} + error={actionData?.errors?.email} + errorProps={{ children: actionData?.errors?.email }} + /> -
- -
- - {actionData?.errors?.password && ( -
- {actionData.errors.password} -
- )} -
-
+ } + name="password" + type="password" + autoComplete="current-password" + aria-invalid={actionData?.errors?.password ? true : undefined} + error={actionData?.errors?.password ? true : undefined} + errorProps={{ children: actionData?.errors?.password }} + /> - - -
-
- - -
-
- -
-
- ) + + + + + + + + ); }