copy base dmenu to dkala

This commit is contained in:
hamid zarghami
2026-02-07 15:31:22 +03:30
commit c9e37f6177
521 changed files with 57786 additions and 0 deletions
BIN
View File
Binary file not shown.
+13
View File
@@ -0,0 +1,13 @@
import { Metadata } from 'next'
export const metadata: Metadata = {
title: "Authentication"
}
export default function AuthLayout({ children }: { children: React.ReactNode }) {
return (
<main className="h-full">
{children}
</main>
)
}
+14
View File
@@ -0,0 +1,14 @@
import { useMutation } from "@tanstack/react-query";
import * as api from "../service/LoginService";
export const useOtpRequest = () => {
return useMutation({
mutationFn: api.loginOTP,
});
};
export const useOtpVerify = () => {
return useMutation({
mutationFn: api.loginVerifyOTP,
});
};
+25
View File
@@ -0,0 +1,25 @@
'use client'
import React from 'react'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
type Props = object
function AuthIndex({ }: Props) {
return (
<AuthFormWrapper isPending={false} onSubmit={() => undefined}>
<StepEnterNumber />
{/* <StepEnterPassword
pending={false}
onChange={() => undefined}
onClick={() => undefined}
value=''
passwordVisible={false}
rememberMe={false}
/> */}
</AuthFormWrapper>
)
}
export default AuthIndex
@@ -0,0 +1,12 @@
import { api } from "@/lib/api/axiosInstance";
import { LoginOTPRequestType, LoginVerifyOTPType } from "../types/Types";
export const loginOTP = async (params: LoginOTPRequestType) => {
const { data } = await api.post("/public/auth/otp/request", params);
return data;
};
export const loginVerifyOTP = async (params: LoginVerifyOTPType) => {
const { data } = await api.post("/public/auth/otp/verify", params);
return data;
};
+10
View File
@@ -0,0 +1,10 @@
export type LoginOTPRequestType = {
phone: string;
slug: string;
};
export type LoginVerifyOTPType = {
phone: string;
otp: string;
slug: string;
};
+93
View File
@@ -0,0 +1,93 @@
'use client'
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { redirect, useRouter } from 'next/navigation'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
import { AUTH_PAGE_ELEMENT } from '@/enums'
import AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const [number, setNumber] = useState(user_temp_store?.number ?? '')
const [isPending, startTransition] = useTransition()
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const router = useRouter()
useEffect(() => {
if (isAuthenticated) {
redirect('/')
}
}, [isAuthenticated])
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
}
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* EMD OF DEV SECTION */
/* SECTION DETAILS */
/* If user is already registered, go to login page */
/* Otherwise go to signup page */
/* We store auth data in a temp auth store so the next page must */
/* access the phone number entered in this page */
/* and if it doesnt, it will ask for the number again */
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
router.push('auth/login')
} else {
router.push('auth/signup')
}
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
update_temp_store({
number: number,
mustLogin: !number.endsWith('0')
})
if (number.endsWith('0')) {
router.push('auth/signup')
} else {
router.push('auth/login')
}
/* END OF DEV SECTION */
}
})
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
<StepEnterNumber pending={isPending} onChange={onChange} value={number} />
</AuthFormWrapper>
)
}
export default AuthIndex
+255
View File
@@ -0,0 +1,255 @@
'use client'
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { useParams, useRouter } from 'next/navigation'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
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 AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useOtpRequest } from '@/hooks/auth/useOtpRequest'
import { useOtpValidation } from '@/hooks/auth/useOtpValidation'
import { useResetPassword } from '@/hooks/auth/useResetPassword'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const user_auth_store = useAuthStore(state => state.user)
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const [number, setNumber] = useState(
user_temp_store?.number ?? user_auth_store?.number ?? ''
)
const [password, setPassword] = useState('')
const [passwordRepeat, setPasswordRepeat] = useState('')
const [otp, setOtp] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [showPasswordRepeat, setShowPasswordRepeat] = useState(false)
const [step, setStep] = useState(
number == '' ? AUTH_STEP.ENTER_NUMBER : AUTH_STEP.ENTER_OTP
)
const { timerRunning, secondsLeft, restart, stop } = useCountdown(
step === AUTH_STEP.ENTER_OTP
)
const [isPending, startTransition] = useTransition()
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const { run: runOtpRequest } = useOtpRequest()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { run: runOtpValidation } = useOtpValidation()
const { run: runResetPassword } = useResetPassword()
const router = useRouter()
const { name } = useParams()
const basePath = `/${name ?? ''}`
useEffect(() => {
if (step === AUTH_STEP.ENTER_OTP) {
startTransition(() => {
setOtp('')
console.log('REQUEST OTP')
runOtpRequest(number)
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step])
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) {
setPassword(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD_REPEAT) {
setPasswordRepeat(() => e.target.value)
} else if (e.target.name.startsWith(AUTH_PAGE_ELEMENT.INPUT_OTP)) {
const index = +e.target.name.split('-')[2]
const prev = otp.padEnd(6).split('')
prev[index] = e.target.value
const next = prev.join('')
if (otp.length >= 6 && next.trim().length >= otp.length) return
setOtp(next.trimEnd())
}
}
const onClick = async (
e:
| React.MouseEvent<HTMLButtonElement, MouseEvent>
| React.MouseEvent<HTMLInputElement, MouseEvent>
) => {
e.stopPropagation()
const target = e.target as HTMLButtonElement
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) {
setShowPassword(state => !state)
}
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_REPEAT_PASSWORD) {
setShowPasswordRepeat(state => !state)
} else if (target.id === AUTH_PAGE_ELEMENT.RESET_PASSWORD) {
setShowPassword(false)
setShowPasswordRepeat(false)
setPassword('')
setPasswordRepeat('')
setStep(() => AUTH_STEP.ENTER_OTP)
} else if (target.id === AUTH_PAGE_ELEMENT.RESEND_OTP) {
if (!timerRunning) {
startTransition(async () => {
try {
await runOtpRequest(number)
restart()
} catch {
console.error('Could not ask for otp')
}
})
}
}
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* END OF DEV SECTION */
/* SECTION DETAILS */
/* if number is entered in the /auth page or is available in main auth store this page directly shows the OTP page */
/* Otherwise it shows the number page */
if (step == AUTH_STEP.ENTER_NUMBER) {
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else {
// TODO: error user not found
}
} else if (step == AUTH_STEP.ENTER_OTP) {
// if (!await runOtpValidation({ phone: number, otp })) {
// setOtp('');
// console.error('Wrong otp');
// }
stop()
console.log(await runUserExistCheck(number))
if (await runUserExistCheck(number)) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else {
// TODO: error user not found
}
} else if (
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_CURRENT_PASSWORD
) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
update_temp_store({
password,
repeatPassword: passwordRepeat
})
try {
await runResetPassword({
phone: number,
newPassword: password,
otp: otp
})
console.log('Password changed')
if (isAuthenticated) {
router.replace(basePath)
} else {
router.replace(`${basePath}/auth`)
}
} catch (e) {
console.error('Could not reset password: ', e)
}
}
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
if (step == AUTH_STEP.ENTER_NUMBER) {
update_temp_store({
number
})
setStep(AUTH_STEP.ENTER_OTP)
} else if (step === AUTH_STEP.ENTER_OTP) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else if (
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_CURRENT_PASSWORD
) {
setStep(AUTH_STEP.ENTER_RESET_PASSWORD)
} else if (step == AUTH_STEP.ENTER_RESET_PASSWORD) {
update_temp_store({
password,
repeatPassword: passwordRepeat
})
try {
console.log('Password changed')
if (isAuthenticated) {
router.replace(basePath)
} else {
router.replace(`${basePath}/auth`)
}
} catch (e) {
console.error('Could not reset password: ', e)
}
}
/* END OF DEV SECTION */
}
})
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
{step === AUTH_STEP.ENTER_NUMBER && (
<StepEnterNumber
pending={isPending}
onChange={onChange}
value={number}
/>
)}
{step === AUTH_STEP.ENTER_OTP && (
<StepEnterOtp
pending={isPending}
onChange={onChange}
onClick={onClick}
phoneNumber={number}
value={otp}
timerRunning={timerRunning}
secondsLeft={secondsLeft}
/>
)}
{step === AUTH_STEP.ENTER_RESET_PASSWORD && (
<StepNewPassword
isReset
pending={isPending}
onChange={onChange}
onClick={onClick}
value={password}
repeatValue={passwordRepeat}
passwordVisible={showPassword}
repeatPasswordVisible={showPasswordRepeat}
/>
)}
</AuthFormWrapper>
)
}
export default AuthIndex
+242
View File
@@ -0,0 +1,242 @@
'use client'
import React, {
useState,
type FormEvent,
type ChangeEvent,
useEffect,
useTransition
} from 'react'
import { useAuthStore } from '@/zustand/authStore'
import { redirect, useParams, useRouter } from 'next/navigation'
import StepEnterNumber from '@/features/auth/components/StepEnterNumber'
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 AuthFormWrapper from '@/features/auth/components/AuthFormWrapper'
import { useSignup } from '@/hooks/auth/useSignup'
import { useCheckUserExistsLazy } from '@/hooks/auth/useCheckUserExists'
import { useOtpRequest } from '@/hooks/auth/useOtpRequest'
import { useOtpValidation } from '@/hooks/auth/useOtpValidation'
import { useAuthTempStore } from '@/zustand/authTempStroe'
type Props = object
function AuthIndex ({}: Props) {
const update_temp_store = useAuthTempStore(state => state.update)
const user_temp_store = useAuthTempStore(state => state.user)
const [number, setNumber] = useState(user_temp_store?.number ?? '')
const [password, setPassword] = useState('')
const [passwordRepeat, setPasswordRepeat] = useState('')
const [otp, setOtp] = useState('')
const [showPassword, setShowPassword] = useState(false)
const [showPasswordRepeat, setShowPasswordRepeat] = useState(false)
const [step, setStep] = useState(
number == '' ? AUTH_STEP.ENTER_NUMBER : AUTH_STEP.ENTER_OTP
)
const { timerRunning, secondsLeft, restart, stop } = useCountdown(
step === AUTH_STEP.ENTER_OTP
)
const [isPending, startTransition] = useTransition()
const isAuthenticated = useAuthStore(state => state.isAuthenticated)
const signupMutation = useSignup()
const { run: runUserExistCheck } = useCheckUserExistsLazy()
const { run: runOtpRequest } = useOtpRequest()
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { run: runOtpValidation } = useOtpValidation()
const router = useRouter()
const { name } = useParams()
const basePath = `/${name ?? ''}`
useEffect(() => {
if (isAuthenticated) {
redirect(basePath)
}
}, [isAuthenticated, basePath])
useEffect(() => {
if (step === AUTH_STEP.ENTER_OTP) {
startTransition(() => {
setOtp('')
console.log('REQUEST OTP')
runOtpRequest(number)
})
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [step])
const onChange = (e: ChangeEvent<HTMLInputElement>) => {
if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PHONE) {
setNumber(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD) {
setPassword(() => e.target.value)
} else if (e.target.name == AUTH_PAGE_ELEMENT.INPUT_PASSWORD_REPEAT) {
setPasswordRepeat(() => e.target.value)
} else if (e.target.name.startsWith(AUTH_PAGE_ELEMENT.INPUT_OTP)) {
const index = +e.target.name.split('-')[2]
const prev = otp.padEnd(6).split('')
prev[index] = e.target.value
const next = prev.join('')
if (otp.length >= 6 && next.trim().length >= otp.length) return
setOtp(next.trimEnd())
}
}
const onClick = async (
e:
| React.MouseEvent<HTMLButtonElement, MouseEvent>
| React.MouseEvent<HTMLInputElement, MouseEvent>
) => {
e.stopPropagation()
const target = e.target as HTMLButtonElement
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_PASSWORD) {
setShowPassword(state => !state)
}
if (target.id === AUTH_PAGE_ELEMENT.TOGGLE_REPEAT_PASSWORD) {
setShowPasswordRepeat(state => !state)
} else if (target.id === AUTH_PAGE_ELEMENT.RESET_PASSWORD) {
setShowPassword(false)
setShowPasswordRepeat(false)
setPassword('')
setPasswordRepeat('')
setStep(() => AUTH_STEP.ENTER_OTP)
} else if (target.id === AUTH_PAGE_ELEMENT.RESEND_OTP) {
if (!timerRunning) {
startTransition(async () => {
try {
await runOtpRequest(number)
restart()
} catch {
console.error('Could not ask for otp')
}
})
}
}
}
const onSubmit = async (e: FormEvent<HTMLFormElement>) => {
e.preventDefault()
startTransition(async () => {
try {
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
throw false
/* END OF DEV SECTION */
/* SECTION DETAILS */
/* if number is entered in the /auth page this page directly shows the OTP page */
/* Otherwise it shows the number page */
if (step == AUTH_STEP.ENTER_NUMBER) {
const userExists = await runUserExistCheck(number)
update_temp_store({
number: number,
mustLogin: userExists
})
if (userExists) {
router.replace(`${basePath}/auth/login`)
} else {
setStep(AUTH_STEP.ENTER_OTP)
}
} else if (step == AUTH_STEP.ENTER_OTP) {
// if (!await runOtpValidation({ phone: number, otp })) {
// setOtp('');
// console.error('Wrong otp');
// }
update_temp_store({
otp: otp
})
stop()
setStep(AUTH_STEP.ENTER_CREATE_PASSWORD)
} else if (
step == AUTH_STEP.ENTER_CREATE_PASSWORD ||
step == AUTH_STEP.ENTER_CURRENT_PASSWORD
) {
update_temp_store({
password: password,
repeatPassword: passwordRepeat
})
try {
await signupMutation.mutateAsync({ phone: number, password })
console.log('Signed up')
router.replace(basePath)
} catch (e) {
console.error('Could not signup: ', e)
}
}
} catch (ex) {
console.error(ex)
// TODO: Add toast errors
/* DEV SECTION */
/* REMOVE THIS CODE FOR PRODUCTION */
if (step == AUTH_STEP.ENTER_NUMBER) {
update_temp_store({
number: number,
mustLogin: !number.endsWith('0')
})
setStep(AUTH_STEP.ENTER_OTP)
} else if (step == AUTH_STEP.ENTER_OTP) {
stop()
update_temp_store({
otp: otp
})
setStep(AUTH_STEP.ENTER_CREATE_PASSWORD)
} else if (step == AUTH_STEP.ENTER_CREATE_PASSWORD) {
update_temp_store({
password: password,
repeatPassword: passwordRepeat
})
try {
console.log('Signed up')
router.replace(basePath)
} catch (e) {
console.error('Could not signup: ', e)
}
}
/* END OF DEV SECTION */
}
})
}
return (
<AuthFormWrapper isPending={isPending} onSubmit={onSubmit}>
{step === AUTH_STEP.ENTER_NUMBER && (
<StepEnterNumber
pending={isPending}
onChange={onChange}
value={number}
/>
)}
{step === AUTH_STEP.ENTER_OTP && (
<StepEnterOtp
pending={isPending}
onChange={onChange}
onClick={onClick}
phoneNumber={number}
value={otp}
timerRunning={timerRunning}
secondsLeft={secondsLeft}
/>
)}
{step === AUTH_STEP.ENTER_CREATE_PASSWORD && (
<StepNewPassword
isReset
pending={isPending}
onChange={onChange}
onClick={onClick}
value={password}
repeatValue={passwordRepeat}
passwordVisible={showPassword}
repeatPasswordVisible={showPasswordRepeat}
/>
)}
</AuthFormWrapper>
)
}
export default AuthIndex