profile ,footer , ...

This commit is contained in:
hamid zarghami
2025-02-19 14:51:12 +03:30
parent f53b246905
commit 43af6ba3e6
12 changed files with 284 additions and 166 deletions
+2
View File
@@ -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()
@@ -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(),
});
};
@@ -0,0 +1,6 @@
import axios from "../../../config/axios";
export const getAnnoncements = async () => {
const { data } = await axios.get(`/announcements/user`);
return data;
};
+30 -2
View File
@@ -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 (
<div>
<div className='mt-5'>
@@ -78,7 +100,13 @@ const LoginStep2: FC = () => {
numInputs={5}
inputType='tel'
onChange={(otp: string) => formik.setFieldValue('code', otp)}
renderInput={(props) => <input {...props} className='w-full h-[50px] flex-1 mx-2 bg-white border rounded-2.5' />}
renderInput={(props) =>
<input
{...props}
autoComplete="one-time-code"
inputMode="numeric"
className='w-full h-[50px] flex-1 mx-2 bg-white border rounded-2.5' />
}
/>
</div>
+9 -1
View File
@@ -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 = () => {
/>
</div>
<div className='mt-8 h-10 text-xs bg-[#FFF2F2] text-[#DB0105] rounded-xl px-4 flex gap-2 items-center'>
<InfoCircle
size={18}
color='#DB0105'
/>
<div>{t('financial.notice_cannot_edit')}</div>
</div>
{
!isReadOnly &&
<div className='mt-8 flex justify-end'>
+186 -134
View File
@@ -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<File>()
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 (
<div className='mt-4 '>
@@ -21,151 +64,160 @@ const Profile: FC = () => {
{t('profile.account_user')}
</div>
<div className='bg-white rounded-3xl xl:p-6 p-4 mt-8 text-sm'>
<div className='xl:mt-10 mt-4 flex xl:flex-row flex-col xl:gap-0 gap-7 xl:items-center border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.image_profile')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.image_profile_desc')}
</div>
</div>
<div className='flex-1 flex xl:gap-6 gap-4'>
<img src={AvatarImage} className='xl:size-20 size-14 rounded-full object-cover' />
<div>
<Button
className='xl:w-[160px] w-fit px-4 xl:;px-0 h-8 xl:h-10 text-xs xl:text-sm'
>
<div className='flex items-center gap-3'>
<DocumentUpload color='white' size={18} />
<div>{t('profile.upload_image')}</div>
</div>
</Button>
<p className='mt-3 text-description'>
{t('profile.format_image')}
</p>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7 border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.info_account')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.auth_after_change_info')}
</div>
</div>
<div className='flex-1 '>
<Username
username={getProfile.data?.data?.user?.userName}
/>
<div className='flex items-end xl:gap-6 gap-3 xl:mt-7 mt-4'>
<Input
label={t('email')}
value={getProfile.data?.data?.user?.email}
readOnly
/>
<div className='flex relative -top-3 gap-1 text-[#0047FF] text-xs items-center'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
{
getProfile.isPending ?
<PageLoading />
:
<div className='bg-white rounded-3xl xl:p-6 p-4 mt-8 text-sm'>
<div className='xl:mt-10 mt-4 flex xl:flex-row flex-col xl:gap-0 gap-7 xl:items-center border-b pb-7'>
<div className='flex-1'>
<div>
{t('edit')}
{t('profile.image_profile')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.image_profile_desc')}
</div>
</div>
<div className='flex-1 flex xl:gap-6 gap-4'>
<img src={file ? URL.createObjectURL(file) : getProfile.data?.data?.user?.profilePic ? getProfile.data?.data?.user?.profilePic : AvatarImage} className='xl:size-20 size-14 rounded-full object-cover' />
<div>
<Button
isLoading={singleUpload.isPending || updateProfile.isPending}
className='xl:w-[160px] w-fit px-4 xl:;px-0 h-8 xl:h-10 text-xs xl:text-sm'
>
<input {...getInputProps()} />
<div {...getRootProps()} className='flex items-center gap-3'>
<DocumentUpload color='white' size={18} />
<div>{t('profile.upload_image')}</div>
</div>
</Button>
<p className='mt-3 items-center flex gap-0.5 text-description'>
{t('profile.formats')}
<div className='-mt-0.5'>{t('profile.format_image')}</div>
<span>{t('profile.max_size')}</span>
</p>
</div>
</div>
</div>
<div className='flex items-end xl:gap-6 gap-3 xl:mt-7 mt-4'>
<Input
label={t('profile.phone_call')}
value={getProfile.data?.data?.user?.phone}
readOnly
/>
<div className='flex relative -top-3 gap-1 text-[#0047FF] text-xs items-center'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7 border-b pb-7'>
<div className='flex-1'>
<div>
{t('edit')}
{t('profile.info_account')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.auth_after_change_info')}
</div>
</div>
<div className='flex-1 '>
<Username
username={getProfile.data?.data?.user?.userName}
/>
<div className='flex items-end xl:gap-6 gap-3 xl:mt-7 mt-4'>
<Input
label={t('email')}
value={getProfile.data?.data?.user?.email}
readOnly
/>
<div className='flex relative -top-3 gap-1 text-[#0047FF] text-xs items-center'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
<div>
{t('edit')}
</div>
</div>
</div>
<div className='flex items-end xl:gap-6 gap-3 xl:mt-7 mt-4'>
<Input
label={t('profile.phone_call')}
value={getProfile.data?.data?.user?.phone}
readOnly
/>
<div className='flex relative -top-3 gap-1 text-[#0047FF] text-xs items-center'>
<Edit className='xl:size-[18px] size-4' color='#0047FF' />
<div>
{t('edit')}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7 border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.personal_information')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.enter_carefully_your_information')}
</div>
</div>
<div className='flex-1'>
<DatePickerComponent
label={t('profile.date_of_birth')}
onChange={() => { }}
placeholder=''
defaulValue={getProfile.data?.data?.user?.birthDate}
/>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7 border-b pb-7'>
<div className='flex-1'>
<div>
{t('profile.personal_information')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.enter_carefully_your_information')}
</div>
</div>
<div className='flex-1'>
<DatePickerComponent
label={t('profile.date_of_birth')}
onChange={() => { }}
placeholder=''
defaulValue={getProfile.data?.data?.user?.birthDate}
/>
<div className='xl:mt-7 mt-4'>
<Input
label={t('profile.national_code')}
value={getProfile.data?.data?.user?.nationalCode}
/>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7'>
<div className='flex-1'>
<div>
{t('profile.address')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.address_live')}
</div>
</div>
<div className='flex-1'>
<div className='rowTwoInput'>
<Select
label={t('profile.province')}
items={[
{
label: 'تهران',
value: 'tehran'
}
]}
className='bg-white border'
/>
<Select
label={t('profile.city')}
items={[
{
label: 'تهران',
value: 'tehran'
}
]}
/>
<div className='xl:mt-7 mt-4'>
<Input
label={t('profile.national_code')}
value={getProfile.data?.data?.user?.nationalCode}
/>
</div>
</div>
</div>
<div className='xl:mt-7 mt-4'>
<Input
label={t('profile.postal_code')}
value={'۱۲۳۴۵۶۷۸'}
/>
</div>
<div className='xl:mt-7 mt-4'>
<Textarea
label={t('profile.address')}
value={'للورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک استورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است'}
/>
</div>
</div>
</div>
<div className='mt-8 flex xl:flex-row flex-col xl:gap-0 gap-7'>
<div className='flex-1'>
<div>
{t('profile.address')}
</div>
<div className='text-description text-xs mt-2'>
{t('profile.address_live')}
</div>
</div>
<div className='flex-1'>
<div className='rowTwoInput'>
<Select
label={t('profile.province')}
items={[
{
label: 'تهران',
value: 'tehran'
}
]}
className='bg-white border'
/>
<Select
label={t('profile.city')}
items={[
{
label: 'تهران',
value: 'tehran'
}
]}
/>
</div>
<div className='h-20 xl:hidden'></div>
</div>
<div className='xl:mt-7 mt-4'>
<Input
label={t('profile.postal_code')}
value={'۱۲۳۴۵۶۷۸'}
/>
</div>
<div className='xl:mt-7 mt-4'>
<Textarea
label={t('profile.address')}
value={'للورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک استورم ایپسوم متن ساختگی با تولید سادگی نامفهوم از صنعت چاپ و با استفاده از طراحان گرافیک است'}
/>
</div>
</div>
</div>
<div className='h-20 xl:hidden'></div>
</div>
}
</div>
)
+1
View File
@@ -8,4 +8,5 @@ export type UpdateProfileType = {
lastName?: string;
birthDate?: string;
userName?: string;
profilePic?: string;
};
+1 -1
View File
@@ -224,7 +224,7 @@ const CreateTicket: FC = () => {
<TickSquare size={20} color='black' variant='Bold' />
</div>
<div className='text-description text-xs leading-5'>
پاسخگویی 24 ساعته تلفنی را تنها از میهن وب هاست می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
پاسخگویی 24 ساعته تلفنی را تنها از داناک می توانید انتظار داشته باشید .بخش پشتیبانی در هر ساعتی حتی در روز های تعطیل آماده پیگیری سریع مشکلات کاربران است.
</div>
</div>
</div>