create datepicker - set farsi digit font - create my account page - fix bugs

This commit is contained in:
Alihaghighattalab
2024-08-07 15:35:14 +03:30
parent 7499180fb1
commit 9797108b91
14 changed files with 308 additions and 10 deletions
+52
View File
@@ -19,9 +19,12 @@
"@types/react-dom": "^18.3.0",
"clsx": "^2.1.1",
"iconsax-react": "^0.0.8",
"jalaali-js": "^1.2.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.2",
"react-modern-calendar-datepicker": "^3.1.6",
"react-multi-date-picker": "^4.5.2",
"react-otp-input": "^3.1.1",
"react-router-dom": "^6.26.0",
"react-scripts": "5.0.1",
@@ -11020,6 +11023,12 @@
"node": ">=8"
}
},
"node_modules/jalaali-js": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/jalaali-js/-/jalaali-js-1.2.7.tgz",
"integrity": "sha512-gE+YHWSbygYAoJa+Xg8LWxGILqFOxZSBQQw39ghel01fVFUxV7bjL0x1JFsHcLQ3uPjvn81HQMa+kxwyPWnxGQ==",
"license": "MIT"
},
"node_modules/jest": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
@@ -15972,6 +15981,12 @@
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT"
},
"node_modules/react-date-object": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/react-date-object/-/react-date-object-2.1.9.tgz",
"integrity": "sha512-BHxD/quWOTo9fLKV/cfL/M31ePoj4a1JaJ/CnOf8Ndg3mrkh4x9wEMMkCfTrzduxDOgU8ZgR8uarhqI5G71sTg==",
"license": "MIT"
},
"node_modules/react-dev-utils": {
"version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@@ -16172,6 +16187,16 @@
"react": "^18.3.1"
}
},
"node_modules/react-element-popper": {
"version": "2.1.7",
"resolved": "https://registry.npmjs.org/react-element-popper/-/react-element-popper-2.1.7.tgz",
"integrity": "sha512-tuM2OxKlW32h+6uFSK6EENHPeZ2OGgOipHfOAl+VLWEv9/j3QkSGbD+ADX3A9uJlmq24i37n28RjJmAbGTfpEg==",
"license": "MIT",
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-error-overlay": {
"version": "6.0.11",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz",
@@ -16200,6 +16225,33 @@
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"license": "MIT"
},
"node_modules/react-modern-calendar-datepicker": {
"version": "3.1.6",
"resolved": "https://registry.npmjs.org/react-modern-calendar-datepicker/-/react-modern-calendar-datepicker-3.1.6.tgz",
"integrity": "sha512-lnMqEMj9Wn32/sm119tjCl5lOkq4u9vJE7wggi7hdXV4s8rPdKlH56FVVehlBi0dfYeWQVZ9npY384Zprjn4WA==",
"license": "MIT",
"dependencies": {
"jalaali-js": "^1.1.0"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-multi-date-picker": {
"version": "4.5.2",
"resolved": "https://registry.npmjs.org/react-multi-date-picker/-/react-multi-date-picker-4.5.2.tgz",
"integrity": "sha512-FgWjZB3Z6IA6XpcWiLPk85PwcRUhOiYhKK42o5k672gD/n2I6rzPfQ8bUrldOIiF/Z7FfOCdH7a6FeubzqteLg==",
"license": "MIT",
"dependencies": {
"react-date-object": "^2.1.8",
"react-element-popper": "^2.1.6"
},
"peerDependencies": {
"react": ">=16.8.0",
"react-dom": ">=16.8.0"
}
},
"node_modules/react-otp-input": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/react-otp-input/-/react-otp-input-3.1.1.tgz",
+3
View File
@@ -14,9 +14,12 @@
"@types/react-dom": "^18.3.0",
"clsx": "^2.1.1",
"iconsax-react": "^0.0.8",
"jalaali-js": "^1.2.7",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-hook-form": "^7.52.2",
"react-modern-calendar-datepicker": "^3.1.6",
"react-multi-date-picker": "^4.5.2",
"react-otp-input": "^3.1.1",
"react-router-dom": "^6.26.0",
"react-scripts": "5.0.1",
Binary file not shown.
Binary file not shown.
-1
View File
@@ -16,7 +16,6 @@ type Props = {
export const ComboBox: FC<Props> = ({ field }) => {
const [query, setQuery] = useState('')
// const [selected, setSelected] = useState(value[1])
const filteredValue =
query === ''
+31
View File
@@ -0,0 +1,31 @@
import { FC } from 'react';
import { Calendar2 } from 'iconsax-react';
import DatePicker from 'react-multi-date-picker';
import persian from "react-date-object/calendars/persian"
import persian_fa from "react-date-object/locales/persian_fa"
import { getJalaaliDate } from '../../utility/getDate';
type Props = {
field: any
}
export const DatepickerComponent: FC<Props> = ({ field }) => {
return (
<div className='relative w-full min-w-max cursor-pointer mb-10 xl:mb-0'>
<div className="absolute top-[18.5px] left-5 grid h-5 w-5 place-items-center !z-50">
<Calendar2 color='#777577' />
</div>
<DatePicker
{...field}
value={field.value}
onChange={field.onChange}
showOtherDays
placeholder={getJalaaliDate()}
calendar={persian}
locale={persian_fa}
calendarPosition="bottom-right"
inputClass='w-full !min-w-full absolute input-style top-0 z-10 cursor-pointer'
/>
</div>
)
}
+3 -2
View File
@@ -7,9 +7,10 @@ type Props = {
placeholder: string,
icon?: React.ReactNode,
field?: any
ref?: any
}
export const Input: FC<Props> = ({ placeholder, type = "text", icon, field }) => {
export const Input: FC<Props> = ({ placeholder, type = "text", icon, field, ref }) => {
const [hidden, setHidden] = useState<boolean>(false)
const handleHidden = () => setHidden(prev => !prev)
return (
@@ -19,7 +20,7 @@ export const Input: FC<Props> = ({ placeholder, type = "text", icon, field }) =>
!hidden ? <Eye size="23" color="#777577" onClick={handleHidden} /> : <EyeSlash size="23" color="#777577" onClick={handleHidden} />
: icon}
</div>
<InputComponent {...field} type={type === "password" && hidden ? "text" : type} placeholder={placeholder} className="input-style min-w-full" />
<InputComponent ref={ref} {...field} type={type === "password" && hidden ? "text" : type} placeholder={placeholder} className="input-style min-w-full" />
</div>
)
}
+142
View File
@@ -0,0 +1,142 @@
import { Button } from "@headlessui/react"
import { Input } from "../common/input"
import { DatepickerComponent } from "../common/datepicker"
import { Controller, SubmitHandler, useForm } from "react-hook-form"
import { MyAccountInterface } from "../../types"
import { yupResolver } from "@hookform/resolvers/yup"
import { MyAccountSchema } from "../../schema"
import { ErrorComponent } from "../common/error"
import { ComboBox } from "../common/combo-box"
export const MyAccountPage = () => {
const { handleSubmit, formState: { errors }, control } = useForm<MyAccountInterface>({
resolver: yupResolver(MyAccountSchema)
})
const handleMyAccountForm: SubmitHandler<MyAccountInterface> = async (data) => {
console.log("data =>", data)
}
return (
<form onSubmit={handleSubmit(handleMyAccountForm)} className="w-full h-full flex flex-col gap-10 bg-auth-form rounded-[40px] p-[30px]">
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
<p className="font-medium text-xl text-primary-text-color">اطلاعات اولیه</p>
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
</p>
</div>
<div className="flex flex-col gap-10 w-full">
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px]">
<Controller name="phoneNumber" control={control} render={({ field }) => (
<div>
<Input placeholder="شماره تماس" type="number" field={field} />
<ErrorComponent show={errors.phoneNumber} message={errors.phoneNumber?.message} />
</div>
)} />
<Controller name="marketName" control={control} render={({ field }) => (
<div>
<Input placeholder="نام شرکت" field={field} />
<ErrorComponent show={errors.marketName} message={errors.marketName?.message} />
</div>
)} />
<Controller name="name" control={control} render={({ field }) => (
<div>
<Input placeholder="نام" field={field} />
<ErrorComponent show={errors.name} message={errors.name?.message} />
</div>
)} />
<Controller name="family" control={control} render={({ field }) => (
<div>
<Input placeholder="نام خانوادگی" field={field} />
<ErrorComponent show={errors.family} message={errors.family?.message} />
</div>
)} />
<Controller name="capCity" control={control} render={({ field }) => (
<div>
<ComboBox field={field} />
<ErrorComponent show={errors.capCity} message={errors.capCity?.message} />
</div>
)} />
<Controller name="city" control={control} render={({ field }) => (
<div>
<ComboBox field={field} />
<ErrorComponent show={errors.city} message={errors.city?.message} />
</div>
)} />
</div>
<div className="divide-y-2 border" />
</div>
</div>
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
<p className="font-medium text-xl text-primary-text-color">اطلاعات تکمیلی</p>
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
</p>
</div>
<div className="flex flex-col gap-10 w-full">
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px]">
<Controller name="address" control={control} render={({ field }) => (
<div>
<Input placeholder="آدرس" field={field} />
<ErrorComponent show={errors.address} message={errors.address?.message} />
</div>
)} />
<Controller name="phoneNumber1" control={control} render={({ field }) => (
<div>
<Input placeholder="شماره تماس" type="number" field={field} />
<ErrorComponent show={errors.phoneNumber1} message={errors.phoneNumber1?.message} />
</div>
)} />
<Controller name="staticNumber" control={control} render={({ field }) => (
<div>
<Input placeholder="شماره ثابت" type="number" field={field} />
<ErrorComponent show={errors.staticNumber} message={errors.staticNumber?.message} />
</div>
)} />
<Controller name="date" control={control} render={({ field }) => (
<div className="flex flex-col gap-y-9">
<DatepickerComponent field={field} />
<ErrorComponent show={errors.date} message={errors.date?.message} />
</div>
)} />
</div>
<div className="divide-y-2 border" />
</div>
</div>
<div className="flex flex-col gap-5 xl:gap-0 xl:flex-row xl:gap-x-10 xl:justify-between items-start">
<div className="flex flex-col gap-y-5 w-full xl:max-w-fit">
<p className="font-medium text-xl text-primary-text-color">تغییر رمز عبور</p>
<p className="font-normal text-base text-secondary-text-color text-wrap xl:max-w-xs">
لورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است چاپگرها و متون
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-[18px] w-full">
<Controller name="currentPassword" control={control} render={({ field }) => (
<div>
<Input placeholder="رمز عبور فعلی" type="password" field={field} />
<ErrorComponent show={errors.currentPassword} message={errors.currentPassword?.message} />
</div>
)} />
<div />
<Controller name="newPassword" control={control} render={({ field }) => (
<div>
<Input placeholder="رمز عبور جدید" type="password" field={field} />
<ErrorComponent show={errors.newPassword} message={errors.newPassword?.message} />
</div>
)} />
<Controller name="repeatPassword" control={control} render={({ field }) => (
<div>
<Input placeholder="تکرار رمز عبور" type="password" field={field} />
<ErrorComponent show={errors.repeatPassword} message={errors.repeatPassword?.message} />
</div>
)} />
</div>
</div>
<div className="flex w-full xl:justify-end mt-10">
<Button type="submit" className="w-full xl:max-w-fit xl:min-w-72">ذخیره</Button>
</div>
</form>
)
}
+8 -3
View File
@@ -7,9 +7,14 @@
src: url("../public/fonts/Vazir.woff") format('woff');
}
@font-face {
font-family: "vaziren";
src: url("../public/fonts/VazirEn.woff") format('woff');
}
@layer base {
html {
font-family: 'vazirmatn';
font-family: 'vazirmatn', 'vaziren';
direction: rtl;
}
@@ -23,11 +28,11 @@
-moz-appearance: textfield;
}
button{
button {
@apply text-base font-medium flex w-full justify-center bg-primary-color max-w-[442px] max-h-[55px] rounded-[20px] p-[18px] text-white
}
#otp{
#otp {
direction: ltr;
}
}
+7
View File
@@ -0,0 +1,7 @@
import { MyAccountPage } from "../../components/my-account"
export const MyAcccount = () => {
return (
<MyAccountPage />
)
}
+5
View File
@@ -9,6 +9,7 @@ import { LoginWithCode } from "../pages/auth/login-with-code";
import { SendCode } from "../pages/auth/send-code";
import { ResetPasswordCode } from "../pages/auth/reset-password-code";
import { DashboardLayout } from "../components/ui/dashboard/layout";
import { MyAcccount } from "../pages/dashboard/my-account";
export const router = createBrowserRouter([
@@ -20,6 +21,10 @@ export const router = createBrowserRouter([
index: true,
element: <App />
},
{
path: "my-account",
element: <MyAcccount />
},
]
},
{
+34 -4
View File
@@ -12,10 +12,10 @@ export const loginSchema = yup.object({
})
export const resetPasswordSchema = yup.object({
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی است"),
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string()
.oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند")
.required("تکرار رمز عبور الزامی است"),
.required("تکرار رمز عبور الزامی میباشد"),
})
export const forgotPasswordSchema = yup.object({
@@ -61,8 +61,38 @@ export const RegisterSchema = yup.object({
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی است"),
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string()
.oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند")
.required("تکرار رمز عبور الزامی است"),
.required("تکرار رمز عبور الزامی میباشد"),
})
export const MyAccountSchema = yup.object({
phoneNumber: yup.string()
.required("وارد کردن شماره تماس الزامی میباشد")
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
marketName: yup.string().required("وارد کردن نام فروشگاه الزامی میباشد").min(2, "نام فروشگاه نمیتواند کمتر از 3 کاراکتر باشد").max(30, "نام فروشگاه نمیتواند بیشتر از 30 کاراکتر باشد"),
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("وارد کردن استان الزامی مباشد"),
address: yup.string().required("وارد کردن آدرس الزامی میباشد"),
phoneNumber1: yup.string()
.required("وارد کردن شماره تماس الزامی میباشد")
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
staticNumber: yup.string()
.required("وارد کردن شماره تماس الزامی میباشد")
.min(8, "شماره تماس نمیتوند کمتر از 8 رقم باشد")
.max(8, "شماره تماس نمیتواند بیشتر از 8 رقم باشد")
.matches(/^[0-9]{8}$/, "شماره تماس نامعتبر"),
date: yup.string().required("وارد کردن تاریخ الزامی میباشد"),
currentPassword: yup.string().required("وارد کردن رمز عبور فعلی الزامی میباشد"),
newPassword: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string()
.oneOf([yup.ref('newPassword')], "رمز عبور و تکرار آن باید یکسان باشند")
.required("تکرار رمز عبور الزامی میاشد"),
})
+16
View File
@@ -54,4 +54,20 @@ export interface StatusBoxItemInterface {
title: string,
icon: React.ReactNode,
value: number
}
export interface MyAccountInterface {
phoneNumber: string,
marketName: string,
name: string,
family: string,
city: object,
capCity: object,
address: string,
phoneNumber1: string,
staticNumber: string,
date: string,
currentPassword: string,
newPassword: string,
repeatPassword: string
}
+7
View File
@@ -0,0 +1,7 @@
const jalaali = require('jalaali-js');
export const getJalaaliDate = () => {
const today = new Date();
const jalaaliDate = jalaali.toJalaali(today.getFullYear(), today.getMonth() + 1, today.getDate());
return `${jalaaliDate.jy}/${String(jalaaliDate.jm).padStart(2, '0')}/${String(jalaaliDate.jd).padStart(2, '0')}`;
}