From ef6370611611179f41b36c59ba7aecc75742751b Mon Sep 17 00:00:00 2001 From: Mahyar Khanbolooki Date: Tue, 1 Jul 2025 19:30:54 +0330 Subject: [PATCH] add: auth functionality basics --- src/app/auth/page.tsx | 46 ++++++++++------ src/app/page.tsx | 9 +++- src/features/auth/actions/loginUser.ts | 19 ------- src/features/auth/actions/signupUser.ts | 19 ------- .../auth/actions/validatePhoneNumber.ts | 10 ---- src/hooks/auth/useCheckUserExists.ts | 14 +++++ src/hooks/auth/useLogin.ts | 11 ++++ src/hooks/auth/useSignup.ts | 11 ++++ src/lib/api/auth.ts | 52 +++++++------------ src/lib/api/axiosInstance.ts | 6 +++ 10 files changed, 99 insertions(+), 98 deletions(-) delete mode 100644 src/features/auth/actions/loginUser.ts delete mode 100644 src/features/auth/actions/signupUser.ts delete mode 100644 src/features/auth/actions/validatePhoneNumber.ts create mode 100644 src/hooks/auth/useCheckUserExists.ts create mode 100644 src/hooks/auth/useLogin.ts create mode 100644 src/hooks/auth/useSignup.ts create mode 100644 src/lib/api/axiosInstance.ts diff --git a/src/app/auth/page.tsx b/src/app/auth/page.tsx index 16ae303..adebac2 100644 --- a/src/app/auth/page.tsx +++ b/src/app/auth/page.tsx @@ -2,7 +2,6 @@ import React, { useState, type FormEvent, type ChangeEvent, useEffect } from 'react' import { useAuthStore } from '@/zustand/userStore'; -import { loginUser } from '@/features/auth/actions/loginUser'; import { redirect } from 'next/navigation'; import StepEnterPassword from '@/features/auth/components/StepEnterPassword'; import StepEnterNumber from '@/features/auth/components/StepEnterNumber'; @@ -10,8 +9,9 @@ import StepEnterOtp from '@/features/auth/components/StepEnterOtp'; import { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums'; import { useCountdown } from '@/hooks/useCountdown'; import StepNewPassword from '@/features/auth/components/StepNewPassword'; -import { validatePhoneNumber } from '@/features/auth/actions/validatePhoneNumber'; -import { signupUser } from '@/features/auth/actions/signupUser'; +import { useLogin } from '@/hooks/auth/useLogin'; +import { useSignup } from '@/hooks/auth/useSignup'; +import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'; type Props = object @@ -27,6 +27,11 @@ function AuthIndex({ }: Props) { const { timerRunning, secondsLeft, restart, stop } = useCountdown(step === AUTH_STEP.ENTER_OTP); const isAuthenticated = useAuthStore((state) => state.isAuthenticated) + const loginMutation = useLogin() + const signupMutation = useSignup() + const checkUserExistsMuation = useCheckUserExistsLazy() + + useEffect(() => { if (isAuthenticated) { @@ -34,6 +39,12 @@ function AuthIndex({ }: Props) { } }, [isAuthenticated]) + useEffect(() => { + if (step === AUTH_STEP.ENTER_OTP) { + console.log("REQUEST OTP") + } + }, [step]); + const onChange = (e: ChangeEvent) => { if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) { setNumber(() => e.target.value) @@ -79,38 +90,43 @@ function AuthIndex({ }: Props) { const onSubmit = async (e: FormEvent) => { e.preventDefault(); if (step == AUTH_STEP.ENTER_NUMBER) { - if (await validatePhoneNumber(number)) { + + if (await checkUserExistsMuation.run(number)) { setStep(AUTH_STEP.ENTER_PASSWORD) } else { setStep(AUTH_STEP.ENTER_OTP); } } else if (step == AUTH_STEP.ENTER_PASSWORD) { - if (await loginUser(number, password)) { + try { + await loginMutation.mutateAsync({ phone: number, password }) console.log("Logged in") } - else { - console.log("Wrong credentials") + catch (e) { + console.log("Wrong credentials: ", e) } } else if (step == AUTH_STEP.ENTER_OTP) { stop(); - if (await validatePhoneNumber(number)) { - setStep(AUTH_STEP.ENTER_RESET_PASSWORD) - } else { - setStep(AUTH_STEP.ENTER_NEW_PASSWORD) - } + setStep(AUTH_STEP.ENTER_NEW_PASSWORD) + // if (await validatePhoneNumber(number)) { + // setStep(AUTH_STEP.ENTER_RESET_PASSWORD) + // } else { + // setStep(AUTH_STEP.ENTER_NEW_PASSWORD) + // } } else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) { console.log("Password changed") setStep(AUTH_STEP.ENTER_NUMBER) } else if (step == AUTH_STEP.ENTER_NEW_PASSWORD) { - if (await signupUser(number, password)) { + try { + await signupMutation.mutateAsync({ phone: number, password }) console.log("Signed up") redirect("/") - } else { - console.log("Could not signup") + } + catch (e) { + console.log("Could not signup: ", e) } } } diff --git a/src/app/page.tsx b/src/app/page.tsx index dafeab3..c02c769 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -1,11 +1,18 @@ +'use client'; + +import { useAuthStore } from "@/zustand/userStore"; import Image from "next/image"; import Link from "next/link"; export default function Home() { + + const logout = useAuthStore((state) => state.logout); + return ( -
+
Login + Logout { - try { - return await checkUserExists(phoneNumber) - } catch (e) { - console.error(e) - return false - } -} diff --git a/src/hooks/auth/useCheckUserExists.ts b/src/hooks/auth/useCheckUserExists.ts new file mode 100644 index 0000000..a33455d --- /dev/null +++ b/src/hooks/auth/useCheckUserExists.ts @@ -0,0 +1,14 @@ +import { checkUserExists } from "@/lib/api/auth" +import { useQueryClient } from "@tanstack/react-query" + +export const useCheckUserExistsLazy = () => { + const queryClient = useQueryClient() + + const run = (phone: string) => + queryClient.fetchQuery({ + queryKey: ['checkUserExists', phone], + queryFn: () => checkUserExists(phone), + }) + + return { run } +} diff --git a/src/hooks/auth/useLogin.ts b/src/hooks/auth/useLogin.ts new file mode 100644 index 0000000..ac904f2 --- /dev/null +++ b/src/hooks/auth/useLogin.ts @@ -0,0 +1,11 @@ +import { useMutation } from '@tanstack/react-query' +import { login } from '@/lib/api/auth' +import { useAuthStore } from '@/zustand/userStore' + +export const useLogin = () => + useMutation({ + mutationFn: login, + onSuccess: (data) => { + useAuthStore.getState().login(data.user) + }, + }) diff --git a/src/hooks/auth/useSignup.ts b/src/hooks/auth/useSignup.ts new file mode 100644 index 0000000..eb1c76d --- /dev/null +++ b/src/hooks/auth/useSignup.ts @@ -0,0 +1,11 @@ +import { useMutation } from '@tanstack/react-query' +import { signup } from '@/lib/api/auth' +import { useAuthStore } from '@/zustand/userStore' + +export const useSignup = () => + useMutation({ + mutationFn: signup, + onSuccess: (data) => { + useAuthStore.getState().login(data.user) + }, + }) diff --git a/src/lib/api/auth.ts b/src/lib/api/auth.ts index 8db1732..9f79b47 100644 --- a/src/lib/api/auth.ts +++ b/src/lib/api/auth.ts @@ -1,41 +1,25 @@ -const BASE_URL = process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com' +import { api } from './axiosInstance' -export async function checkUserExists(phoneNumber: string): Promise { - const res = phoneNumber === "123"; - const data = { status: 200, result: { exists: res } } - return data.result.exists +export const login = async ({ phone, password }: { phone: string; password: string }) => { + const res = await api.post('/login', { phone, password }) + console.log(res); + return res.data // { user: { id, name, email, token } } } -export async function signup(userData: { - phoneNumber: string, +export const signup = async ({ + phone, + password, +}: { + phone: string password: string -}) { - const res = userData.phoneNumber === "123" && userData.password === "123"; - const data = { sucess: res } - return { - data, - user: { - id: '123', - name: 'John Doe', - number: userData.phoneNumber, - token: 'mock-jwt-token', - } - } +}) => { + const res = await api.post('/signup', { phone, password }) + console.log(res); + return res.data } -export async function login(credentials: { - phoneNumber: string, - password: string -}) { - const res = credentials.phoneNumber === "1234" && credentials.password === "1234"; - const data = { sucess: res } - return { - data, - user: { - id: '1234', - name: 'Foo bar', - number: credentials.phoneNumber, - token: 'mock-jwt-token', - } - } +export const checkUserExists = async (phone: string) => { + const res = await api.get(`/exists/${encodeURIComponent(phone)}`) + console.log(res); + return res.data.exists as boolean } diff --git a/src/lib/api/axiosInstance.ts b/src/lib/api/axiosInstance.ts new file mode 100644 index 0000000..7c0830e --- /dev/null +++ b/src/lib/api/axiosInstance.ts @@ -0,0 +1,6 @@ +import axios from 'axios' + +export const api = axios.create({ + baseURL: process.env.NEXT_PUBLIC_API_URL || 'https://api.example.com', + headers: { 'Content-Type': 'application/json' }, +})