mirror of
https://github.com/Sosokker/B2D-Ventures.git
synced 2025-12-20 06:24:06 +01:00
refactor: use server action for login signup logout action
This commit is contained in:
parent
3beb6e2c72
commit
0929364496
@ -1,3 +0,0 @@
|
|||||||
export default function AuthError() {
|
|
||||||
return <div>Authentication Error</div>;
|
|
||||||
}
|
|
||||||
50
src/components/auth/action.ts
Normal file
50
src/components/auth/action.ts
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,30 +1,57 @@
|
|||||||
"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);
|
||||||
|
|
||||||
|
if (!result.success) {
|
||||||
|
const formErrors: { email?: string; password?: string } = {};
|
||||||
|
result.error.errors.forEach((error) => {
|
||||||
|
formErrors[error.path[0] as keyof typeof formErrors] = error.message;
|
||||||
});
|
});
|
||||||
router.push("/");
|
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">
|
||||||
|
<div>
|
||||||
<Input id="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
|
<Input id="email" type="text" value={email} onChange={(e) => setEmail(e.target.value)} placeholder="Email" />
|
||||||
|
{errors.email && <p className="text-red-600">{errors.email}</p>}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
<Input
|
<Input
|
||||||
id="password"
|
id="password"
|
||||||
type="password"
|
type="password"
|
||||||
@ -32,9 +59,12 @@ export function LoginForm() {
|
|||||||
onChange={(e) => setPassword(e.target.value)}
|
onChange={(e) => setPassword(e.target.value)}
|
||||||
placeholder="Password"
|
placeholder="Password"
|
||||||
/>
|
/>
|
||||||
<Button id="login" onClick={handleLogin}>
|
{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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
17
src/types/schemas/authentication.schema.ts
Normal file
17
src/types/schemas/authentication.schema.ts
Normal 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"],
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue
Block a user