diff --git a/src/actions/auth.ts b/src/actions/auth.ts
deleted file mode 100644
index ceb5340..0000000
--- a/src/actions/auth.ts
+++ /dev/null
@@ -1,78 +0,0 @@
-"use server";
-
-import bcrypt from "bcrypt";
-import prisma from "@/lib/prisma";
-import { signIn } from "@/lib/auth";
-import { authSchema } from "@/lib/zod";
-import { getTranslations } from "next-intl/server";
-import { CredentialsSignInFormValues } from "@/components/credentials-sign-in-form";
-import { CredentialsSignUpFormValues } from "@/components/credentials-sign-up-form";
-
-const saltRounds = 10;
-
-export async function signInWithCredentials(formData: CredentialsSignInFormValues, redirectTo?: string) {
- const t = await getTranslations("signInWithCredentials");
-
- try {
- // Parse credentials using authSchema for validation
- const { email, password } = await authSchema.parseAsync(formData);
-
- // Find user by email
- const user = await prisma.user.findUnique({ where: { email } });
-
- // Check if the user exists
- if (!user) {
- throw new Error(t("userNotFound"));
- }
-
- // Check if the user has a password
- if (!user.password) {
- throw new Error(t("invalidCredentials"));
- }
-
- // Check if the password matches
- const passwordMatch = await bcrypt.compare(password, user.password);
- if (!passwordMatch) {
- throw new Error(t("incorrectPassword"));
- }
-
- await signIn("credentials", { ...formData, redirectTo, redirect: !!redirectTo });
- return { success: true };
- } catch (error) {
- return { error: error instanceof Error ? error.message : t("signInFailedFallback") };
- }
-}
-
-export async function signUpWithCredentials(formData: CredentialsSignUpFormValues) {
- const t = await getTranslations("signUpWithCredentials");
-
- try {
- const validatedData = await authSchema.parseAsync(formData);
-
- // Check if user already exists
- const existingUser = await prisma.user.findUnique({ where: { email: validatedData.email } });
- if (existingUser) {
- throw new Error(t("userAlreadyExists"));
- }
-
- // Hash password and create user
- const pwHash = await bcrypt.hash(validatedData.password, saltRounds);
- const user = await prisma.user.create({
- data: { email: validatedData.email, password: pwHash },
- });
-
- // Assign admin role if first user
- const userCount = await prisma.user.count();
- if (userCount === 1) {
- await prisma.user.update({ where: { id: user.id }, data: { role: "ADMIN" } });
- }
-
- return { success: true };
- } catch (error) {
- return { error: error instanceof Error ? error.message : t("registrationFailedFallback") };
- }
-}
-
-export async function signInWithGithub(redirectTo?: string) {
- await signIn("github", { redirectTo, redirect: !!redirectTo });
-}
diff --git a/src/app/(auth)/layout.tsx b/src/app/(auth)/layout.tsx
deleted file mode 100644
index f62b2a5..0000000
--- a/src/app/(auth)/layout.tsx
+++ /dev/null
@@ -1,37 +0,0 @@
-import Link from "next/link";
-import Image from "next/image";
-import { CodeIcon } from "lucide-react";
-
-interface AuthLayoutProps {
- children: React.ReactNode;
-}
-
-export default async function AuthLayout({
- children
-}: AuthLayoutProps) {
- return (
-
- );
-}
diff --git a/src/app/(auth)/sign-in/page.tsx b/src/app/(auth)/sign-in/page.tsx
index f8c93d3..6293983 100644
--- a/src/app/(auth)/sign-in/page.tsx
+++ b/src/app/(auth)/sign-in/page.tsx
@@ -1,5 +1,98 @@
-import { SignInForm } from "@/components/sign-in-form";
+import Link from "next/link";
+import Image from "next/image";
+import { CodeIcon } from "lucide-react";
+import { Button } from "@/components/ui/button";
+import { providerMap, signIn } from "@/lib/auth";
+import { getTranslations } from "next-intl/server";
+import { FaGithub, FaGoogle } from "react-icons/fa";
-export default function SignInPage() {
- return ;
+interface ProviderIconProps {
+ providerId: string;
+}
+
+const ProviderIcon = ({ providerId }: ProviderIconProps) => {
+ switch (providerId) {
+ case "github":
+ return ;
+ case "google":
+ return ;
+ default:
+ return null;
+ }
+};
+
+interface SignInPageProps {
+ searchParams: Promise<{
+ callbackUrl: string | undefined;
+ }>;
+}
+
+export default async function SignInPage({ searchParams }: SignInPageProps) {
+ const { callbackUrl } = await searchParams;
+ const t = await getTranslations("SignInForm");
+
+ return (
+
+
+
+
+
+
+
+
{t("title")}
+
+ {t("description")}
+
+
+
+
+ {t("or")}
+
+
+ {Object.values(providerMap).map((provider) => {
+ return (
+
+ );
+ })}
+
+
+
+
+
+
+
+
+ );
}
diff --git a/src/app/(auth)/sign-up/page.tsx b/src/app/(auth)/sign-up/page.tsx
deleted file mode 100644
index b71518b..0000000
--- a/src/app/(auth)/sign-up/page.tsx
+++ /dev/null
@@ -1,5 +0,0 @@
-import { SignUpForm } from "@/components/sign-up-form";
-
-export default function SignUpPage() {
- return ;
-}
diff --git a/src/components/credentials-sign-in-form.tsx b/src/components/credentials-sign-in-form.tsx
deleted file mode 100644
index 84e370d..0000000
--- a/src/components/credentials-sign-in-form.tsx
+++ /dev/null
@@ -1,121 +0,0 @@
-"use client";
-
-import { z } from "zod";
-import {
- Form,
- FormField,
- FormItem,
- FormControl,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form";
-import { toast } from "sonner";
-import { authSchema } from "@/lib/zod";
-import { useForm } from "react-hook-form";
-import { useTranslations } from "next-intl";
-import { Input } from "@/components/ui/input";
-import { Button } from "@/components/ui/button";
-import { useState, useTransition } from "react";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { signInWithCredentials } from "@/actions/auth";
-import { EyeIcon, EyeOffIcon, MailIcon } from "lucide-react";
-import { useRouter, useSearchParams } from "next/navigation";
-
-export type CredentialsSignInFormValues = z.infer;
-
-export function CredentialsSignInForm() {
- const router = useRouter();
- const searchParams = useSearchParams();
- const redirectTo = searchParams.get("redirectTo");
- const t = useTranslations("CredentialsSignInForm");
- const [isPending, startTransition] = useTransition();
- const [isVisible, setIsVisible] = useState(false);
-
- const form = useForm({
- resolver: zodResolver(authSchema),
- defaultValues: {
- email: "",
- password: "",
- },
- });
-
- const toggleVisibility = () => setIsVisible((prev) => !prev);
-
- const onSubmit = (data: CredentialsSignInFormValues) => {
- startTransition(async () => {
- const result = await signInWithCredentials(data);
-
- if (result?.error) {
- toast.error(t("signInFailed"), {
- description: result.error,
- });
- } else {
- toast.success(t("signInSuccess"));
- router.push(redirectTo || "/");
- }
- });
- };
-
- return (
-
-
- );
-}
diff --git a/src/components/credentials-sign-up-form.tsx b/src/components/credentials-sign-up-form.tsx
deleted file mode 100644
index af9750c..0000000
--- a/src/components/credentials-sign-up-form.tsx
+++ /dev/null
@@ -1,123 +0,0 @@
-"use client";
-
-import { z } from "zod";
-import {
- Form,
- FormControl,
- FormField,
- FormItem,
- FormLabel,
- FormMessage,
-} from "@/components/ui/form";
-import { toast } from "sonner";
-import { authSchema } from "@/lib/zod";
-import { useForm } from "react-hook-form";
-import { useTranslations } from "next-intl";
-import { Input } from "@/components/ui/input";
-import { Button } from "@/components/ui/button";
-import { useState, useTransition } from "react";
-import { zodResolver } from "@hookform/resolvers/zod";
-import { signUpWithCredentials } from "@/actions/auth";
-import { EyeIcon, EyeOffIcon, MailIcon } from "lucide-react";
-import { useRouter, useSearchParams } from "next/navigation";
-
-export type CredentialsSignUpFormValues = z.infer;
-
-export function CredentialsSignUpForm() {
- const router = useRouter();
- const searchParams = useSearchParams();
- const redirectTo = searchParams.get("redirectTo");
- const t = useTranslations("CredentialsSignUpForm");
- const [isPending, startTransition] = useTransition();
- const [isVisible, setIsVisible] = useState(false);
-
- const form = useForm({
- resolver: zodResolver(authSchema),
- defaultValues: {
- email: "",
- password: "",
- },
- });
-
- const toggleVisibility = () => setIsVisible((prev) => !prev);
-
- const onSubmit = (data: CredentialsSignUpFormValues) => {
- startTransition(async () => {
- const result = await signUpWithCredentials(data);
-
- if (result?.error) {
- toast.error(t("signUpFailed"), {
- description: result.error,
- });
- } else {
- toast.success(t("signUpSuccess"), {
- description: t("signUpSuccessDescription"),
- });
- router.push(`/sign-in?${redirectTo}`)
- }
- });
- };
-
- return (
-
-
- );
-}
diff --git a/src/components/github-sign-in-form.tsx b/src/components/github-sign-in-form.tsx
deleted file mode 100644
index 30d2022..0000000
--- a/src/components/github-sign-in-form.tsx
+++ /dev/null
@@ -1,27 +0,0 @@
-"use client";
-
-import { useTranslations } from "next-intl";
-import { Button } from "@/components/ui/button";
-import { signInWithGithub } from "@/actions/auth";
-import { useSearchParams } from "next/navigation";
-
-export function GithubSignInForm() {
- const t = useTranslations();
- const searchParams = useSearchParams();
- const redirectTo = searchParams.get("redirectTo");
- const signInAction = signInWithGithub.bind(null, redirectTo || "/");
-
- return (
-
- );
-}
diff --git a/src/components/sign-in-form.tsx b/src/components/sign-in-form.tsx
deleted file mode 100644
index db8213c..0000000
--- a/src/components/sign-in-form.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-"use client";
-
-import { useTranslations } from "next-intl";
-import { useRouter, useSearchParams } from "next/navigation";
-import { GithubSignInForm } from "@/components/github-sign-in-form";
-import { CredentialsSignInForm } from "@/components/credentials-sign-in-form";
-
-export function SignInForm() {
- const router = useRouter();
- const searchParams = useSearchParams();
- const t = useTranslations("SignInForm");
-
- const handleSignUp = () => {
- const params = new URLSearchParams(searchParams.toString());
- router.push(`/sign-up?${params.toString()}`);
- };
-
- return (
-
-
-
{t("title")}
-
- {t("description")}
-
-
-
-
-
- {t("or")}
-
-
-
-
- {t("noAccount")}{" "}
-
- {t("signUp")}
-
-
-
- );
-}
diff --git a/src/components/sign-up-form.tsx b/src/components/sign-up-form.tsx
deleted file mode 100644
index 5a54453..0000000
--- a/src/components/sign-up-form.tsx
+++ /dev/null
@@ -1,44 +0,0 @@
-"use client";
-
-import { useTranslations } from "next-intl";
-import { useRouter, useSearchParams } from "next/navigation";
-import { GithubSignInForm } from "@/components/github-sign-in-form";
-import { CredentialsSignUpForm } from "@/components/credentials-sign-up-form";
-
-export function SignUpForm() {
- const router = useRouter();
- const searchParams = useSearchParams();
- const t = useTranslations("SignUpForm");
-
- const handleSignIn = () => {
- const params = new URLSearchParams(searchParams.toString());
- router.push(`/sign-in?${params.toString()}`);
- };
-
- return (
-
-
-
{t("title")}
-
- {t("description")}
-
-
-
-
-
- {t("or")}
-
-
-
-
- {t("haveAccount")}{" "}
-
- {t("signIn")}
-
-
-
- );
-}
diff --git a/src/lib/zod.ts b/src/lib/zod.ts
deleted file mode 100644
index 89246d1..0000000
--- a/src/lib/zod.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import { z } from "zod";
-
-export const authSchema = z.object({
- email: z
- .string()
- .nonempty("Email is required")
- .email("Invalid email"),
- password: z
- .string()
- .nonempty("Password is required")
- .min(8, "Password must be at least 8 characters")
- .max(32, "Password must be less than 32 characters"),
-});