add: auth functionality basics
This commit is contained in:
+31
-15
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
import React, { useState, type FormEvent, type ChangeEvent, useEffect } from 'react'
|
import React, { useState, type FormEvent, type ChangeEvent, useEffect } from 'react'
|
||||||
import { useAuthStore } from '@/zustand/userStore';
|
import { useAuthStore } from '@/zustand/userStore';
|
||||||
import { loginUser } from '@/features/auth/actions/loginUser';
|
|
||||||
import { redirect } from 'next/navigation';
|
import { redirect } from 'next/navigation';
|
||||||
import StepEnterPassword from '@/features/auth/components/StepEnterPassword';
|
import StepEnterPassword from '@/features/auth/components/StepEnterPassword';
|
||||||
import StepEnterNumber from '@/features/auth/components/StepEnterNumber';
|
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 { AUTH_PAGE_ELEMENT, AUTH_STEP } from '@/enums';
|
||||||
import { useCountdown } from '@/hooks/useCountdown';
|
import { useCountdown } from '@/hooks/useCountdown';
|
||||||
import StepNewPassword from '@/features/auth/components/StepNewPassword';
|
import StepNewPassword from '@/features/auth/components/StepNewPassword';
|
||||||
import { validatePhoneNumber } from '@/features/auth/actions/validatePhoneNumber';
|
import { useLogin } from '@/hooks/auth/useLogin';
|
||||||
import { signupUser } from '@/features/auth/actions/signupUser';
|
import { useSignup } from '@/hooks/auth/useSignup';
|
||||||
|
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists';
|
||||||
|
|
||||||
type Props = object
|
type Props = object
|
||||||
|
|
||||||
@@ -27,6 +27,11 @@ function AuthIndex({ }: Props) {
|
|||||||
const { timerRunning, secondsLeft, restart, stop } = useCountdown(step === AUTH_STEP.ENTER_OTP);
|
const { timerRunning, secondsLeft, restart, stop } = useCountdown(step === AUTH_STEP.ENTER_OTP);
|
||||||
|
|
||||||
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
const isAuthenticated = useAuthStore((state) => state.isAuthenticated)
|
||||||
|
const loginMutation = useLogin()
|
||||||
|
const signupMutation = useSignup()
|
||||||
|
const checkUserExistsMuation = useCheckUserExistsLazy()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isAuthenticated) {
|
if (isAuthenticated) {
|
||||||
@@ -34,6 +39,12 @@ function AuthIndex({ }: Props) {
|
|||||||
}
|
}
|
||||||
}, [isAuthenticated])
|
}, [isAuthenticated])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (step === AUTH_STEP.ENTER_OTP) {
|
||||||
|
console.log("REQUEST OTP")
|
||||||
|
}
|
||||||
|
}, [step]);
|
||||||
|
|
||||||
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
|
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
|
||||||
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
|
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
|
||||||
setNumber(() => e.target.value)
|
setNumber(() => e.target.value)
|
||||||
@@ -79,38 +90,43 @@ function AuthIndex({ }: Props) {
|
|||||||
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
if (step == AUTH_STEP.ENTER_NUMBER) {
|
if (step == AUTH_STEP.ENTER_NUMBER) {
|
||||||
if (await validatePhoneNumber(number)) {
|
|
||||||
|
if (await checkUserExistsMuation.run(number)) {
|
||||||
setStep(AUTH_STEP.ENTER_PASSWORD)
|
setStep(AUTH_STEP.ENTER_PASSWORD)
|
||||||
} else {
|
} else {
|
||||||
setStep(AUTH_STEP.ENTER_OTP);
|
setStep(AUTH_STEP.ENTER_OTP);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (step == AUTH_STEP.ENTER_PASSWORD) {
|
else if (step == AUTH_STEP.ENTER_PASSWORD) {
|
||||||
if (await loginUser(number, password)) {
|
try {
|
||||||
|
await loginMutation.mutateAsync({ phone: number, password })
|
||||||
console.log("Logged in")
|
console.log("Logged in")
|
||||||
}
|
}
|
||||||
else {
|
catch (e) {
|
||||||
console.log("Wrong credentials")
|
console.log("Wrong credentials: ", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (step == AUTH_STEP.ENTER_OTP) {
|
else if (step == AUTH_STEP.ENTER_OTP) {
|
||||||
stop();
|
stop();
|
||||||
if (await validatePhoneNumber(number)) {
|
setStep(AUTH_STEP.ENTER_NEW_PASSWORD)
|
||||||
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
|
// if (await validatePhoneNumber(number)) {
|
||||||
} else {
|
// setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
|
||||||
setStep(AUTH_STEP.ENTER_NEW_PASSWORD)
|
// } else {
|
||||||
}
|
// setStep(AUTH_STEP.ENTER_NEW_PASSWORD)
|
||||||
|
// }
|
||||||
}
|
}
|
||||||
else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
|
else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
|
||||||
console.log("Password changed")
|
console.log("Password changed")
|
||||||
setStep(AUTH_STEP.ENTER_NUMBER)
|
setStep(AUTH_STEP.ENTER_NUMBER)
|
||||||
}
|
}
|
||||||
else if (step == AUTH_STEP.ENTER_NEW_PASSWORD) {
|
else if (step == AUTH_STEP.ENTER_NEW_PASSWORD) {
|
||||||
if (await signupUser(number, password)) {
|
try {
|
||||||
|
await signupMutation.mutateAsync({ phone: number, password })
|
||||||
console.log("Signed up")
|
console.log("Signed up")
|
||||||
redirect("/")
|
redirect("/")
|
||||||
} else {
|
}
|
||||||
console.log("Could not signup")
|
catch (e) {
|
||||||
|
console.log("Could not signup: ", e)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+8
-1
@@ -1,11 +1,18 @@
|
|||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useAuthStore } from "@/zustand/userStore";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
|
|
||||||
|
const logout = useAuthStore((state) => state.logout);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="grid grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
<div className="grid font-sans grid-rows-[20px_1fr_20px] items-center justify-items-center min-h-screen p-8 pb-20 gap-16 sm:p-20 font-[family-name:var(--font-geist-sans)]">
|
||||||
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
<main className="flex flex-col gap-[32px] row-start-2 items-center sm:items-start">
|
||||||
<Link href={"/auth"}>Login</Link>
|
<Link href={"/auth"}>Login</Link>
|
||||||
|
<Link href={"/"} onClick={logout}>Logout</Link>
|
||||||
<Image
|
<Image
|
||||||
className="dark:invert"
|
className="dark:invert"
|
||||||
src="/next.svg"
|
src="/next.svg"
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
import { useAuthStore } from '@/zustand/userStore'
|
|
||||||
import { login } from '@/lib/api/auth'
|
|
||||||
|
|
||||||
export async function loginUser(phoneNumber: string, password: string) {
|
|
||||||
if (phoneNumber == "123" && password == "123") {
|
|
||||||
let response = login({
|
|
||||||
phoneNumber,
|
|
||||||
password,
|
|
||||||
})
|
|
||||||
|
|
||||||
{
|
|
||||||
const { login } = useAuthStore.getState()
|
|
||||||
login(response.user)
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
import { useAuthStore } from '@/zustand/userStore'
|
|
||||||
import { signup } from '@/lib/api/auth'
|
|
||||||
|
|
||||||
export async function signupUser(phoneNumber: string, password: string) {
|
|
||||||
if (phoneNumber == "1234" && password == "1234") {
|
|
||||||
let response = signup({
|
|
||||||
phoneNumber,
|
|
||||||
password,
|
|
||||||
})
|
|
||||||
|
|
||||||
{
|
|
||||||
const { login } = useAuthStore.getState()
|
|
||||||
login(response.user)
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
import { checkUserExists } from '@/lib/api/auth'
|
|
||||||
|
|
||||||
export async function validatePhoneNumber(phoneNumber: string): Promise<boolean> {
|
|
||||||
try {
|
|
||||||
return await checkUserExists(phoneNumber)
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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 }
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
},
|
||||||
|
})
|
||||||
@@ -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)
|
||||||
|
},
|
||||||
|
})
|
||||||
+18
-34
@@ -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<boolean> {
|
export const login = async ({ phone, password }: { phone: string; password: string }) => {
|
||||||
const res = phoneNumber === "123";
|
const res = await api.post('/login', { phone, password })
|
||||||
const data = { status: 200, result: { exists: res } }
|
console.log(res);
|
||||||
return data.result.exists
|
return res.data // { user: { id, name, email, token } }
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function signup(userData: {
|
export const signup = async ({
|
||||||
phoneNumber: string,
|
phone,
|
||||||
|
password,
|
||||||
|
}: {
|
||||||
|
phone: string
|
||||||
password: string
|
password: string
|
||||||
}) {
|
}) => {
|
||||||
const res = userData.phoneNumber === "123" && userData.password === "123";
|
const res = await api.post('/signup', { phone, password })
|
||||||
const data = { sucess: res }
|
console.log(res);
|
||||||
return {
|
return res.data
|
||||||
data,
|
|
||||||
user: {
|
|
||||||
id: '123',
|
|
||||||
name: 'John Doe',
|
|
||||||
number: userData.phoneNumber,
|
|
||||||
token: 'mock-jwt-token',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function login(credentials: {
|
export const checkUserExists = async (phone: string) => {
|
||||||
phoneNumber: string,
|
const res = await api.get(`/exists/${encodeURIComponent(phone)}`)
|
||||||
password: string
|
console.log(res);
|
||||||
}) {
|
return res.data.exists as boolean
|
||||||
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',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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' },
|
||||||
|
})
|
||||||
Reference in New Issue
Block a user