change password + setting + notification + header

This commit is contained in:
hamid zarghami
2025-07-20 12:59:04 +03:30
parent 4f3976bfe8
commit 1106952e22
12 changed files with 470 additions and 60 deletions
+3 -3
View File
@@ -82,9 +82,9 @@ const Notifications: FC = () => {
<div ref={ref}>
<div onClick={() => setShowModal(!showModal)} className='relative cursor-pointer'>
<Notification size={18} color="black" />
{/* <div className="absolute top-0 right-0 -mt-1 -mr-1 rounded-full bg-red-500 text-white text-[8px] size-3 flex pt-0.5 pl-[1px] justify-center items-center">
{getDashboard.data?.data?.notificationCount}
</div> */}
<div className="absolute top-0 right-0 -mt-1 -mr-1 rounded-full bg-red-500 text-white text-[8px] size-3 flex pt-0.5 pl-[1px] justify-center items-center">
{data?.pages[0]?.data?.notificationCount}
</div>
</div>
{showModal && (
+101
View File
@@ -0,0 +1,101 @@
import { FC, Fragment, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Tabs from '../../components/Tabs'
import { KeySquare, Notification1 } from 'iconsax-react'
import { useGetSettings } from './hooks/useSettingData'
import Sms from './components/Sms'
import ChangePassword from './components/ChangePassword'
import TitleLine from '@/components/TitleLine'
import { SettingCategoryType, SettingItemType } from './types/SettingTypes'
const Setting: FC = () => {
const { t } = useTranslation()
const getSettings = useGetSettings()
const [activeTab, setActiveTab] = useState<string>('notification')
return (
<div className='mt-4'>
<div>
{t('setting.setting')}
</div>
<div className='mt-10'>
<Tabs
items={[
{
icon: <Notification1 color={activeTab === 'notification' ? 'black' : '#8C90A3'} size={22} />,
label: t('setting.notification'),
value: 'notification'
},
{
icon: <KeySquare color={activeTab === 'password' ? 'black' : '#8C90A3'} size={22} />,
label: t('setting.password'),
value: 'password'
},
]}
active={activeTab}
onChange={setActiveTab}
/>
</div>
{
activeTab === 'notification' ?
<div className='mt-10 bg-white xl:p-8 p-4 rounded-3xl'>
<div>
{t('setting.setting_email_message')}
</div>
<div className='flex xl:flex-row flex-wrap flex-col gap-6 text-sm mt-8'>
{
getSettings.isPending ?
<div className='w-full'>
</div>
:
getSettings.data?.data?.settings?.map((item: SettingCategoryType) => {
return (
<Fragment key={item.category}>
<TitleLine
title={t(`setting.${item.category}`)}
/>
{
item.settings?.map((settingItem: SettingItemType) => {
return (
<div key={settingItem.id} className='flex-1 min-w-[40%] border border-border px-4 rounded-2xl min-h-14 items-center flex justify-between'>
<div className='xl:text-[13px] text-xs'>
{settingItem?.description}
</div>
<div className='flex xl:gap-4 gap-2'>
{/* <div className='flex gap-2 text-xs items-center text-description'>
<div>
{t('setting.email')}
</div>
<Email id={settingItem.id} email={settingItem.email} />
</div> */}
<div className='flex gap-2 text-xs items-center text-description'>
<Sms
sms={settingItem.isActive}
id={settingItem.id}
/>
</div>
</div>
</div>
)
})
}
</Fragment>
)
})
}
<div className='flex-1 px-4 min-w-[40%]'></div>
</div>
</div>
:
<ChangePassword />
}
</div>
)
}
export default Setting
@@ -0,0 +1,152 @@
import { FC } from 'react'
import { useTranslation } from 'react-i18next'
import Input from '../../../components/Input'
import Button from '../../../components/Button'
import { useFormik } from 'formik'
import { ChangePasswordType } from '../types/SettingTypes'
import * as Yup from 'yup'
import { toast } from '../../../components/Toast'
import { useChangePassword } from '../hooks/useSettingData'
import { ErrorType } from '../../../helpers/types'
import { TickCircle } from 'iconsax-react'
const ChangePassword: FC = () => {
const changePassword = useChangePassword()
const { t } = useTranslation('global')
const formik = useFormik<ChangePasswordType>({
initialValues: {
oldPassword: '',
newPassword: '',
repeatPassword: ''
},
validationSchema: Yup.object({
oldPassword: Yup.string()
.required(t('errors.required')),
newPassword: Yup.string()
.required(t('errors.required')),
repeatPassword: Yup.string()
.required(t('errors.required')),
}),
onSubmit: (values) => {
if (values.newPassword !== values.repeatPassword) {
toast(t('errors.password_not_match'), 'error')
} else {
changePassword.mutate(values, {
onSuccess: () => {
toast(t('setting.password_changed'), 'success')
formik.resetForm()
},
onError: (error: ErrorType) => {
toast(error.response?.data?.error?.message[0], 'error')
}
})
}
}
})
return (
<div className='mt-10 bg-white p-8 rounded-3xl'>
<div>
{t('setting.change_password')}
</div>
<div className='flex xl:flex-row flex-col xl:gap-10 gap-4'>
<div className='flex-1'>
<Input
type='password'
className='mt-6'
placeholder={t('setting.current_password')}
name='oldPassword'
onChange={formik.handleChange}
error_text={formik.touched.oldPassword && formik.errors.oldPassword ? formik.errors.oldPassword : ''}
/>
<Input
type='password'
className='mt-6'
placeholder={t('setting.new_password')}
name='newPassword'
onChange={formik.handleChange}
error_text={formik.touched.newPassword && formik.errors.newPassword ? formik.errors.newPassword : ''}
/>
<Input
type='password'
className='mt-6'
placeholder={t('setting.reapeat_password')}
name='repeatPassword'
onChange={formik.handleChange}
error_text={formik.touched.repeatPassword && formik.errors.repeatPassword ? formik.errors.repeatPassword : ''}
/>
<div className='mt-9 xl:block hidden'>
<Button
className='w-fit px-4'
onClick={() => formik.handleSubmit()}
>
<div className='flex gap-2 items-center'>
<TickCircle size={20} color='white' />
<div>
{t('setting.save_change')}
</div>
</div>
</Button>
</div>
</div>
<div className='flex-1 xl:p-8 p-4 bg-[#EEF0F7] rounded-3xl text-sm'>
<div>
{t('setting.password_is')}
</div>
<ul className='list-disc ps-4 flex flex-col gap-2 text-xs mt-4'>
<li>
{t('setting.min-8')}
</li>
<li>
{t('setting.mix_charachter_number')}
</li>
<li>
{t('setting.exsist_number')}
</li>
<li>
{t('setting.exsist_special_character')}
</li>
</ul>
</div>
<div className=' xl:hidden block'>
<Button
className='w-32 hidden xl:block'
onClick={() => formik.handleSubmit()}
>
<div className='flex gap-2 items-center'>
<TickCircle size={20} color='white' />
<div>
{t('setting.save_change')}
</div>
</div>
</Button>
</div>
<div className='justify-end xl:hidden flex'>
<Button
className='xl:w-fit px-10'
onClick={() => formik.handleSubmit()}
loading={changePassword.isPending}
>
<div className='flex justify-center gap-2 items-center'>
<TickCircle size={20} color='white' />
<div>
{t('setting.save_change')}
</div>
</div>
</Button>
</div>
</div>
<div className='h-14'></div>
</div>
)
}
export default ChangePassword
+27
View File
@@ -0,0 +1,27 @@
import { FC, useState } from 'react'
import SwitchComponent from '../../../components/Switch'
import { useUpdateSetting } from '../hooks/useSettingData'
type Props = {
email: boolean,
id: string
}
const Email: FC<Props> = (props: Props) => {
const updateSetting = useUpdateSetting()
const [email, setEmail] = useState<boolean>(props.email)
const handleChange = (value: boolean) => {
setEmail(value)
updateSetting.mutate({ email: value, id: props.id })
}
return (
<SwitchComponent
active={email}
onChange={handleChange}
/>
)
}
export default Email
+33
View File
@@ -0,0 +1,33 @@
import { FC, useState } from 'react'
import SwitchComponent from '@/components/Switch'
import { useUpdateSetting } from '../hooks/useSettingData'
import { ErrorType } from '../../../helpers/types'
import { toast } from '../../../components/Toast'
type Props = {
sms: boolean,
id: string
}
const Sms: FC<Props> = (props: Props) => {
const updateSetting = useUpdateSetting()
const [sms, setSms] = useState<boolean>(props.sms)
const handleChange = (value: boolean) => {
setSms(value)
updateSetting.mutate({ sms: value, id: props.id }, {
onError: (error: ErrorType) => {
toast(error.response?.data?.error?.message[0], 'error')
}
})
}
return (
<SwitchComponent
active={sms}
onChange={handleChange}
/>
)
}
export default Sms
+23
View File
@@ -0,0 +1,23 @@
import { useMutation, useQuery } from "@tanstack/react-query";
import * as api from "../service/SettingService";
import { ChangePasswordType, UpdateSettingType } from "../types/SettingTypes";
export const useGetSettings = () => {
return useQuery({
queryKey: ["settings"],
queryFn: () => api.getSettings(),
});
};
export const useUpdateSetting = () => {
return useMutation({
mutationFn: (variables: UpdateSettingType) => api.updateSettings(variables),
});
};
export const useChangePassword = () => {
return useMutation({
mutationFn: (variables: ChangePasswordType) =>
api.changePassword(variables),
});
};
@@ -0,0 +1,15 @@
import axios from "../../../config/axios";
import { ChangePasswordType, UpdateSettingType } from "../types/SettingTypes";
export const getSettings = async () => {
const { data } = await axios.get(`/settings`);
return data;
};
export const updateSettings = async (params: UpdateSettingType) => {
await axios.patch(`/settings/${params.id}/toggle`, params);
};
export const changePassword = async (params: ChangePasswordType) => {
await axios.patch(`/auth/change-password`, params);
};
+31
View File
@@ -0,0 +1,31 @@
export type UpdateSettingType = {
id: string;
email?: boolean;
sms?: boolean;
};
export type ChangePasswordType = {
oldPassword: string;
newPassword: string;
repeatPassword: string;
};
export type SettingItemType = {
id: string;
type: string;
isActive: boolean;
description: string;
};
export type SettingCategoryType = {
category: string;
settings: SettingItemType[];
};
export type SettingsResponseType = {
statusCode: number;
success: boolean;
data: {
settings: SettingCategoryType[];
};
};