Move website directory

This commit is contained in:
Aaron Po
2026-04-27 16:00:11 -04:00
parent 189bce040b
commit 5a21589029
58 changed files with 0 additions and 0 deletions

View File

@@ -0,0 +1,128 @@
import { zodResolver } from '@hookform/resolvers/zod';
import { HomeSimpleDoor, LogIn, UserPlus } from 'iconoir-react';
import { useEffect } from 'react';
import { useForm } from 'react-hook-form';
import { Link, redirect, useNavigation, useSubmit } from 'react-router';
import FormField from '../components/forms/FormField';
import SubmitButton from '../components/forms/SubmitButton';
import { showErrorToast } from '../components/toast/toast';
import { createAuthSession, getOptionalAuth, login } from '../lib/auth.server';
import { loginSchema, type LoginSchema } from '../lib/schemas';
import type { Route } from './+types/login';
export function meta({}: Route.MetaArgs) {
return [{ title: 'Login | The Biergarten App' }];
}
export async function loader({ request }: Route.LoaderArgs) {
const auth = await getOptionalAuth(request);
if (auth) throw redirect('/dashboard');
return null;
}
export async function action({ request }: Route.ActionArgs) {
const formData = await request.formData();
const result = loginSchema.safeParse({
username: formData.get('username'),
password: formData.get('password'),
});
if (!result.success) {
return { error: result.error.issues[0].message };
}
try {
const payload = await login(result.data.username, result.data.password);
return createAuthSession(payload, '/dashboard');
} catch (err) {
return { error: err instanceof Error ? err.message : 'Login failed.' };
}
}
export default function Login({ actionData }: Route.ComponentProps) {
const navigation = useNavigation();
const submit = useSubmit();
const isSubmitting = navigation.state === 'submitting';
const {
register,
handleSubmit,
formState: { errors },
} = useForm<LoginSchema>({ resolver: zodResolver(loginSchema) });
const onSubmit = handleSubmit((data) => {
submit(data, { method: 'post' });
});
useEffect(() => {
if (actionData?.error) {
showErrorToast(actionData.error);
}
}, [actionData?.error]);
return (
<div className="hero min-h-screen bg-base-200">
<div className="card w-full max-w-md bg-base-100 shadow-xl">
<div className="card-body gap-4">
<div className="text-center">
<h1 className="card-title text-3xl justify-center gap-2">
<LogIn className="size-7" aria-hidden="true" />
Login
</h1>
<p className="text-base-content/70">Sign in to your Biergarten account</p>
</div>
{actionData?.error && (
<div role="alert" className="alert alert-error alert-soft">
<span>{actionData.error}</span>
</div>
)}
<form onSubmit={onSubmit} className="space-y-3">
<FormField
id="username"
type="text"
autoComplete="username"
placeholder="your_username"
label="Username"
error={errors.username?.message}
{...register('username')}
/>
<FormField
id="password"
type="password"
autoComplete="current-password"
placeholder="••••••••"
label="Password"
error={errors.password?.message}
{...register('password')}
/>
<SubmitButton
isSubmitting={isSubmitting}
idleText="Sign In"
submittingText="Signing in..."
/>
</form>
<div className="divider text-xs">New here?</div>
<div className="text-center space-y-2">
<Link to="/register" className="btn btn-outline btn-sm w-full gap-2">
<UserPlus className="size-4" aria-hidden="true" />
Create an account
</Link>
<Link
to="/"
className="link link-hover text-sm text-base-content/60 inline-flex items-center gap-1"
>
<HomeSimpleDoor className="size-4" aria-hidden="true" />
Back to home
</Link>
</div>
</div>
</div>
</div>
);
}