From 43af6ba3e640cc879292d5da224815939cbdc2bf Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Wed, 19 Feb 2025 14:51:12 +0330 Subject: [PATCH] profile ,footer , ... --- src/langs/fa.json | 10 +- src/pages/annoncement/List.tsx | 2 + .../annoncement/hooks/useAnnoncementData.ts | 9 + .../annoncement/service/AnnoncementService.ts | 6 + src/pages/auth/components/LoginStep2.tsx | 32 +- src/pages/financial/components/Legal.tsx | 10 +- src/pages/profile/Profile.tsx | 320 ++++++++++-------- src/pages/profile/types/ProfileTypes.ts | 1 + src/pages/ticket/CreateTicket.tsx | 2 +- src/shared/Footer.tsx | 50 +-- src/shared/Header.tsx | 4 +- src/shared/SideBar.tsx | 4 +- 12 files changed, 284 insertions(+), 166 deletions(-) create mode 100644 src/pages/annoncement/hooks/useAnnoncementData.ts create mode 100644 src/pages/annoncement/service/AnnoncementService.ts diff --git a/src/langs/fa.json b/src/langs/fa.json index 7a0cd36..314ee6c 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -43,7 +43,8 @@ }, "errors": { "required": "این فیلد اجباری می باشد", - "password_not_match": "رمز عبور با تکرار آن مطابقت ندارد" + "password_not_match": "رمز عبور با تکرار آن مطابقت ندارد", + "is_not_image": "فرمت فایل انتخاب شده اشتباه می باشد" }, "description": "توضیحات", "sidebar": { @@ -300,7 +301,9 @@ "image_profile": "تصویر حساب کاربری", "image_profile_desc": "تصویر خود را در حساب کاربری قرار دهید.", "upload_image": "آپلود تصویر", - "format_image": "فرمت های jpg, png,jpeg.حداکثر ۱مگابایت", + "formats": "فرمت های", + "max_size": "حداکثر ۱مگابایت", + "format_image": " jpg, png,jpeg.", "info_account": "اطلاعات حساب", "auth_after_change_info": "در صورت تغییر اطلاعات حساب باید مجددا احراز هویت انجام شود.", "username": "نام کاربری", @@ -316,7 +319,8 @@ "postal_code": "کد پستی", "username_available": "نام کاربری موجود", "username_not_available": "نام کاربری موجود نیست", - "username_save_success": "نام کاربری با موفقیت ذخیره شد" + "username_save_success": "نام کاربری با موفقیت ذخیره شد", + "image_uploaded_successfully": "تصویر با موفقیت آپلود شد" }, "email": "ایمیل", "save": "ذخیره", diff --git a/src/pages/annoncement/List.tsx b/src/pages/annoncement/List.tsx index 1fa9ca6..653b4a5 100644 --- a/src/pages/annoncement/List.tsx +++ b/src/pages/annoncement/List.tsx @@ -6,9 +6,11 @@ import StatusWithText from '../../components/StatusWithText' import { clx } from '../../helpers/utils' import { useNavigate } from 'react-router-dom' import { Pages } from '../../config/Pages' +import { useGetAnnoncement } from './hooks/useAnnoncementData' const AnnouncementtList: FC = () => { + const getAnnoncements = useGetAnnoncement() const { t } = useTranslation('global') const navigate = useNavigate() diff --git a/src/pages/annoncement/hooks/useAnnoncementData.ts b/src/pages/annoncement/hooks/useAnnoncementData.ts new file mode 100644 index 0000000..4e28797 --- /dev/null +++ b/src/pages/annoncement/hooks/useAnnoncementData.ts @@ -0,0 +1,9 @@ +import * as api from "../service/AnnoncementService"; +import { useQuery } from "@tanstack/react-query"; + +export const useGetAnnoncement = () => { + return useQuery({ + queryKey: ["announcements"], + queryFn: () => api.getAnnoncements(), + }); +}; diff --git a/src/pages/annoncement/service/AnnoncementService.ts b/src/pages/annoncement/service/AnnoncementService.ts new file mode 100644 index 0000000..69696b4 --- /dev/null +++ b/src/pages/annoncement/service/AnnoncementService.ts @@ -0,0 +1,6 @@ +import axios from "../../../config/axios"; + +export const getAnnoncements = async () => { + const { data } = await axios.get(`/announcements/user`); + return data; +}; diff --git a/src/pages/auth/components/LoginStep2.tsx b/src/pages/auth/components/LoginStep2.tsx index 0856d98..96bb7b4 100644 --- a/src/pages/auth/components/LoginStep2.tsx +++ b/src/pages/auth/components/LoginStep2.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, useEffect } from 'react' import { useTranslation } from 'react-i18next' import { OtpVerifyType } from '../types/AuthTypes' import { useFormik } from 'formik' @@ -47,6 +47,28 @@ const LoginStep2: FC = () => { }, }) + useEffect(() => { + // بررسی پشتیبانی از Web OTP API + if (!("OTPCredential" in window)) return; + + const ac = new AbortController(); + + navigator.credentials + .get({ + otp: { transport: ["sms"] }, + signal: ac.signal, + } as CredentialRequestOptions) // اطمینان از تطابق تایپ‌ها + .then((otpCredential) => { + if (otpCredential && "code" in otpCredential) { + alert("OTP Received: " + otpCredential.code); + formik.setFieldValue("code", (otpCredential).code); + } + }) + .catch((err) => console.error("SMS AutoFill Error:", err)); + + return () => ac.abort(); + }, []); + return (
@@ -78,7 +100,13 @@ const LoginStep2: FC = () => { numInputs={5} inputType='tel' onChange={(otp: string) => formik.setFieldValue('code', otp)} - renderInput={(props) => } + renderInput={(props) => + + } />
diff --git a/src/pages/financial/components/Legal.tsx b/src/pages/financial/components/Legal.tsx index 94bb8be..b8678d5 100644 --- a/src/pages/financial/components/Legal.tsx +++ b/src/pages/financial/components/Legal.tsx @@ -4,7 +4,7 @@ import Input from '../../../components/Input' import { useFormik } from 'formik' import Textarea from '../../../components/Textarea' import Button from '../../../components/Button' -import { TickCircle } from 'iconsax-react' +import { InfoCircle, TickCircle } from 'iconsax-react' import { CreateLegalUserType, ProvinesItemType } from '../types/FinancialTypes' import * as Yup from 'yup' import { useCreateLegalUser, useGetCities, useGetFinancialInfo, useGetProvines } from '../hooks/useFinancialData' @@ -186,6 +186,14 @@ const Legal: FC = () => { />
+
+ +
{t('financial.notice_cannot_edit')}
+
+ { !isReadOnly &&
diff --git a/src/pages/profile/Profile.tsx b/src/pages/profile/Profile.tsx index dca82e4..7e2fdb9 100644 --- a/src/pages/profile/Profile.tsx +++ b/src/pages/profile/Profile.tsx @@ -1,4 +1,4 @@ -import { FC } from 'react' +import { FC, useCallback, useState } from 'react' import { useTranslation } from 'react-i18next' import AvatarImage from '../../assets/images/avatar_image.png' import Button from '../../components/Button' @@ -7,13 +7,56 @@ import Input from '../../components/Input' import DatePickerComponent from '../../components/DatePicker' import Select from '../../components/Select' import Textarea from '../../components/Textarea' -import { useGetProfile } from './hooks/useProfileData' +import { useGetProfile, useUpdateProfile } from './hooks/useProfileData' import Username from './components/Username' +import { useDropzone } from 'react-dropzone' +import { toast } from 'react-toastify' +import { useSingleUpload } from '../ticket/hooks/useTicketData' +import { ErrorType } from '../../helpers/types' +import { UpdateProfileType } from './types/ProfileTypes' +import PageLoading from '../../components/PageLoading' const Profile: FC = () => { const { t } = useTranslation('global') + const singleUpload = useSingleUpload() + const [file, setFile] = useState() const getProfile = useGetProfile() + const updateProfile = useUpdateProfile() + + const onDrop = useCallback((acceptedFiles: File[]) => { + + if (acceptedFiles.length > 0) { + const file = acceptedFiles[0]; + if (file.type.startsWith('image/')) { + setFile(file); + const formData = new FormData() + formData.append('file', file) + singleUpload.mutate(formData, { + onSuccess: (data) => { + const params: UpdateProfileType = { + profilePic: data.data?.url + } + updateProfile.mutate(params, { + onSuccess: () => { + toast.success(t('profile.image_uploaded_successfully')) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + }, + onError: (error: ErrorType) => { + toast.error(error.response?.data?.error?.message[0]) + } + }) + } else { + toast.error(t('errors.is_not_image')) + } + } + + }, []) + const { getRootProps, getInputProps } = useDropzone({ onDrop }) return (
@@ -21,151 +64,160 @@ const Profile: FC = () => { {t('profile.account_user')}
-
-
-
-
- {t('profile.image_profile')} -
-
- {t('profile.image_profile_desc')} -
-
-
- -
- -

- {t('profile.format_image')} -

-
-
-
- -
-
-
- {t('profile.info_account')} -
-
- {t('profile.auth_after_change_info')} -
-
-
- -
- -
- + { + getProfile.isPending ? + + : +
+
+
- {t('edit')} + {t('profile.image_profile')} +
+
+ {t('profile.image_profile_desc')} +
+
+
+ +
+ +

+ {t('profile.formats')} +

{t('profile.format_image')}
+ {t('profile.max_size')} +

-
- -
- + +
+
- {t('edit')} + {t('profile.info_account')} +
+
+ {t('profile.auth_after_change_info')} +
+
+
+ +
+ +
+ +
+ {t('edit')} +
+
+
+
+ +
+ +
+ {t('edit')} +
+
-
-
-
-
-
- {t('profile.personal_information')} -
-
- {t('profile.enter_carefully_your_information')} -
-
-
- { }} - placeholder='' - defaulValue={getProfile.data?.data?.user?.birthDate} - /> +
+
+
+ {t('profile.personal_information')} +
+
+ {t('profile.enter_carefully_your_information')} +
+
+
+ { }} + placeholder='' + defaulValue={getProfile.data?.data?.user?.birthDate} + /> -
- -
-
-
- -
-
-
- {t('profile.address')} -
-
- {t('profile.address_live')} -
-
-
-
- +
+ +
+
-
- -
-
-