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", "@types/react-dom": "^18.3.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"iconsax-react": "^0.0.8", "iconsax-react": "^0.0.8",
"jalaali-js": "^1.2.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.52.2", "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-otp-input": "^3.1.1",
"react-router-dom": "^6.26.0", "react-router-dom": "^6.26.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@@ -11020,6 +11023,12 @@
"node": ">=8" "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": { "node_modules/jest": {
"version": "27.5.1", "version": "27.5.1",
"resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz", "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
@@ -15972,6 +15981,12 @@
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==",
"license": "MIT" "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": { "node_modules/react-dev-utils": {
"version": "12.0.1", "version": "12.0.1",
"resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz", "resolved": "https://registry.npmjs.org/react-dev-utils/-/react-dev-utils-12.0.1.tgz",
@@ -16172,6 +16187,16 @@
"react": "^18.3.1" "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": { "node_modules/react-error-overlay": {
"version": "6.0.11", "version": "6.0.11",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "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==", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"license": "MIT" "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": { "node_modules/react-otp-input": {
"version": "3.1.1", "version": "3.1.1",
"resolved": "https://registry.npmjs.org/react-otp-input/-/react-otp-input-3.1.1.tgz", "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", "@types/react-dom": "^18.3.0",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"iconsax-react": "^0.0.8", "iconsax-react": "^0.0.8",
"jalaali-js": "^1.2.7",
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-hook-form": "^7.52.2", "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-otp-input": "^3.1.1",
"react-router-dom": "^6.26.0", "react-router-dom": "^6.26.0",
"react-scripts": "5.0.1", "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 }) => { export const ComboBox: FC<Props> = ({ field }) => {
const [query, setQuery] = useState('') const [query, setQuery] = useState('')
// const [selected, setSelected] = useState(value[1])
const filteredValue = const filteredValue =
query === '' 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, placeholder: string,
icon?: React.ReactNode, icon?: React.ReactNode,
field?: any 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 [hidden, setHidden] = useState<boolean>(false)
const handleHidden = () => setHidden(prev => !prev) const handleHidden = () => setHidden(prev => !prev)
return ( 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} /> !hidden ? <Eye size="23" color="#777577" onClick={handleHidden} /> : <EyeSlash size="23" color="#777577" onClick={handleHidden} />
: icon} : icon}
</div> </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> </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'); src: url("../public/fonts/Vazir.woff") format('woff');
} }
@font-face {
font-family: "vaziren";
src: url("../public/fonts/VazirEn.woff") format('woff');
}
@layer base { @layer base {
html { html {
font-family: 'vazirmatn'; font-family: 'vazirmatn', 'vaziren';
direction: rtl; direction: rtl;
} }
@@ -23,11 +28,11 @@
-moz-appearance: textfield; -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 @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; 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 { SendCode } from "../pages/auth/send-code";
import { ResetPasswordCode } from "../pages/auth/reset-password-code"; import { ResetPasswordCode } from "../pages/auth/reset-password-code";
import { DashboardLayout } from "../components/ui/dashboard/layout"; import { DashboardLayout } from "../components/ui/dashboard/layout";
import { MyAcccount } from "../pages/dashboard/my-account";
export const router = createBrowserRouter([ export const router = createBrowserRouter([
@@ -20,6 +21,10 @@ export const router = createBrowserRouter([
index: true, index: true,
element: <App /> 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({ export const resetPasswordSchema = yup.object({
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی است"), password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string() repeatPassword: yup.string()
.oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند") .oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند")
.required("تکرار رمز عبور الزامی است"), .required("تکرار رمز عبور الزامی میباشد"),
}) })
export const forgotPasswordSchema = yup.object({ export const forgotPasswordSchema = yup.object({
@@ -61,8 +61,38 @@ export const RegisterSchema = yup.object({
.min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد") .min(11, "شماره تماس نمیتوند کمتر از 11 رقم باشد")
.max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد") .max(11, "شماره تماس نمیتواند بیشتر از 11 رقم باشد")
.matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"), .matches(/^[0-9]{11}$/, "شماره تماس نامعتبر"),
password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی است"), password: yup.string().min(8, "رمز عبور نمیتواند کمتر از 8 کاراکتر باشد").required("رمز عبور الزامی میباشد"),
repeatPassword: yup.string() repeatPassword: yup.string()
.oneOf([yup.ref('password')], "رمز عبور و تکرار آن باید یکسان باشند") .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
@@ -55,3 +55,19 @@ export interface StatusBoxItemInterface {
icon: React.ReactNode, icon: React.ReactNode,
value: number 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')}`;
}