refactor: use server action for login signup logout action

This commit is contained in:
Sosokker 2024-11-04 20:16:11 +07:00
parent 3beb6e2c72
commit 0929364496
6 changed files with 173 additions and 56 deletions

View File

@ -1,3 +0,0 @@
export default function AuthError() {
return <div>Authentication Error</div>;
}

View File

@ -0,0 +1,50 @@
"use server";
import { revalidatePath } from "next/cache";
import { createSupabaseClient } from "@/lib/supabase/serverComponentClient";
import { redirect } from "next/navigation";
export async function login(formData: FormData) {
const supabase = await createSupabaseClient();
const data = {
email: formData.get("email") as string,
password: formData.get("password") as string,
};
const { error } = await supabase.auth.signInWithPassword(data);
if (error) {
throw error;
}
revalidatePath("/", "layout");
redirect("/");
}
export async function signup(formData: FormData) {
const supabase = await createSupabaseClient();
const data = {
email: formData.get("email") as string,
password: formData.get("password") as string,
};
const { error } = await supabase.auth.signUp(data);
if (error) {
throw error;
}
revalidatePath("/", "layout");
redirect("/");
}
export async function logout() {
const supabase = await createSupabaseClient();
const { error } = await supabase.auth.signOut();
if (error) {
throw new Error("Logout failed: " + error.message);
}
}

View File

@ -1,40 +1,70 @@
"use client"; "use client";
import React from "react"; import React, { useState } from "react";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { useState } from "react";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation"; import { login } from "./action";
import { LoginFormSchema } from "@/types/schemas/authentication.schema";
import toast from "react-hot-toast";
export function LoginForm() { export function LoginForm() {
const router = useRouter();
const supabase = createSupabaseClient();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [errors, setErrors] = useState<{ email?: string; password?: string; server?: string }>({});
const handleLogin = async (event: React.MouseEvent<HTMLButtonElement>) => { const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
await supabase.auth.signInWithPassword({
email, const formData = { email, password };
password,
}); const result = LoginFormSchema.safeParse(formData);
router.push("/");
if (!result.success) {
const formErrors: { email?: string; password?: string } = {};
result.error.errors.forEach((error) => {
formErrors[error.path[0] as keyof typeof formErrors] = error.message;
});
setErrors(formErrors);
return;
}
setErrors({});
const form = new FormData();
form.append("email", email);
form.append("password", password);
try {
await login(form);
toast.success("Login succesfully!");
} catch (authError: any) {
setErrors((prevErrors) => ({
...prevErrors,
server: authError.message || "An error occurred during login.",
}));
}
}; };
return ( return (
<div className="flex flex-col space-y-2"> <form onSubmit={handleSubmit} className="flex flex-col space-y-2">
<Input id="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" /> <div>
<Input <Input id="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
id="password" {errors.email && <p className="text-red-600">{errors.email}</p>}
type="password" </div>
value={password} <div>
onChange={(e) => setPassword(e.target.value)} <Input
placeholder="Password" id="password"
/> type="password"
<Button id="login" onClick={handleLogin}> value={password}
onChange={(e) => setPassword(e.target.value)}
placeholder="Password"
/>
{errors.password && <p className="text-red-600">{errors.password}</p>}
</div>
{errors.server && <p className="text-red-600">{errors.server}</p>}
<Button id="login" type="submit">
Login Login
</Button> </Button>
</div> </form>
); );
} }

View File

@ -1,24 +1,30 @@
"use client"; "use client";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient"; import { logout } from "./action"; // Adjust the import path accordingly
import { usePathname } from "next/navigation"; import { usePathname, useRouter } from "next/navigation";
import { useRouter } from "next/navigation"; import toast from "react-hot-toast";
export function LogoutButton() { export function LogoutButton() {
const supabase = createSupabaseClient();
const pathname = usePathname(); const pathname = usePathname();
const router = useRouter(); const router = useRouter();
const handleLogout = async () => { const handleLogout = async () => {
await supabase.auth.signOut(); try {
await logout();
if (pathname === "/") { if (pathname === "/") {
window.location.reload(); window.location.reload();
} else { } else {
await router.push("/"); await router.push("/");
window.location.reload(); window.location.reload();
}
} catch (error: any) {
toast.error(error.message || "An error occurred during logout.");
} }
}; };
return <button onClick={handleLogout}>Logout</button>; return (
<div>
<button onClick={handleLogout}>Logout</button>
</div>
);
} }

View File

@ -1,50 +1,65 @@
"use client"; "use client";
import React from "react"; import React, { useState } from "react";
import { createSupabaseClient } from "@/lib/supabase/clientComponentClient";
import { useState } from "react";
import { Input } from "@/components/ui/input"; import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { useRouter } from "next/navigation"; import { useRouter } from "next/navigation";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { signup } from "./action";
import { signupSchema } from "@/types/schemas/authentication.schema";
export function SignupForm() { export function SignupForm() {
const router = useRouter(); const router = useRouter();
const supabase = createSupabaseClient();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
const [password, setPassword] = useState(""); const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [confirmPassword, setConfirmPassword] = useState(""); const [confirmPassword, setConfirmPassword] = useState("");
const handleSignup = async (event: React.MouseEvent<HTMLButtonElement>) => { const handleSignup = async (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault(); event.preventDefault();
if (password !== confirmPassword) { const parsedData = signupSchema.safeParse({
alert("Passwords do not match!"); email,
password,
confirmPassword,
});
if (!parsedData.success) {
setError(parsedData.error.errors[0].message);
return; return;
} }
const { error } = await supabase.auth.signUp({ const formData = new FormData();
email, formData.append("email", email);
password, formData.append("password", password);
}); formData.append("confirmPassword", confirmPassword);
if (error) { try {
toast.error(error.message); await signup(formData);
} else {
toast.success("Account created successfully!"); toast.success("Account created successfully!");
router.push("/"); router.push("/");
} catch (error: any) {
setError(error.message);
} }
}; };
return ( return (
<div className="flex flex-col space-y-2"> <form onSubmit={handleSignup} className="flex flex-col space-y-2">
<Input id="email" type="email" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" /> <Input
id="email"
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Email"
required
/>
<Input <Input
id="password" id="password"
type="password" type="password"
value={password} value={password}
onChange={(e) => setPassword(e.target.value)} onChange={(e) => setPassword(e.target.value)}
placeholder="Password" placeholder="Password"
required
/> />
<Input <Input
id="confirmPassword" id="confirmPassword"
@ -52,10 +67,12 @@ export function SignupForm() {
value={confirmPassword} value={confirmPassword}
onChange={(e) => setConfirmPassword(e.target.value)} onChange={(e) => setConfirmPassword(e.target.value)}
placeholder="Confirm Password" placeholder="Confirm Password"
required
/> />
<Button id="signup" onClick={handleSignup}> {error && <p className="text-red-600">{error}</p>}
<Button id="signup" type="submit">
Sign Up Sign Up
</Button> </Button>
</div> </form>
); );
} }

View File

@ -0,0 +1,17 @@
import * as z from "zod";
export const LoginFormSchema = z.object({
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters"),
});
export const signupSchema = z
.object({
email: z.string().email("Invalid email format"),
password: z.string().min(6, "Password must be at least 6 characters long"),
confirmPassword: z.string().min(6, "Confirm password must be at least 6 characters long"),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords must match",
path: ["confirmPassword"],
});