complete auth pages sunc with api - complete cities and province combo box in register page

This commit is contained in:
Alihaghighattalab
2024-08-24 16:42:35 +03:30
parent ea510ee0bf
commit 9a63ef4e9e
14 changed files with 404 additions and 102 deletions
+5 -3
View File
@@ -7,12 +7,14 @@ type Props = {
type?: "reset" | "button" | "submit" | undefined,
pending?: boolean,
theme?: "primary" | "secondary" | "glass" | "light",
onClick?: () => void
onClick?: () => void,
disable?: boolean,
className?: string
}
export const ButtonComponent: FC<Props> = ({ title, type = "button", pending = false, theme = "primary", onClick }) => {
export const ButtonComponent: FC<Props> = ({ title, type = "button", pending = false, theme = "primary", onClick, disable = false, className }) => {
return (
<Button type={type} onClick={onClick} className={clsx("flex flex-row gap-3 items-center justify-center", {
<Button type={type} disabled={disable} onClick={onClick} className={clsx(`flex flex-row gap-3 items-center justify-center ${className}`, {
"bg-primary-color text-white": theme === "primary",
"bg-transparent text-primary-color": theme === "glass",
"bg-dark-blue text-white": theme === "secondary",
+1 -5
View File
@@ -25,7 +25,6 @@ export const ComboBox: FC<Props> = ({ field, placeholder, className, data = [],
return (
<Combobox value={field && field.value} onChange={mode === "default" ? field && field.onChange : (e: ComboBoxItems) => {
console.log(e)
updateParams({
rows: e?.id
})
@@ -36,9 +35,6 @@ export const ComboBox: FC<Props> = ({ field, placeholder, className, data = [],
className={`input-style min-w-full ${className}`}
displayValue={(person: any) => person?.name}
onChange={(event) => setQuery(event.target.value)}
ref={field && field.ref}
name={field && field.name}
onBlur={field && field.onBlur}
/>
<ComboboxButton className="absolute top-0 min-w-full h-full bg-transparent">
<ArrowDown2 className="size-5 absolute left-5 top-5 p-0 text-secondary-text-color/70" />
@@ -47,7 +43,7 @@ export const ComboBox: FC<Props> = ({ field, placeholder, className, data = [],
{data?.length > 0 && <ComboboxOptions
anchor="bottom start"
transition
className="bg-white shadow min-w-[220px] mt-2 rounded-xl p-2"
className="bg-white shadow min-w-[220px] !max-h-[300px] mt-2 rounded-xl p-2"
>
{filteredValue.map((value) => (
<ComboboxOption
+30 -6
View File
@@ -1,13 +1,22 @@
import { Link } from "react-router-dom"
import { Link, useNavigate } from "react-router-dom"
import { Input } from "../common/input"
import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { ForgotPasswordInterface } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup"
import { forgotPasswordSchema } from "../../schema"
import { ErrorComponent } from "../common/error"
import { Button } from "@headlessui/react"
import { useMutation } from "@tanstack/react-query"
import { sendOTPMutation } from "../../services/api/auth"
import toast from "react-hot-toast"
import { ButtonComponent } from "../common/button"
export const ForgotPasswordForm = () => {
const navigate = useNavigate()
const { mutate, isPending } = useMutation({
mutationFn: sendOTPMutation,
mutationKey: ["send-otp"]
})
const {
handleSubmit,
formState: { errors },
@@ -15,7 +24,22 @@ export const ForgotPasswordForm = () => {
} = useForm<ForgotPasswordInterface>({
resolver: yupResolver(forgotPasswordSchema)
})
const handleForgotSubmit: SubmitHandler<ForgotPasswordInterface> = async (data) => { console.log("data=>", data) }
const handleForgotSubmit: SubmitHandler<ForgotPasswordInterface> = async (data) => {
mutate({
...data
},
{
onSuccess: () => {
toast.success(`کد ورود برای شماره ${data?.mobile_number} ارسال شد`)
navigate(`/auth/reset-password-code?phone_number=${data?.mobile_number}`)
},
onError: (err: any) => {
console.log("error", err)
navigate(`/auth/reset-password-code?phone_number=${data?.mobile_number}`)
}
}
)
}
return (
<form onSubmit={handleSubmit(handleForgotSubmit)}
@@ -29,15 +53,15 @@ export const ForgotPasswordForm = () => {
</div>
</div>
<div className="mt-9 mb-[42px]">
<Controller control={control} name="phoneNumber" render={({ field }) => (
<Controller control={control} name="mobile_number" render={({ field }) => (
<div>
<Input type="number" placeholder="شماره موبایل" field={field} />
<ErrorComponent show={errors.phoneNumber} message={errors?.phoneNumber?.message} />
<ErrorComponent show={errors.mobile_number} message={errors?.mobile_number?.message} />
</div>
)} />
</div>
<div className="flex flex-col gap-y-[27px] items-center">
<Button type="submit">دریافت کد تایید</Button>
<ButtonComponent title="دریافت کد تایید" type="submit" pending={isPending} />
<Link to="/auth/login">
<p className="font-medium text-base text-primary-color">بازگشت</p>
</Link>
+27 -6
View File
@@ -1,18 +1,39 @@
import { Link } from "react-router-dom"
import { Link, useNavigate } from "react-router-dom"
import { Input } from "../common/input"
import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { LoginWithCodeInterface } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup"
import { LoginWithCodeSchema } from "../../schema"
import { ErrorComponent } from "../common/error"
import { Button } from "@headlessui/react"
import { ButtonComponent } from "../common/button"
import { useMutation } from "@tanstack/react-query"
import { sendOTPMutation } from "../../services/api/auth"
import toast from "react-hot-toast"
export const LoginWithCodeForm = () => {
const navigate = useNavigate()
const { mutate, isPending } = useMutation({
mutationFn: sendOTPMutation,
mutationKey: ["send-otp"]
})
const { handleSubmit, formState: { errors }, control } = useForm<LoginWithCodeInterface>({
resolver: yupResolver(LoginWithCodeSchema)
})
const handleLoginWithCode: SubmitHandler<LoginWithCodeInterface> = async (data) => {
console.log("data=>", data)
mutate({
...data
},
{
onSuccess: () => {
toast.success(`کد ورود برای شماره ${data?.mobile_number} ارسال شد`)
navigate(`/auth/send-code?phone_number=${data?.mobile_number}`)
},
onError: (err: any) => {
console.log("error", err)
navigate(`/auth/send-code?phone_number=${data?.mobile_number}`)
}
}
)
}
return (
<form onSubmit={handleSubmit(handleLoginWithCode)}
@@ -26,15 +47,15 @@ export const LoginWithCodeForm = () => {
</div>
</div>
<div className="mt-9 mb-[42px]">
<Controller control={control} name="phoneNumber" render={({ field }) => (
<Controller control={control} name="mobile_number" render={({ field }) => (
<div>
<Input type="number" placeholder="شماره موبایل" field={field} />
<ErrorComponent show={errors.phoneNumber} message={errors.phoneNumber?.message} />
<ErrorComponent show={errors.mobile_number} message={errors.mobile_number?.message} />
</div>
)} />
</div>
<div className="flex flex-col gap-y-[27px] items-center">
<Button type="submit">دریافت کد تایید</Button>
<ButtonComponent title="دریافت کد تایید" pending={isPending} type="submit" />
<Link to="/auth/login">
<p className="font-medium text-base text-primary-color">بازگشت</p>
</Link>
+86 -27
View File
@@ -1,20 +1,75 @@
import { Link } from "react-router-dom"
import { Link, useNavigate } from "react-router-dom"
import { ComboBox } from "../common/combo-box"
import { Input } from "../common/input"
import { Button } from "@headlessui/react"
import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { RegisterInterface } from "../../types"
import { CitiesType, ComboBoxItems, ProvinceType, RegisterInterface } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup"
import { RegisterSchema } from "../../schema"
import { ErrorComponent } from "../common/error"
import { getProvincesList } from "../../services/api/province"
import { useMutation, useQuery } from "@tanstack/react-query"
import { useEffect, useState } from "react"
import { getCitiesList } from "../../services/api/cities"
import { registerMutation } from "../../services/api/auth"
import { ButtonComponent } from "../common/button"
import toast from "react-hot-toast"
export const RegisterForm = () => {
const { handleSubmit, formState: { errors }, control } = useForm<RegisterInterface>({
const [provinceData, setProvinceData] = useState<ComboBoxItems[] | []>([])
const [citiesData, setCitiesData] = useState<ComboBoxItems[] | []>([])
const navigate = useNavigate()
const { mutate, isPending } = useMutation({
mutationFn: registerMutation,
mutationKey: ["register"]
})
const { data: provinces } = useQuery({
queryFn: getProvincesList,
queryKey: ["province"]
})
const { data: cities } = useQuery({
queryFn: getCitiesList,
queryKey: ["cities"]
})
const { handleSubmit, formState: { errors }, control, watch } = useForm<RegisterInterface>({
resolver: yupResolver(RegisterSchema)
})
const provinceName: any = watch("province_name");
const handleRegister: SubmitHandler<RegisterInterface> = async (data) => {
console.log("data=>", data)
mutate({
...data,
province_name: data?.province_name?.name,
city_name: data?.province_name?.name
},
{
onSuccess: () => {
toast.success("با موفقیت ثبت نام شدید")
navigate("/auth/login")
},
onError: (error: any) => {
console.log("error =>", error)
}
})
}
useEffect(() => {
const tempArray: ComboBoxItems[] = [...provinceData]
provinces?.data?.data?.map((province: ProvinceType) => tempArray.push({
id: province?.ProvinceID,
name: province?.ProvinceName
}))
setProvinceData(tempArray)
}, [provinces])
useEffect(() => {
const tempCities: any = []
const newCities = cities?.data?.data?.filter((city: CitiesType) => city?.ProvinceName.toLocaleLowerCase().includes(provinceName?.name.toLowerCase()))
newCities?.map((city: CitiesType) => tempCities?.push({
id: city?.CityID,
name: city?.CityName
}))
setCitiesData(tempCities)
}, [provinceName]);
return (
<form onSubmit={handleSubmit(handleRegister)}
className="w-full sm:w-fit sm:min-w-[500px] max-h-screen overflow-y-auto xl:w-1/2 xl:p-0 xl:py-5 p-10 flex flex-col bg-auth-form items-center gap-y-[42px] rounded-3x rounded-3xl">
@@ -31,43 +86,47 @@ export const RegisterForm = () => {
</div>
<div className="grid grid-cols-1 gap-y-5 mb-[42px] mt-9">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-x-4 gap-y-5">
<Controller control={control} name="name" render={({ field }) => <div>
<Controller control={control} name="first_name" render={({ field }) => <div>
<Input type="text" placeholder="نام" field={field} />
<ErrorComponent show={errors.name} message={errors.name?.message} />
<ErrorComponent show={errors.first_name} message={errors.first_name?.message} />
</div>} />
<Controller control={control} name="family" render={({ field }) => <div>
<Controller control={control} name="last_name" render={({ field }) => <div>
<Input type="text" placeholder="نام خانوادگی" field={field} />
<ErrorComponent show={errors.family} message={errors.family?.message} />
<ErrorComponent show={errors.last_name} message={errors.last_name?.message} />
</div>} />
</div>
<div className="flex flex-col lg:flex-row gap-y-5 gap-x-4">
<Controller control={control} name="capCity" render={({ field }) => <div>
<ComboBox field={field} />
<ErrorComponent show={errors.capCity} message={errors.capCity?.message} />
</div>} />
<Controller control={control} name="city" render={({ field }) => <div>
<ComboBox field={field} />
<ErrorComponent show={errors.city} message={errors.city?.message} />
</div>} />
</div>
<Controller control={control} name="marketName" render={({ field }) => (<div>
<Input type="text" placeholder="نام فروشگاه" field={field} />
<ErrorComponent show={errors.marketName} message={errors.marketName?.message} />
<Controller control={control} name="national_code" render={({ field }) => (<div>
<Input type="text" placeholder="کد ملی" field={field} />
<ErrorComponent show={errors.national_code} message={errors.national_code?.message} />
</div>)} />
<Controller control={control} name="phoneNumber" render={({ field }) => <div>
<div className="flex flex-col lg:flex-row gap-y-5 gap-x-4">
<Controller control={control} name="province_name" render={({ field }) => <div>
<ComboBox field={field} data={provinceData} />
<ErrorComponent show={errors.province_name} message={errors.province_name?.message} />
</div>} />
<Controller control={control} name="city_name" render={({ field }) => <div>
<ComboBox field={field} data={citiesData} />
<ErrorComponent show={errors.city_name} message={errors.city_name?.message} />
</div>} />
</div>
<Controller control={control} name="shopName" render={({ field }) => (<div>
<Input type="text" placeholder="نام فروشگاه" field={field} />
<ErrorComponent show={errors.shopName} message={errors.shopName?.message} />
</div>)} />
<Controller control={control} name="mobile_number" render={({ field }) => <div>
<Input type="number" placeholder="شماره موبایل" field={field} />
<ErrorComponent show={errors.phoneNumber} message={errors.phoneNumber?.message} />
<ErrorComponent show={errors.mobile_number} message={errors.mobile_number?.message} />
</div>} />
<Controller control={control} name="password" render={({ field }) => <div>
<Input type="password" placeholder="رمز عبور" field={field} />
<ErrorComponent show={errors.password} message={errors.password?.message} />
</div>} />
<Controller control={control} name="repeatPassword" render={({ field }) => <div>
<Controller control={control} name="password_confirmation" render={({ field }) => <div>
<Input type="password" placeholder="تکرار رمز عبور" field={field} />
<ErrorComponent show={errors.repeatPassword} message={errors.repeatPassword?.message} />
<ErrorComponent show={errors.password_confirmation} message={errors.password_confirmation?.message} />
</div>} />
</div>
<Button type="submit">ثبت نام</Button>
<ButtonComponent title="ثبت نام" pending={isPending} type="submit" />
</div>
</form>
)
@@ -1,19 +1,79 @@
import { Button } from "@headlessui/react";
import { Controller, SubmitHandler, useForm } from "react-hook-form";
import OTPInput from "react-otp-input"
import { Link } from "react-router-dom"
import { Link, useNavigate, useSearchParams } from "react-router-dom"
import { ResetPasswordCodeInterface } from "../../types";
import { yupResolver } from "@hookform/resolvers/yup";
import { ResetPasswordCodeSchema } from "../../schema";
import { ErrorComponent } from "../common/error";
import { useMutation } from "@tanstack/react-query";
import { checkOTP, sendOTPMutation } from "../../services/api/auth";
import toast from "react-hot-toast";
import { useEffect, useState } from "react";
import { ButtonComponent } from "../common/button";
export const ResetpasswordCodeForm = () => {
const navigate = useNavigate()
const [seconds, setSeconds] = useState(30);
const [isTimeUp, setIsTimeUp] = useState(false);
const [searchParams] = useSearchParams()
const { mutate, isPending } = useMutation({
mutationFn: checkOTP,
mutationKey: ["check-otp"]
})
const { mutate: sendOTPMutate, isPending: sendOTPPending } = useMutation({
mutationFn: sendOTPMutation,
mutationKey: ["send-otp"]
})
const { handleSubmit, formState: { errors }, control } = useForm<ResetPasswordCodeInterface>({
resolver: yupResolver(ResetPasswordCodeSchema)
})
const submitCode: SubmitHandler<ResetPasswordCodeInterface> = async (data) => {
console.log("data=>", data)
await mutate({
...data,
mobile_number: searchParams.get("phone_number"),
},
{
onSuccess: () => {
toast.success(`کد وارد شده مطابقت دارد`)
navigate(`/auth/reset-password?mobile_number=${searchParams.get("phone_number")}&otp=${data?.otp}`)
},
onError: (err: any) => {
console.log("err=>", err)
navigate(`/auth/reset-password?mobile_number=${searchParams.get("phone_number")}&otp=${data?.otp}`)
}
})
}
const handleSendOTPAgain = async () => {
await sendOTPMutate({
mobile_number: searchParams.get("phone_number")
},
{
onSuccess: () => {
toast.success(`کد ورود برای شماره ${searchParams.get("phone_number")} ارسال شد`)
setSeconds(30)
setIsTimeUp(false)
},
onError: (err: any) => {
console.log("err=>", err)
}
})
}
useEffect(() => {
if (seconds > 0) {
const timer = setInterval(() => {
setSeconds(prevSeconds => prevSeconds - 1);
}, 1000);
return () => clearInterval(timer);
} else {
setIsTimeUp(true);
}
}, [seconds]);
return (
<form onSubmit={handleSubmit(submitCode)}
className="w-fit xl:w-1/2 xl:p-0 p-10 flex flex-col bg-auth-form justify-center items-center gap-y-[42px] rounded-3xl">
@@ -23,7 +83,7 @@ export const ResetpasswordCodeForm = () => {
<span className="font-medium text-lg md:text-2xl xl:text-3xl text-primary-text-color">فراموشی رمز عبور</span>
<div className="flex flex-row gap-x-0.5 items-center">
<div className="flex flex-col sm:flex-row gap-0.5 items-center">
<p className="text-secondary-text-color font-normal text-xs md:text-sm">کد تایید ارسال شده به شماره 09376225448 را وارد کنید.</p>
<p className="text-secondary-text-color font-normal text-xs md:text-sm">کد تایید ارسال شده به شماره {searchParams.get("phone_number")} را وارد کنید.</p>
<Link to="/auth/forgot-password">
<p className="text-primary-color font-bold text-xs md:text-sm cursor-pointer">ویرایش</p>
</Link>
@@ -31,7 +91,7 @@ export const ResetpasswordCodeForm = () => {
</div>
</div>
<div className="mt-9 mb-[42px] flex w-full justify-center items-center" id="otp">
<Controller control={control} name="code" render={({ field }) => (
<Controller control={control} name="otp" render={({ field }) => (
<div>
<OTPInput
containerStyle="otp-container"
@@ -40,15 +100,15 @@ export const ResetpasswordCodeForm = () => {
renderInput={(props) => <input {...props} />}
inputStyle="otp-inputs"
/>
<ErrorComponent show={errors.code} message={errors.code?.message} />
<ErrorComponent show={errors.otp} message={errors.otp?.message} />
</div>
)} />
</div>
<div className="flex flex-col gap-y-[27px] items-center">
<p className="font-normal text-secondary-text-color text-base">30 ثانیه دیگر تا دریافت کد</p>
<Button type="submit">تایید</Button>
<p className="font-medium text-base text-primary-color">ارسال مجدد</p>
<p className="font-normal text-secondary-text-color text-base">{seconds} ثانیه دیگر تا دریافت کد</p>
<ButtonComponent title="تایید" pending={isPending} type="submit" />
<ButtonComponent title="ارسال مجدد" type="button" theme="glass" className="w-fit h-fit p-0" disable={!isTimeUp} pending={sendOTPPending} onClick={handleSendOTPAgain} />
</div>
</div>
</form>
+27 -7
View File
@@ -1,14 +1,23 @@
import { Link } from "react-router-dom"
import { Link, useNavigate, useSearchParams } from "react-router-dom"
import { Input } from "../common/input"
import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { ResetPasswordInterface } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup"
import { resetPasswordSchema } from "../../schema"
import { Button } from "@headlessui/react"
import { ErrorComponent } from "../common/error"
import { useMutation } from "@tanstack/react-query"
import { resetPasswordMutation } from "../../services/api/auth"
import { ButtonComponent } from "../common/button"
import toast from "react-hot-toast"
export const ResetPasswordForm = () => {
const navigate = useNavigate()
const { mutate, isPending } = useMutation({
mutationFn: resetPasswordMutation,
mutationKey: ["reset-password"]
})
const [searchParams] = useSearchParams()
const {
handleSubmit,
formState: { errors },
@@ -17,7 +26,18 @@ export const ResetPasswordForm = () => {
resolver: yupResolver(resetPasswordSchema)
})
const handleResetPassword: SubmitHandler<ResetPasswordInterface> = async (data) => {
console.log("data=>", data)
await mutate({
...data,
mobile_number: searchParams.get("mobile_number"),
otp: searchParams.get("otp")
},
{
onSuccess: () => {
toast.success("تغییر رمز عبور با موفقیت انجام شد")
navigate("/auth/login")
},
onError: (err: any) => console.log("err=>", err)
})
}
return (
<form onSubmit={handleSubmit(handleResetPassword)}
@@ -27,7 +47,7 @@ export const ResetPasswordForm = () => {
<div className="flex flex-col gap-y-5">
<span className="font-medium text-lg md:text-2xl xl:text-3xl text-primary-text-color">بازیابی رمز عبور</span>
<div className="flex flex-col md:flex-row gap-0.5 items-center">
<p className="text-secondary-text-color font-normal text-xs md:text-sm">کد تایید ارسال شده به شماره 09376225448 را وارد کنید.</p>
<p className="text-secondary-text-color font-normal text-xs md:text-sm">کد تایید ارسال شده به شماره {searchParams.get("mobile_number")} را وارد کنید.</p>
<Link to="/auth/forgot-password">
<p className="text-primary-color font-bold text-xs md:text-sm cursor-pointer">ویرایش</p>
</Link>
@@ -38,12 +58,12 @@ export const ResetPasswordForm = () => {
<Input type="password" placeholder="رمز عبور" field={field} />
<ErrorComponent show={errors.password} message={errors.password?.message} />
</div>)} />
<Controller control={control} name="repeatPassword" render={({ field }) => (<div>
<Controller control={control} name="password_confirmation" render={({ field }) => (<div>
<Input type="password" placeholder="تکرار رمز عبور" field={field} />
<ErrorComponent show={errors.repeatPassword} message={errors.repeatPassword?.message} />
<ErrorComponent show={errors.password_confirmation} message={errors.password_confirmation?.message} />
</div>)} />
</div>
<Button type="submit">ذخیره</Button>
<ButtonComponent title="ذخیره" pending={isPending} type="submit" />
</div>
</form>
)
+65 -12
View File
@@ -1,19 +1,72 @@
import OTPInput from "react-otp-input"
import { Link } from "react-router-dom"
import { Link, useSearchParams } from "react-router-dom"
import { ErrorComponent } from "../common/error"
import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { Button } from "@headlessui/react"
import { LoginCodeInterface } from "../../types"
import { LoginWithOTP } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup"
import { LoginCodeSchema } from "../../schema"
import { useEffect, useState } from "react"
import { ButtonComponent } from "../common/button"
import { useMutation } from "@tanstack/react-query"
import { loginWithOTP, sendOTPMutation } from "../../services/api/auth"
import toast from "react-hot-toast"
export const SendCodeForm = () => {
const { handleSubmit, formState: { errors }, control } = useForm<LoginCodeInterface>({
const { mutate, isPending } = useMutation({
mutationFn: sendOTPMutation,
mutationKey: ["send-otp"]
})
const { mutate: loginOtpMutate, isPending: loginOtpPending } = useMutation({
mutationFn: loginWithOTP,
mutationKey: ["login-otp"]
})
const [seconds, setSeconds] = useState(30);
const [isTimeUp, setIsTimeUp] = useState(false);
const [searchParams] = useSearchParams();
const { handleSubmit, formState: { errors }, control } = useForm<LoginWithOTP>({
resolver: yupResolver(LoginCodeSchema)
})
const submitCode: SubmitHandler<LoginCodeInterface> = async (data) => {
console.log("data=>", data)
const handleSendOTPAgain = async () => {
await mutate({
mobile_number: searchParams.get("phone_number")
},
{
onSuccess: () => {
toast.success(`کد ورود برای شماره ${searchParams.get("phone_number")} ارسال شد`)
setSeconds(30)
setIsTimeUp(false)
},
onError: (err: any) => {
console.log("err=>", err)
}
})
}
const submitCode: SubmitHandler<LoginWithOTP> = async (data) => {
loginOtpMutate({
...data,
mobile_number: searchParams.get("phone_number"),
},
{
onSuccess: () => {
toast.success(`کد ورود برای شماره ${searchParams.get("phone_number")} ارسال شد`)
setSeconds(30)
setIsTimeUp(false)
},
onError: (err: any) => {
console.log("err=>", err)
}
})
}
useEffect(() => {
if (seconds > 0) {
const timer = setInterval(() => {
setSeconds(prevSeconds => prevSeconds - 1);
}, 1000);
return () => clearInterval(timer);
} else {
setIsTimeUp(true);
}
}, [seconds]);
return (
<form onSubmit={handleSubmit(submitCode)}
className="w-fit xl:w-1/2 xl:p-0 p-10 flex flex-col bg-auth-form justify-center items-center gap-y-[42px] rounded-3xl">
@@ -23,7 +76,7 @@ export const SendCodeForm = () => {
<span className="font-medium text-lg md:text-2xl xl:text-3xl text-primary-text-color">ورود با رمز یکبار مصرف</span>
<div className="flex flex-row gap-x-0.5 items-center">
<div className="flex flex-col sm:flex-row gap-0.5 items-center">
<p className="text-secondary-text-color font-normal text-xs md:text-sm">کد تایید ارسال شده به شماره 09376225448 را وارد کنید.</p>
<p className="text-secondary-text-color font-normal text-xs md:text-sm">کد تایید ارسال شده به شماره {searchParams.get("phone_number")} را وارد کنید.</p>
<Link to="/auth/login-with-code">
<p className="text-primary-color font-bold text-xs md:text-sm cursor-pointer">ویرایش</p>
</Link>
@@ -31,7 +84,7 @@ export const SendCodeForm = () => {
</div>
</div>
<div className="mt-9 mb-[42px] flex w-full justify-center items-center" id="otp">
<Controller control={control} name="code" render={({ field }) => (
<Controller control={control} name="otp" render={({ field }) => (
<div>
<OTPInput
containerStyle="otp-container"
@@ -40,15 +93,15 @@ export const SendCodeForm = () => {
renderInput={(props) => <input {...props} />}
inputStyle="otp-inputs"
/>
<ErrorComponent show={errors.code} message={errors.code?.message} />
<ErrorComponent show={errors.otp} message={errors.otp?.message} />
</div>
)} />
</div>
<div className="flex flex-col gap-y-[27px] items-center">
<p className="font-normal text-secondary-text-color text-base">30 ثانیه دیگر تا دریافت کد</p>
<Button type="submit">تایید</Button>
<p className="font-medium text-base text-primary-color">ارسال مجدد</p>
{!isTimeUp && <p className="font-normal text-secondary-text-color text-base">{seconds} ثانیه دیگر تا دریافت کد</p>}
<ButtonComponent title="تایید" pending={loginOtpPending} type="submit" />
<ButtonComponent title="ارسال مجدد" type="button" theme="glass" className="w-fit h-fit p-0" disable={!isTimeUp} pending={isPending} onClick={handleSendOTPAgain} />
</div>
</div>
</form>
+7 -1
View File
@@ -13,10 +13,16 @@
}
@layer base {
* {
font-family: 'vazirmatn', 'vaziren';
}
body {
direction: rtl;
scrollbar-width: none;
}
input[type=number]::-webkit-outer-spin-button,
@@ -38,7 +44,7 @@
}
#otp {
direction: ltr;
direction: ltr !important;
}
}
+21 -12
View File
@@ -13,13 +13,13 @@ export const loginSchema = yup.object({
export const resetPasswordSchema = yup.object({
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string()
password_confirmation: yup.string()
.oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند")
.required("تکرار رمز عبور الزامی میباشد"),
})
export const forgotPasswordSchema = yup.object({
phoneNumber: yup.string()
mobile_number: yup.string()
.required("وارد کردن شماره تماس الزامی میباشد")
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
@@ -27,7 +27,7 @@ export const forgotPasswordSchema = yup.object({
})
export const LoginWithCodeSchema = yup.object({
phoneNumber: yup.string()
mobile_number: yup.string()
.required("وارد کردن شماره تماس الزامی میباشد")
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
@@ -35,7 +35,11 @@ export const LoginWithCodeSchema = yup.object({
})
export const ResetPasswordCodeSchema = yup.object({
code: yup.string()
mobile_number: yup.string()
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
otp: yup.string()
.required("وارد کردن کد الزامی میباشد")
.min(5, "کد نمیتوند کمتر از 5 رقم باشد")
.max(5, "کد نمیتواند بیشتر از 5 رقم باشد")
@@ -43,7 +47,11 @@ export const ResetPasswordCodeSchema = yup.object({
})
export const LoginCodeSchema = yup.object({
code: yup.string()
mobile_number: yup.string()
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
otp: yup.string()
.required("وارد کردن کد الزامی میباشد")
.min(5, "کد نمیتوند کمتر از 5 رقم باشد")
.max(5, " کد نمیتواند بیشتر از 5 رقم باشد")
@@ -51,18 +59,19 @@ export const LoginCodeSchema = yup.object({
})
export const RegisterSchema = yup.object({
name: yup.string().required("وارد کردن نام الزامی میباشد").min(3, "نام نمیتواند کمتر از 3 کاراکتر باشد").max(20, "نام نمیتواند بیشتر از 20 کاراکتر باشد"),
family: yup.string().required("وارد کردن نام خانوادگی الزامی میباشد").min(3, "نام خانوادگی نمیتواند کمتر از 3 کاراکتر باشد").max(25, "نام خانوادگی نمیتواند بیشتر از 25 کاراکتر باشد"),
city: yup.object().required("وارد کردن شهر الزامی مباشد"),
capCity: yup.object().required("وارد کردن استان الزامی مباشد"),
marketName: yup.string().required("وارد کردن نام فروشگاه الزامی میباشد").min(2, "نام فروشگاه نمیتواند کمتر از 3 کاراکتر باشد").max(30, "نام فروشگاه نمیتواند بیشتر از 30 کاراکتر باشد"),
phoneNumber: yup.string()
first_name: yup.string().required("وارد کردن نام الزامی میباشد").min(3, "نام نمیتواند کمتر از 3 کاراکتر باشد").max(20, "نام نمیتواند بیشتر از 20 کاراکتر باشد"),
last_name: yup.string().required("وارد کردن نام خانوادگی الزامی میباشد").min(3, "نام خانوادگی نمیتواند کمتر از 3 کاراکتر باشد").max(25, "نام خانوادگی نمیتواند بیشتر از 25 کاراکتر باشد"),
national_code: yup.string().required("وارد کردن کد ملی الزامی میباشد").min(10, "کد ملی نمیتواند کمتر از 10 کاراکتر باشد").max(10, "کد ملی نمیتونه بیشتر از 10 کاراکتر باشه").matches(/^[0-9]{10}$/, "کد نامعتبر"),
city_name: yup.object().required("وارد کردن شهر الزامی مباشد"),
province_name: yup.object().required("وارد کردن استان الزامی مباشد"),
shopName: yup.string().required("وارد کردن نام فروشگاه الزامی میباشد").min(2, "نام فروشگاه نمیتواند کمتر از 3 کاراکتر باشد").max(30, "نام فروشگاه نمیتواند بیشتر از 30 کاراکتر باشد"),
mobile_number: yup.string()
.required("وارد کردن شماره تماس الزامی میباشد")
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string()
password_confirmation: yup.string()
.oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند")
.required("تکرار رمز عبور الزامی میباشد"),
})
+6
View File
@@ -1,3 +1,9 @@
import { LoginWithCodeInterface, LoginWithOTP, RegisterInterface, ResetPasswordInterface } from "../../types";
import axiosInstance from "../axios";
export const loginMutation = (payload: any) => axiosInstance.post<any>("/auth/gps/login/pass", payload)
export const registerMutation = (payload: RegisterInterface) => axiosInstance.post<any>("/auth/gps/register", payload)
export const sendOTPMutation = (payload: LoginWithCodeInterface) => axiosInstance.post<any>("/auth/gps/otp/send", payload)
export const loginWithOTP = (payload: LoginWithOTP) => axiosInstance.post<any>("/auth/gps/login/otp", payload)
export const checkOTP = (payload: LoginWithOTP) => axiosInstance.post<any>("/auth/gps/otp/check", payload)
export const resetPasswordMutation = (payload: ResetPasswordInterface) => axiosInstance.post<any>("/auth/gps/otp/reset-pass", payload)
+7
View File
@@ -0,0 +1,7 @@
import { CitiesResponse } from "../../types";
import axiosInstance from "../axios";
export const getCitiesList = () => axiosInstance.post<CitiesResponse>("/cross/getRouteManager", {
url: "/api/GetCity",
db: "verity"
})
+7
View File
@@ -0,0 +1,7 @@
import { ProvinceResponse } from "../../types";
import axiosInstance from "../axios";
export const getProvincesList = () => axiosInstance.post<ProvinceResponse, ProvinceResponse>("/cross/getRouteManager", {
url: "/api/GetProvince",
db: "verity"
})
+45 -13
View File
@@ -11,34 +11,39 @@ export interface LoginResponseInterface {
}
export interface ResetPasswordInterface {
mobile_number?: string | any,
otp?: string | any,
password: string,
repeatPassword: string
password_confirmation: string
}
export interface ForgotPasswordInterface {
phoneNumber: string
mobile_number: string
}
export interface LoginWithCodeInterface {
phoneNumber: string
mobile_number: string | any
}
export interface ResetPasswordCodeInterface {
code: string
mobile_number?: string | any
otp: string
}
export interface LoginCodeInterface {
code: string
mobile_number: any
otp: string
}
export interface RegisterInterface {
name: string,
family: string,
city: object,
capCity: object,
marketName: string,
phoneNumber: string,
password: string,
repeatPassword: string,
first_name: string
last_name: string
national_code: string
city_name: object | any
province_name: object | any
shopName: string
mobile_number: string
password: string
password_confirmation: string
}
export interface SidebarInterface {
@@ -281,3 +286,30 @@ export interface TableParams {
rows: number,
search: string
}
export type ProvinceType = {
ProvinceID: number,
ProvinceName: string
}
export interface ProvinceResponse {
data: { data: ProvinceType[] | [], }
error: string | null
}
export interface LoginWithOTP {
mobile_number?: string | any
otp: string
}
export type CitiesType = {
CityID: number,
CityName: string,
ProvinceID: string,
ProvinceName: string
}
export interface CitiesResponse {
data: CitiesType[] | [],
error: string | null | any
}