remixed v0
This commit is contained in:
49
app/features/auth/pages/forgot-password.tsx
Normal file
49
app/features/auth/pages/forgot-password.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
import { Form, useActionData, useTransition } from "@remix-run/react";
|
||||
|
||||
import type { ForgotPasswordActionData } from "../actions/forgot-password";
|
||||
import LabeledTextField from "~/features/core/components/labeled-text-field";
|
||||
import Button from "~/features/core/components/button";
|
||||
|
||||
export default function ForgotPasswordPage() {
|
||||
const actionData = useActionData<ForgotPasswordActionData>();
|
||||
const transition = useTransition();
|
||||
const isSubmitting = transition.state === "submitting";
|
||||
|
||||
return (
|
||||
<section>
|
||||
<header>
|
||||
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
|
||||
Forgot your password?
|
||||
</h2>
|
||||
</header>
|
||||
|
||||
<Form method="post" className="mt-8 mx-auto w-full max-w-sm">
|
||||
{actionData?.submitted ? (
|
||||
<p className="text-center">
|
||||
If your email is in our system, you will receive instructions to reset your password shortly.
|
||||
</p>
|
||||
) : (
|
||||
<>
|
||||
<LabeledTextField
|
||||
name="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.email}
|
||||
tabIndex={1}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={transition.state === "submitting"}
|
||||
tabIndex={2}
|
||||
className="w-full flex justify-center py-2 px-4 text-base font-medium"
|
||||
>
|
||||
Send reset password link
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</Form>
|
||||
</section>
|
||||
);
|
||||
}
|
83
app/features/auth/pages/register.tsx
Normal file
83
app/features/auth/pages/register.tsx
Normal file
@ -0,0 +1,83 @@
|
||||
import { Form, Link, useActionData, useLoaderData, useTransition } from "@remix-run/react";
|
||||
|
||||
import type { RegisterActionData } from "../actions/register";
|
||||
import type { RegisterLoaderData } from "../loaders/register";
|
||||
import LabeledTextField from "~/features/core/components/labeled-text-field";
|
||||
import Alert from "~/features/core/components/alert";
|
||||
import Button from "~/features/core/components/button";
|
||||
|
||||
export default function RegisterPage() {
|
||||
const loaderData = useLoaderData<RegisterLoaderData>();
|
||||
const actionData = useActionData<RegisterActionData>();
|
||||
const transition = useTransition();
|
||||
const isSubmitting = transition.state === "submitting";
|
||||
const topErrorMessage = loaderData?.errors?.general || actionData?.errors?.general;
|
||||
|
||||
return (
|
||||
<section>
|
||||
<header>
|
||||
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">
|
||||
Create your account
|
||||
</h2>
|
||||
<p className="mt-2 text-center text-sm leading-5 text-gray-600">
|
||||
<Link
|
||||
to="/sign-in"
|
||||
prefetch="intent"
|
||||
className="font-medium text-primary-600 hover:text-primary-500 focus:underline transition ease-in-out duration-150"
|
||||
>
|
||||
Already have an account?
|
||||
</Link>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<Form method="post" className="mt-8 mx-auto w-full max-w-sm">
|
||||
{topErrorMessage ? (
|
||||
<div role="alert" className="mb-8 sm:mx-auto sm:w-full sm:max-w-sm whitespace-pre">
|
||||
<Alert title="Oops, there was an issue" message={topErrorMessage!} variant="error" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<LabeledTextField
|
||||
name="orgName"
|
||||
type="text"
|
||||
label="Organization name"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.orgName}
|
||||
tabIndex={1}
|
||||
/>
|
||||
<LabeledTextField
|
||||
name="fullName"
|
||||
type="text"
|
||||
label="Full name"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.fullName}
|
||||
tabIndex={2}
|
||||
/>
|
||||
<LabeledTextField
|
||||
name="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.email}
|
||||
tabIndex={3}
|
||||
/>
|
||||
<LabeledTextField
|
||||
name="password"
|
||||
type="password"
|
||||
label="Password"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.password}
|
||||
tabIndex={4}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={transition.state === "submitting"}
|
||||
tabIndex={5}
|
||||
>
|
||||
Register
|
||||
</Button>
|
||||
</Form>
|
||||
</section>
|
||||
);
|
||||
}
|
55
app/features/auth/pages/reset-password.tsx
Normal file
55
app/features/auth/pages/reset-password.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { Form, useActionData, useSearchParams, useTransition } from "@remix-run/react";
|
||||
import clsx from "clsx";
|
||||
|
||||
import type { ResetPasswordActionData } from "../actions/reset-password";
|
||||
import LabeledTextField from "~/features/core/components/labeled-text-field";
|
||||
|
||||
export default function ForgotPasswordPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const actionData = useActionData<ResetPasswordActionData>();
|
||||
const transition = useTransition();
|
||||
const isSubmitting = transition.state === "submitting";
|
||||
|
||||
return (
|
||||
<section>
|
||||
<header>
|
||||
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">Set a new password</h2>
|
||||
</header>
|
||||
|
||||
<Form method="post" action={`./?${searchParams}`} className="mt-8 mx-auto w-full max-w-sm">
|
||||
<LabeledTextField
|
||||
name="password"
|
||||
label="New Password"
|
||||
type="password"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.password}
|
||||
tabIndex={1}
|
||||
/>
|
||||
|
||||
<LabeledTextField
|
||||
name="passwordConfirmation"
|
||||
label="Confirm New Password"
|
||||
type="password"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.passwordConfirmation}
|
||||
tabIndex={2}
|
||||
/>
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
disabled={transition.state === "submitting"}
|
||||
className={clsx(
|
||||
"w-full flex justify-center py-2 px-4 border border-transparent text-base font-medium rounded-md text-white focus:outline-none focus:border-primary-700 focus:shadow-outline-primary transition duration-150 ease-in-out",
|
||||
{
|
||||
"bg-primary-400 cursor-not-allowed": isSubmitting,
|
||||
"bg-primary-600 hover:bg-primary-700": !isSubmitting,
|
||||
},
|
||||
)}
|
||||
tabIndex={3}
|
||||
>
|
||||
Reset password
|
||||
</button>
|
||||
</Form>
|
||||
</section>
|
||||
);
|
||||
}
|
75
app/features/auth/pages/sign-in.tsx
Normal file
75
app/features/auth/pages/sign-in.tsx
Normal file
@ -0,0 +1,75 @@
|
||||
import { Form, Link, useActionData, useLoaderData, useSearchParams, useTransition } from "@remix-run/react";
|
||||
|
||||
import type { SignInActionData } from "../actions/sign-in";
|
||||
import type { SignInLoaderData } from "../loaders/sign-in";
|
||||
import LabeledTextField from "~/features/core/components/labeled-text-field";
|
||||
import Alert from "~/features/core/components/alert";
|
||||
import Button from "~/features/core/components/button";
|
||||
|
||||
export default function SignInPage() {
|
||||
const [searchParams] = useSearchParams();
|
||||
const loaderData = useLoaderData<SignInLoaderData>();
|
||||
const actionData = useActionData<SignInActionData>();
|
||||
const transition = useTransition();
|
||||
const isSubmitting = transition.state === "submitting";
|
||||
return (
|
||||
<section>
|
||||
<header>
|
||||
<h2 className="mt-6 text-center text-3xl leading-9 font-extrabold text-gray-900">Welcome back!</h2>
|
||||
<p className="mt-2 text-center text-sm leading-5 text-gray-600">
|
||||
Need an account?
|
||||
<Link
|
||||
to="/register"
|
||||
prefetch="intent"
|
||||
className="font-medium text-primary-600 hover:text-primary-500 focus:underline transition ease-in-out duration-150"
|
||||
>
|
||||
Create yours for free
|
||||
</Link>
|
||||
</p>
|
||||
</header>
|
||||
|
||||
<Form method="post" action={`./?${searchParams}`} className="mt-8 mx-auto w-full max-w-sm">
|
||||
{loaderData?.errors ? (
|
||||
<div role="alert" className="mb-8 sm:mx-auto sm:w-full sm:max-w-sm whitespace-pre">
|
||||
<Alert title="Oops, there was an issue" message={loaderData.errors.general} variant="error" />
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<LabeledTextField
|
||||
name="email"
|
||||
type="email"
|
||||
label="Email"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.email}
|
||||
tabIndex={1}
|
||||
/>
|
||||
|
||||
<LabeledTextField
|
||||
name="password"
|
||||
type="password"
|
||||
label="Password"
|
||||
disabled={isSubmitting}
|
||||
error={actionData?.errors?.password}
|
||||
tabIndex={2}
|
||||
sideLabel={
|
||||
<Link
|
||||
to="/forgot-password"
|
||||
prefetch="intent"
|
||||
className="font-medium text-primary-600 hover:text-primary-500 transition ease-in-out duration-150"
|
||||
>
|
||||
Forgot your password?
|
||||
</Link>
|
||||
}
|
||||
/>
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
disabled={transition.state === "submitting"}
|
||||
tabIndex={3}
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</Form>
|
||||
</section>
|
||||
);
|
||||
}
|
Reference in New Issue
Block a user