Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/[locale]/(main)/order/[id]/confirmed/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ type Props = {
}
export const metadata: Metadata = {
title: "Order Confirmed",
description: "You purchase was successful",
description: "Your purchase was successful",
}

export default async function OrderConfirmedPage(props: Props) {
Expand Down
16 changes: 16 additions & 0 deletions src/app/[locale]/(main)/user/forgot-password/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { ForgotPasswordForm } from "@/components/molecules/ForgotPasswordForm/ForgotPasswordForm"
import { Metadata } from "next"

export const metadata: Metadata = {
title: "Forgot password",
description: "Create a new password",
}

export default function ForgotPasswordPage() {

return (
<main className="container">
<ForgotPasswordForm />
</main>
)
}
96 changes: 96 additions & 0 deletions src/components/molecules/ForgotPasswordForm/ForgotPasswordForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"use client"
import {
FieldError,
FormProvider,
useForm,
useFormContext,
} from "react-hook-form"
import { Button } from "@/components/atoms"
import { zodResolver } from "@hookform/resolvers/zod"
import { LabeledInput } from "@/components/cells"
import { forgotPasswordSchema, ForgotPasswordFormData } from "./schema"
import { useState } from "react"
import { sendResetPasswordEmail } from "@/lib/data/customer"
import { toast } from "@/lib/helpers/toast"
import Link from "next/link"
import { FetchError } from "@medusajs/js-sdk"

export const ForgotPasswordForm = () => {
const methods = useForm<ForgotPasswordFormData>({
resolver: zodResolver(forgotPasswordSchema),
defaultValues: {
email: "",
},
})

return (
<FormProvider {...methods}>
<Form />
</FormProvider>
)
}

const Form = () => {
const [error, setError] = useState("")
const {
handleSubmit,
register,
formState: { errors, isSubmitting },
reset,
} = useFormContext<ForgotPasswordFormData>()

const submit = async (data: ForgotPasswordFormData) => {
if (!data.email) return

const result = await sendResetPasswordEmail(data.email)

if (!result.success) {
setError(result.error || "An error occurred. Please try again.")
return
}

setError("")
reset({ email: "" })

toast.success({
title: `A password reset has been requested for ${data.email}. Check your inbox and spam folder. Remember, the link is only active for one hour.`,
})
}

return (
<div className="max-w-xl w-full mx-auto mt-6 space-y-4 rounded-sm border p-4">
<h1 className="heading-md uppercase my-0 mb-2">Forgot your password?</h1>
<p className="text-md">
Enter the email you used to sign up and we’ll send you a password reset
email.
</p>
<form onSubmit={handleSubmit(submit)}>
<div className="space-y-4">
<LabeledInput
label="E-mail"
placeholder="Your e-mail address"
error={errors.email as FieldError}
{...register("email")}
/>

{error && <p className="label-md text-negative">{error}</p>}
</div>

<div className="space-y-4 mt-8">
<Button className="w-full uppercase" disabled={isSubmitting}>
Reset Password
</Button>

<Link href="/user" className="flex">
<Button
variant="tonal"
className="w-full flex justify-center uppercase"
>
Back to log in
</Button>
</Link>
</div>
</form>
</div>
)
}
7 changes: 7 additions & 0 deletions src/components/molecules/ForgotPasswordForm/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { z } from "zod"

export const forgotPasswordSchema = z.object({
email: z.string().nonempty("Please enter email").email("Please enter a valid email"),
})

export type ForgotPasswordFormData = z.infer<typeof forgotPasswordSchema>
77 changes: 48 additions & 29 deletions src/components/molecules/LoginForm/LoginForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { loginFormSchema, LoginFormData } from "./schema"
import { useState } from "react"
import { login } from "@/lib/data/customer"
import { useRouter } from "next/navigation"
import Link from "next/link"

export const LoginForm = () => {
const methods = useForm<LoginFormData>({
Expand Down Expand Up @@ -56,36 +57,54 @@ const Form = () => {

return (
<main className="container">
<h1 className="heading-xl text-center uppercase my-6">
Log in to your account
</h1>
<form onSubmit={handleSubmit(submit)}>
<div className="w-96 max-w-full mx-auto space-y-4">
<LabeledInput
label="E-mail"
placeholder="Your e-mail address"
error={errors.email as FieldError}
{...register("email")}
/>
<LabeledInput
label="Password"
placeholder="Your password"
type="password"
error={errors.password as FieldError}
{...register("password")}
/>
{error && <p className="label-md text-negative">{error}</p>}
<Button className="w-full" disabled={isSubmitting}>
Log in
</Button>
<p className="text-center label-md">
Don&apos;t have an account yet?{" "}
<LocalizedClientLink href="/user/register" className="underline">
Sign up!
</LocalizedClientLink>
</p>
<div className="max-w-xl w-full mx-auto mt-6 space-y-4">
<div className="rounded-sm border p-4">
<h1 className="heading-md uppercase mb-8 text-primary">Log in</h1>
<form onSubmit={handleSubmit(submit)}>
<div className="space-y-4">
<LabeledInput
label="E-mail"
placeholder="Your e-mail address"
error={errors.email as FieldError}
{...register("email")}
/>
<LabeledInput
label="Password"
placeholder="Your password"
type="password"
error={errors.password as FieldError}
{...register("password")}
/>
</div>

<Link href="/user/forgot-password" className="block text-right label-md uppercase text-action-on-secondary mt-4">
Forgot your password?
</Link>

<Button className="w-full uppercase mt-8" disabled={isSubmitting}>
Log in
</Button>

{error && (
<p className="label-md text-negative my-4 text-center">{error}</p>
)}
</form>
</div>

<div className="rounded-sm border p-4">
<h2 className="heading-md uppercase mb-4 text-primary">
Don&apos;t have an account yet?
</h2>
<Link href="/user/register">
<Button
variant="tonal"
className="w-full flex justify-center mt-8 uppercase"
>
Create account
</Button>
</Link>
</div>
</form>
</div>
</main>
)
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
"use client"

import { Button, Card } from "@/components/atoms"
import { Button } from "@/components/atoms"
import { LabeledInput } from "@/components/cells"
import { zodResolver } from "@hookform/resolvers/zod"
import { CheckCircle } from "@medusajs/icons"
import {
FieldError,
FieldValues,
Expand All @@ -13,7 +12,7 @@ import {
UseFormReturn,
} from "react-hook-form"
import { ProfilePasswordFormData, profilePasswordSchema } from "./schema"
import { useEffect, useState } from "react"
import { useState } from "react"
import { updateCustomerPassword } from "@/lib/data/customer"
import { Heading, toast } from "@medusajs/ui"
import LocalizedClientLink from "../LocalizedLink/LocalizedLink"
Expand Down
22 changes: 10 additions & 12 deletions src/components/molecules/RegisterForm/RegisterForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -129,19 +129,17 @@ const Form = () => {
</form>
</Container>
<Container className="border max-w-xl mx-auto mt-8 p-4">
<h1 className="heading-md text-primary uppercase mb-8">
<h2 className="heading-md text-primary uppercase mb-8">
Already have an account?
</h1>
<p className="text-center label-md">
<Link href="/user">
<Button
variant="tonal"
className="w-full flex justify-center mt-8 uppercase"
>
Log in
</Button>
</Link>
</p>
</h2>
<Link href="/user">
<Button
variant="tonal"
className="w-full flex justify-center mt-8 uppercase"
>
Log in
</Button>
</Link>
</Container>
</main>
)
Expand Down
16 changes: 14 additions & 2 deletions src/lib/helpers/toast.ts → src/lib/helpers/toast.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { DoneIcon, ErrorIcon } from "@/icons";
import { toast as sonnerToast } from "sonner"

export const toast = {
Expand All @@ -15,14 +16,25 @@ export const toast = {
title: string
}) => {
sonnerToast.success(title, {
className: "bg-green-100 text-green-900",
description,
duration: 10000,
icon: <DoneIcon color="rgb(20, 83, 45)"/>,
classNames: {
icon: "self-start pt-2",
toast: "items-start gap-3",
title: "text-md text-primary",
},
})
},
error: ({ description, title }: { description?: string; title: string }) => {
sonnerToast.error(title, {
className: "bg-red-100 text-red-900",
description,
icon: <ErrorIcon color="rgb(155, 34, 25)" />,
classNames: {
icon: "self-start pt-2",
toast: "items-start gap-3",
title: "text-md text-primary"
}
})
},
}