feat: refactor authentication flow, implement user hooks, and add validation schemas for login/signup
This commit is contained in:
parent
e45772e2a9
commit
a2ae7d5b5a
@ -6,13 +6,14 @@
|
|||||||
"useIgnoreFile": false
|
"useIgnoreFile": false
|
||||||
},
|
},
|
||||||
"files": {
|
"files": {
|
||||||
"ignoreUnknown": false,
|
"ignoreUnknown": true,
|
||||||
"includes": [
|
"includes": [
|
||||||
"**/src/**/*",
|
"**/src/**/*",
|
||||||
"**/.vscode/**/*",
|
"**/.vscode/**/*",
|
||||||
"**/index.html",
|
"**/index.html",
|
||||||
"**/vite.config.js",
|
"**/vite.config.js",
|
||||||
"!**/src/routeTree.gen.ts"
|
"!**/src/routeTree.gen.ts",
|
||||||
|
"!**/node_modules/**/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"formatter": {
|
"formatter": {
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
import { HeroUIProvider as HeroProvider } from "@heroui/react"
|
import { HeroUIProvider as HeroProvider } from "@heroui/react"
|
||||||
|
|
||||||
export const HeroUIProvider = ({ children }: { children: React.ReactNode }) => {
|
export const HeroUIProvider = ({ children }: { children: React.ReactNode }) => {
|
||||||
return <HeroProvider>{children}</HeroProvider>
|
return <HeroProvider validationBehavior="native">{children}</HeroProvider>
|
||||||
}
|
}
|
||||||
|
|||||||
84
src/lib/db/user.ts
Normal file
84
src/lib/db/user.ts
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { redirect } from "@tanstack/react-router"
|
||||||
|
import { createServerFn } from "@tanstack/react-start"
|
||||||
|
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
||||||
|
import { loginFormSchema, signupFormSchema } from "../validation/user"
|
||||||
|
|
||||||
|
export const getUser = createServerFn().handler(async () => {
|
||||||
|
const supabase = getSupabaseServerClient()
|
||||||
|
const { data, error } = await supabase.auth.getUser()
|
||||||
|
if (error || !data.user) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
message: error?.message ?? "Unknown error"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
user: {
|
||||||
|
id: data.user.id,
|
||||||
|
email: data.user.email,
|
||||||
|
name: data.user.user_metadata.name || ""
|
||||||
|
},
|
||||||
|
error: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const loginUser = createServerFn({
|
||||||
|
method: "POST"
|
||||||
|
})
|
||||||
|
.validator(loginFormSchema)
|
||||||
|
.handler(async ({ data }) => {
|
||||||
|
const supabase = getSupabaseServerClient()
|
||||||
|
const login = await supabase.auth.signInWithPassword({
|
||||||
|
email: data.email,
|
||||||
|
password: data.password
|
||||||
|
})
|
||||||
|
if (login.error) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
message: login.error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: false
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
export const logoutUser = createServerFn().handler(async () => {
|
||||||
|
const supabase = getSupabaseServerClient()
|
||||||
|
|
||||||
|
const { error } = await supabase.auth.signOut()
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
message: error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw redirect({
|
||||||
|
to: "/",
|
||||||
|
viewTransition: true,
|
||||||
|
replace: true
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
export const signupUser = createServerFn({ method: "POST" })
|
||||||
|
.validator(signupFormSchema)
|
||||||
|
.handler(async ({ data }) => {
|
||||||
|
const supabase = getSupabaseServerClient()
|
||||||
|
const { error } = await supabase.auth.signUp({
|
||||||
|
email: data.email,
|
||||||
|
password: data.password
|
||||||
|
})
|
||||||
|
if (error) {
|
||||||
|
return {
|
||||||
|
error: true,
|
||||||
|
message: error.message
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw redirect({
|
||||||
|
href: data.redirectUrl || "/"
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -15,7 +15,7 @@ export const useValidation = <T,>({
|
|||||||
schema
|
schema
|
||||||
}: {
|
}: {
|
||||||
formData: FormDataValidation
|
formData: FormDataValidation
|
||||||
schema?: z.ZodSchema<T>
|
schema?: z.ZodType<T>
|
||||||
}) => {
|
}) => {
|
||||||
const result =
|
const result =
|
||||||
schema?.safeParse(formData) ?? defaultSchema?.safeParse(formData)
|
schema?.safeParse(formData) ?? defaultSchema?.safeParse(formData)
|
||||||
@ -25,6 +25,7 @@ export const useValidation = <T,>({
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!result.success) {
|
if (!result.success) {
|
||||||
|
//FIXME: Flatten errors, new in zod v4
|
||||||
setErrors(result.error.flatten().fieldErrors as T)
|
setErrors(result.error.flatten().fieldErrors as T)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
54
src/lib/hooks/user/useLogin.tsx
Normal file
54
src/lib/hooks/user/useLogin.tsx
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query"
|
||||||
|
import { useNavigate } from "@tanstack/react-router"
|
||||||
|
import { toast } from "sonner"
|
||||||
|
import type z from "zod"
|
||||||
|
import { loginUser } from "@/lib/db/user"
|
||||||
|
import { loginFormSchema } from "@/lib/validation/user"
|
||||||
|
import { useValidation } from "../useValidation"
|
||||||
|
|
||||||
|
type TLoginForm = z.infer<typeof loginFormSchema>
|
||||||
|
|
||||||
|
export const useLogin = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const { errors, validate } = useValidation({
|
||||||
|
defaultSchema: loginFormSchema
|
||||||
|
})
|
||||||
|
const loginMutation = useMutation({
|
||||||
|
mutationKey: ["login"],
|
||||||
|
mutationFn: async (data: TLoginForm) =>
|
||||||
|
loginUser({
|
||||||
|
data
|
||||||
|
}),
|
||||||
|
onMutate: () => {
|
||||||
|
toast.loading("Logging in...", { id: "login" })
|
||||||
|
},
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Login successful! Redirecting to posts..", { id: "login" })
|
||||||
|
navigate({
|
||||||
|
to: "/post"
|
||||||
|
})
|
||||||
|
},
|
||||||
|
onError: () => {
|
||||||
|
toast.error("Failed to log in.", { id: "login" })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const validateLogin = (formData: TLoginForm) => {
|
||||||
|
const isValid = validate({
|
||||||
|
formData
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!isValid) {
|
||||||
|
toast.error("Error en el formulario.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
loginMutation.mutate(formData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
login: validateLogin,
|
||||||
|
isPending: loginMutation.isPending,
|
||||||
|
errors: errors
|
||||||
|
}
|
||||||
|
}
|
||||||
44
src/lib/hooks/user/useSignup.tsx
Normal file
44
src/lib/hooks/user/useSignup.tsx
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
import { useMutation } from "@tanstack/react-query"
|
||||||
|
import { useNavigate } from "@tanstack/react-router"
|
||||||
|
import { toast } from "sonner"
|
||||||
|
import type z from "zod"
|
||||||
|
import { signupUser } from "@/lib/db/user"
|
||||||
|
import { signupFormSchema } from "@/lib/validation/user"
|
||||||
|
import { useValidation } from "../useValidation"
|
||||||
|
|
||||||
|
type TSignupForm = z.infer<typeof signupFormSchema>
|
||||||
|
|
||||||
|
export const useSignup = () => {
|
||||||
|
const navigate = useNavigate()
|
||||||
|
const { validate, errors } = useValidation({
|
||||||
|
defaultSchema: signupFormSchema
|
||||||
|
})
|
||||||
|
const signup = useMutation({
|
||||||
|
mutationKey: ["signup"],
|
||||||
|
mutationFn: async (data: TSignupForm) => signupUser({ data }),
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Signup successful! Redirecting to login...", {
|
||||||
|
id: "signup"
|
||||||
|
})
|
||||||
|
navigate({
|
||||||
|
to: "/login"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const validateSignup = (formData: TSignupForm) => {
|
||||||
|
const isValid = validate({ formData })
|
||||||
|
if (!isValid) {
|
||||||
|
toast.error("Signup failed. Please check your input.", {
|
||||||
|
id: "signup"
|
||||||
|
})
|
||||||
|
}
|
||||||
|
signup.mutate(formData)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
signup: validateSignup,
|
||||||
|
errors,
|
||||||
|
isPending: signup.isPending
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { getRouteApi } from "@tanstack/react-router"
|
|
||||||
import { createServerFn, useServerFn } from "@tanstack/react-start"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
|
||||||
import { loginSchema } from "../schemas/login"
|
|
||||||
|
|
||||||
const apiRouter = getRouteApi("/login")
|
|
||||||
|
|
||||||
export const loginFn = createServerFn({ method: "POST" })
|
|
||||||
.validator(loginSchema)
|
|
||||||
.handler(async ({ data }) => {
|
|
||||||
const supabase = getSupabaseServerClient()
|
|
||||||
const response = await supabase.auth.signInWithPassword({
|
|
||||||
email: data.email,
|
|
||||||
password: data.password
|
|
||||||
})
|
|
||||||
console.log(response)
|
|
||||||
if (response.error) {
|
|
||||||
return {
|
|
||||||
error: true,
|
|
||||||
message: response.error.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
export const mutationLogin = () => {
|
|
||||||
const navigate = apiRouter.useNavigate()
|
|
||||||
const loginFunction = useServerFn(loginFn)
|
|
||||||
const mutation = useMutation({
|
|
||||||
mutationKey: ["login"],
|
|
||||||
mutationFn: async (data: { email: string; password: string }) => {
|
|
||||||
toast.loading("Logging in...", { id: "login" })
|
|
||||||
return loginFunction({
|
|
||||||
data: {
|
|
||||||
email: data.email,
|
|
||||||
password: data.password
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onSuccess: (data) => {
|
|
||||||
if (data?.error) {
|
|
||||||
toast.error(data.message, { id: "login" })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
toast.success("Login successful! Redirecting to posts...", {
|
|
||||||
id: "login"
|
|
||||||
})
|
|
||||||
navigate({
|
|
||||||
to: "/post"
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onError: (error) => {
|
|
||||||
toast.error("Login failed. Please try again.", { id: "login" })
|
|
||||||
console.log("No se ha podido procesar el login", error)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return mutation
|
|
||||||
}
|
|
||||||
@ -1,59 +0,0 @@
|
|||||||
import { useMutation } from "@tanstack/react-query"
|
|
||||||
import { getRouteApi, redirect } from "@tanstack/react-router"
|
|
||||||
import { createServerFn, useServerFn } from "@tanstack/react-start"
|
|
||||||
import { toast } from "sonner"
|
|
||||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
|
||||||
import { signupSchema } from "../schemas/signup"
|
|
||||||
|
|
||||||
const apiRouter = getRouteApi("/signup")
|
|
||||||
export const signupFn = createServerFn({ method: "POST" })
|
|
||||||
.validator(signupSchema)
|
|
||||||
.handler(async ({ data }) => {
|
|
||||||
const supabase = getSupabaseServerClient()
|
|
||||||
const { error } = await supabase.auth.signUp({
|
|
||||||
email: data.email,
|
|
||||||
password: data.password
|
|
||||||
})
|
|
||||||
if (error) {
|
|
||||||
return {
|
|
||||||
error: true,
|
|
||||||
message: error.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw redirect({
|
|
||||||
href: data.redirectUrl || "/"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const mutationSignup = () => {
|
|
||||||
const navigate = apiRouter.useNavigate()
|
|
||||||
const signup = useServerFn(signupFn)
|
|
||||||
const mutation = useMutation({
|
|
||||||
mutationKey: ["signup"],
|
|
||||||
mutationFn: async (data: {
|
|
||||||
email: string
|
|
||||||
password: string
|
|
||||||
redirectUrl?: string
|
|
||||||
}) => {
|
|
||||||
toast.loading("Signing up...", { id: "signup" })
|
|
||||||
return signup({
|
|
||||||
data: {
|
|
||||||
email: data.email,
|
|
||||||
password: data.password,
|
|
||||||
redirectUrl: data.redirectUrl
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
onSuccess: () => {
|
|
||||||
toast.success("Signup successful! Redirecting to login...", {
|
|
||||||
id: "signup"
|
|
||||||
})
|
|
||||||
navigate({
|
|
||||||
to: "/login"
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return mutation
|
|
||||||
}
|
|
||||||
@ -1,6 +0,0 @@
|
|||||||
import z from "zod"
|
|
||||||
|
|
||||||
export const loginSchema = z.object({
|
|
||||||
email: z.email("Invalid email address"),
|
|
||||||
password: z.string().min(6, "Password must be at least 6 characters long")
|
|
||||||
})
|
|
||||||
@ -1,7 +0,0 @@
|
|||||||
import z from "zod"
|
|
||||||
|
|
||||||
export const signupSchema = z.object({
|
|
||||||
email: z.email("Invalid email address"),
|
|
||||||
password: z.string().min(6, "Password must be at least 6 characters long"),
|
|
||||||
redirectUrl: z.string().optional()
|
|
||||||
})
|
|
||||||
12
src/lib/validation/user.ts
Normal file
12
src/lib/validation/user.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import z from "zod"
|
||||||
|
|
||||||
|
export const loginFormSchema = z.object({
|
||||||
|
email: z.string("Invalid email address"),
|
||||||
|
password: z.string().min(1, "Password must be at least 1 character long")
|
||||||
|
})
|
||||||
|
|
||||||
|
export const signupFormSchema = z.object({
|
||||||
|
email: z.string("Invalid email address"),
|
||||||
|
password: z.string().min(6, "Password must be at least 6 characters long"),
|
||||||
|
redirectUrl: z.string().optional()
|
||||||
|
})
|
||||||
@ -6,31 +6,18 @@ import {
|
|||||||
Scripts
|
Scripts
|
||||||
} from "@tanstack/react-router"
|
} from "@tanstack/react-router"
|
||||||
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"
|
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools"
|
||||||
import { createServerFn } from "@tanstack/react-start"
|
|
||||||
import { HeroUIProvider } from "@/integrations/heroui/provider"
|
import { HeroUIProvider } from "@/integrations/heroui/provider"
|
||||||
import { SonnerProvider } from "@/integrations/sonner/provider"
|
import { SonnerProvider } from "@/integrations/sonner/provider"
|
||||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
import { getUser } from "@/lib/db/user"
|
||||||
|
|
||||||
interface MyRouterContext {
|
interface MyRouterContext {
|
||||||
queryClient: QueryClient
|
queryClient: QueryClient
|
||||||
user: null
|
user: null
|
||||||
}
|
}
|
||||||
const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
|
|
||||||
const supabase = getSupabaseServerClient()
|
|
||||||
const { data, error: _error } = await supabase.auth.getUser()
|
|
||||||
|
|
||||||
if (!data.user?.email) {
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
email: data.user.email
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
export const Route = createRootRouteWithContext<MyRouterContext>()({
|
||||||
beforeLoad: async () => {
|
beforeLoad: async () => {
|
||||||
const user = await fetchUser()
|
const user = await getUser()
|
||||||
return {
|
return {
|
||||||
user
|
user
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/_authed")({
|
export const Route = createFileRoute("/_authed")({
|
||||||
beforeLoad: ({ context }) => {
|
beforeLoad: ({ context }) => {
|
||||||
console.log("contextw", context)
|
console.log("contextw", context)
|
||||||
@ -10,9 +9,11 @@ export const Route = createFileRoute("/_authed")({
|
|||||||
},
|
},
|
||||||
errorComponent: ({ error }) => {
|
errorComponent: ({ error }) => {
|
||||||
if (error.message === "Not authenticated") {
|
if (error.message === "Not authenticated") {
|
||||||
;<p>
|
return (
|
||||||
|
<p>
|
||||||
Not authenticated. Please <a href="/login">login</a>.
|
Not authenticated. Please <a href="/login">login</a>.
|
||||||
</p>
|
</p>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
throw error
|
throw error
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
import logo from "@assets/logo.svg"
|
import logo from "@assets/logo.svg"
|
||||||
import { Button } from "@heroui/react"
|
import { Button } from "@heroui/react"
|
||||||
import { createFileRoute, getRouteApi } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
|
|
||||||
export const Route = createFileRoute("/")({
|
export const Route = createFileRoute("/")({
|
||||||
component: App
|
component: App
|
||||||
})
|
})
|
||||||
const apiRouter = getRouteApi("/")
|
|
||||||
function App() {
|
function App() {
|
||||||
const navigate = apiRouter.useNavigate()
|
const navigate = Route.useNavigate()
|
||||||
return (
|
return (
|
||||||
<div className="text-center">
|
<div className="text-center">
|
||||||
<header className="min-h-screen flex flex-col items-center justify-center bg-[#282c34] text-white text-[calc(10px+2vmin)]">
|
<header className="min-h-screen flex flex-col items-center justify-center bg-[#282c34] text-white text-[calc(10px+2vmin)]">
|
||||||
|
|||||||
@ -1,42 +1,37 @@
|
|||||||
import { Button, Form, Input } from "@heroui/react"
|
import { Button, Form, Input } from "@heroui/react"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { useValidation } from "@/lib/hooks/useValidation"
|
import type { FormEvent } from "react"
|
||||||
import { mutationLogin } from "@/lib/mutations/mutationLogin"
|
import { useLogin } from "@/lib/hooks/user/useLogin"
|
||||||
import { loginSchema } from "@/lib/schemas/login"
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/login")({
|
export const Route = createFileRoute("/login")({
|
||||||
component: LoginComp
|
component: LoginComp
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function LoginComp() {
|
function LoginComp() {
|
||||||
const loginMutation = mutationLogin()
|
const { errors, isPending, login } = useLogin()
|
||||||
const { errors, validate } = useValidation({
|
|
||||||
defaultSchema: loginSchema
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const formData = new FormData(e.currentTarget)
|
||||||
|
|
||||||
|
login({
|
||||||
|
email: formData.get("email") as string,
|
||||||
|
password: formData.get("password") as string
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center items-center flex-col h-screen">
|
<div className="flex justify-center items-center flex-col h-screen">
|
||||||
<p className="font-semibold mb-3">Login</p>
|
<p className="font-semibold mb-3">Login</p>
|
||||||
<Form
|
<Form
|
||||||
validationErrors={errors}
|
validationErrors={errors}
|
||||||
className="grid gap-2 max-w-sm w-full"
|
className="grid gap-2 max-w-sm w-full"
|
||||||
onSubmit={(e) => {
|
onSubmit={handleSubmit}
|
||||||
e.preventDefault()
|
|
||||||
const formData = new FormData(e.currentTarget)
|
|
||||||
const email = formData.get("email") as string
|
|
||||||
const password = formData.get("password") as string
|
|
||||||
if (
|
|
||||||
validate({
|
|
||||||
formData: { email, password }
|
|
||||||
})
|
|
||||||
)
|
|
||||||
loginMutation.mutate({ email, password })
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Input name="email" label="Email" />
|
<Input name="email" label="Email" />
|
||||||
<Input name="password" type="password" label="Password" />
|
<Input name="password" type="password" label="Password" />
|
||||||
<Button type="submit" isLoading={loginMutation.isPending}>
|
<Button type="submit" isLoading={isPending}>
|
||||||
Entrar
|
Entrar
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
@ -1,26 +1,7 @@
|
|||||||
import { createFileRoute, redirect } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { createServerFn } from "@tanstack/react-start"
|
import { logoutUser } from "@/lib/db/user"
|
||||||
import { toast } from "sonner"
|
|
||||||
import { getSupabaseServerClient } from "@/integrations/supabase/supabase"
|
|
||||||
|
|
||||||
const logoutFn = createServerFn().handler(async () => {
|
|
||||||
const supabase = getSupabaseServerClient()
|
|
||||||
const { error } = await supabase.auth.signOut()
|
|
||||||
|
|
||||||
if (error) {
|
|
||||||
toast.error("Logout failed. Please try again.")
|
|
||||||
return {
|
|
||||||
error: true,
|
|
||||||
message: error.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw redirect({
|
|
||||||
href: "/"
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/logout")({
|
export const Route = createFileRoute("/logout")({
|
||||||
preload: false,
|
preload: false,
|
||||||
loader: () => logoutFn()
|
loader: () => logoutUser()
|
||||||
})
|
})
|
||||||
|
|||||||
@ -1,43 +1,37 @@
|
|||||||
import { Button, Form, Input } from "@heroui/react"
|
import { Button, Form, Input } from "@heroui/react"
|
||||||
import { createFileRoute } from "@tanstack/react-router"
|
import { createFileRoute } from "@tanstack/react-router"
|
||||||
import { useValidation } from "@/lib/hooks/useValidation"
|
import type { FormEvent } from "react"
|
||||||
import { mutationSignup } from "@/lib/mutations/mutationSignup"
|
import { useSignup } from "@/lib/hooks/user/useSignup"
|
||||||
import { signupSchema } from "@/lib/schemas/signup"
|
|
||||||
|
|
||||||
export const Route = createFileRoute("/signup")({
|
export const Route = createFileRoute("/signup")({
|
||||||
component: SignupComp
|
component: SignupComp
|
||||||
})
|
})
|
||||||
|
|
||||||
function SignupComp() {
|
function SignupComp() {
|
||||||
const signupMutation = mutationSignup()
|
const { signup, errors, isPending } = useSignup()
|
||||||
const { errors, validate } = useValidation({
|
|
||||||
defaultSchema: signupSchema
|
const handleSubmit = (e: FormEvent<HTMLFormElement>) => {
|
||||||
|
e.preventDefault()
|
||||||
|
|
||||||
|
const formData = new FormData(e.currentTarget)
|
||||||
|
|
||||||
|
signup({
|
||||||
|
email: formData.get("email") as string,
|
||||||
|
password: formData.get("password") as string
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="flex justify-center items-center flex-col h-screen">
|
<div className="flex justify-center items-center flex-col h-screen">
|
||||||
<p className="font-semibold mb-3">Signup</p>
|
<p className="font-semibold mb-3">Signup</p>
|
||||||
<Form
|
<Form
|
||||||
validationErrors={errors}
|
validationErrors={errors}
|
||||||
className="grid gap-2 max-w-sm w-full"
|
className="grid gap-2 max-w-sm w-full"
|
||||||
onSubmit={(e) => {
|
onSubmit={handleSubmit}
|
||||||
e.preventDefault()
|
|
||||||
const formData = new FormData(e.currentTarget)
|
|
||||||
const email = formData.get("email") as string
|
|
||||||
const password = formData.get("password") as string
|
|
||||||
if (
|
|
||||||
validate({
|
|
||||||
formData: { email, password }
|
|
||||||
})
|
|
||||||
)
|
|
||||||
signupMutation.mutate({
|
|
||||||
email,
|
|
||||||
password
|
|
||||||
})
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<Input name="email" type="email" label="Email" />
|
<Input name="email" type="email" label="Email" />
|
||||||
<Input name="password" type="password" label="Password" />
|
<Input name="password" type="password" label="Password" />
|
||||||
<Button type="submit" isLoading={signupMutation.isPending}>
|
<Button type="submit" isLoading={isPending}>
|
||||||
Enviar
|
Enviar
|
||||||
</Button>
|
</Button>
|
||||||
</Form>
|
</Form>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user