Skip to content

Commit 6a091c9

Browse files
authored
Merge pull request #141 from CodeForStartup/feat/refactor-auth
feat: refactor auth
2 parents 0d23d30 + 52f81b4 commit 6a091c9

File tree

22 files changed

+717
-273
lines changed

22 files changed

+717
-273
lines changed

apps/web/@/actions/auth/index.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
"use server"
22

3+
import bcrypt from "bcrypt"
34
import { signIn, signOut } from "configs/auth"
5+
import { Prisma } from "database"
6+
import { createUser } from "database/src/users/queries"
7+
8+
import { redirect } from "@/utils/navigation"
9+
10+
import { SignUpDataOutput, signUpSchema } from "./type"
411

512
export const signInWithCredentials = async (email: string, password: string) => {
613
await signIn("credentials", {
@@ -18,3 +25,38 @@ export const onSignOut = async () => {
1825
redirectTo: "/",
1926
})
2027
}
28+
29+
// SIGN UP
30+
export const signUp = async (
31+
data: Pick<Prisma.UserCreateInput, "email" | "password">
32+
): Promise<SignUpDataOutput> => {
33+
try {
34+
// hash password
35+
const { email, password } = data
36+
const hashedPassword = await bcrypt.hash(password, 10)
37+
38+
await createUser({
39+
data: {
40+
email,
41+
password: hashedPassword,
42+
},
43+
})
44+
} catch (error) {
45+
if (error?.error?.code === "P2002") {
46+
return {
47+
formErrors: null,
48+
fieldErrors: {
49+
email: ["Email already exists"], // TODO: localize error message
50+
},
51+
}
52+
}
53+
54+
return {
55+
formErrors: [error?.message],
56+
fieldErrors: {},
57+
}
58+
}
59+
60+
// TODO: white this redirect not work
61+
redirect("/login")
62+
}

apps/web/@/actions/auth/type.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
import { z } from "zod"
2+
3+
export const signUpSchema = z.object({
4+
email: z.string().email("Email is invalid"),
5+
password: z.string().min(8),
6+
confirmPassword: z.string().min(8),
7+
})
8+
9+
export type SignUpDataInput = z.infer<typeof signUpSchema>
10+
11+
export type SignUpDataOutput = z.inferFlattenedErrors<typeof signUpSchema>

apps/web/@/messages/en.json

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,10 @@
142142
"auth": {
143143
"email_label": "Email",
144144
"password_label": "Password",
145+
"confirm_password_label": "Confirm Password",
145146
"email_placeholder": "Enter your email",
146147
"password_placeholder": "Enter your password",
148+
"confirm_password_placeholder": "Enter your password again",
147149
"dont_have_an_account": "Don't have an account?",
148150
"or_continue_with": "Or continue with",
149151
"github": "GitHub",
@@ -165,11 +167,14 @@
165167
"sign_in_with_linkedin": "Sign in with LinkedIn",
166168
"sign_in_with_instagram": "Sign in with Instagram",
167169
"sign_in_with_youtube": "Sign in with YouTube",
168-
"sign_in_with_tiktok": "Sign in with TikTok"
170+
"sign_in_with_tiktok": "Sign in with TikTok",
171+
"sign_in_to_your_account_to_continue": "Sign in to your account to continue"
169172
},
170173
"sign_up": {
171174
"title": "Sign Up",
172-
"description": "Sign up for an account"
175+
"description": "Sign up for an account",
176+
"already_have_an_account": "Already have an account?",
177+
"sign_in": "Sign In"
173178
},
174179
"forgot_password": {
175180
"title": "Forgot Password",
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Card, CardContent, Typography } from "ui"
2+
3+
interface AuthFormProps {
4+
title: string
5+
description: string
6+
children: React.ReactNode
7+
}
8+
9+
export default function AuthForm({ title, description, children }: AuthFormProps) {
10+
return (
11+
<div>
12+
<div className="text-center">
13+
<Typography variant="h1">{title}</Typography>
14+
<Typography variant="lead">{description}</Typography>
15+
</div>
16+
17+
<Card className="mt-6">
18+
<CardContent className="p-6">{children}</CardContent>
19+
</Card>
20+
</div>
21+
)
22+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function ForgotPassword() {
2+
return <div>ForgotPassword</div>
3+
}
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export default function ResetPassword() {
2+
return <div>ResetPassword</div>
3+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
"use client"
2+
3+
import Link from "next/link"
4+
5+
import { zodResolver } from "@hookform/resolvers/zod"
6+
import { Github } from "lucide-react"
7+
import { useTranslations } from "next-intl"
8+
import { useForm } from "react-hook-form"
9+
import { Button, Input, Label, Typography } from "ui"
10+
import { z } from "zod"
11+
12+
import { signInWithCredentials, signInWithGithub } from "@/actions/auth"
13+
14+
import AuthForm from "../auth-form"
15+
16+
type FormData = {
17+
email: string
18+
password: string
19+
}
20+
21+
export default function SignIn() {
22+
const t = useTranslations("auth")
23+
24+
const { register, handleSubmit, formState } = useForm<FormData>({
25+
resolver: zodResolver(
26+
z.object({
27+
email: z.string().email(),
28+
password: z.string().min(8),
29+
})
30+
),
31+
})
32+
33+
console.log("formState", formState)
34+
35+
const onSignIn = async (data: FormData) => {
36+
await signInWithCredentials(data.email, data.password)
37+
}
38+
39+
return (
40+
<div className="w-full max-w-md flex-1 p-8">
41+
<AuthForm
42+
title={t("sign_in.title")}
43+
description={t("sign_in.description")}
44+
>
45+
<div className="w-full">
46+
<form onSubmit={handleSubmit(onSignIn)}>
47+
<div className="grid gap-4">
48+
<div className="grid gap-2">
49+
<Label htmlFor="email">Email</Label>
50+
<Input
51+
id="email"
52+
placeholder="[email protected]"
53+
type="email"
54+
autoCapitalize="none"
55+
autoComplete="email"
56+
autoCorrect="off"
57+
{...register("email", { required: true })}
58+
/>
59+
</div>
60+
<div className="grid gap-2">
61+
<Label htmlFor="password">Password</Label>
62+
<Input
63+
id="password"
64+
placeholder="********"
65+
type="password"
66+
autoCapitalize="none"
67+
autoCorrect="off"
68+
{...register("password", { required: true })}
69+
/>
70+
</div>
71+
<Button type="submit">{t("sign_in.title")}</Button>
72+
</div>
73+
</form>
74+
<div className="flex w-full flex-col">
75+
<div className="relative">
76+
<div className="absolute inset-0 flex items-center">
77+
<span className="w-full border-t" />
78+
</div>
79+
<div className="relative flex justify-center py-4 text-xs uppercase">
80+
<span className="bg-background px-2 text-muted-foreground">
81+
{t("or_continue_with")}
82+
</span>
83+
</div>
84+
</div>
85+
86+
<form action={signInWithGithub}>
87+
<Button
88+
variant="outline"
89+
type="submit"
90+
className="w-full"
91+
>
92+
<Github size={16} />
93+
<span className="ml-2">{t("github")}</span>
94+
</Button>
95+
</form>
96+
</div>
97+
</div>
98+
</AuthForm>
99+
<div className="mt-4 text-center">
100+
<Link href="/signup">
101+
<Typography
102+
variant="span"
103+
className="mt-4"
104+
>
105+
{t("dont_have_an_account")}
106+
<Typography
107+
className="pl-1 font-bold hover:underline"
108+
variant="span"
109+
>
110+
{t("sign_up.title")}
111+
</Typography>
112+
</Typography>
113+
</Link>
114+
</div>
115+
</div>
116+
)
117+
}

0 commit comments

Comments
 (0)