settiig
This commit is contained in:
+13
-10
@@ -1,13 +1,16 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Vite + React + TS</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>داناک - dzone</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.tsx"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
Vendored
BIN
Binary file not shown.
+6
-3
@@ -69,7 +69,8 @@
|
||||
"login_with_otp": "ورود با رمز عبور یکبار مصرف",
|
||||
"mobile_number": "شماره موبایل",
|
||||
"enter_mobile_number": "شماره موبایل خود را وارد کنید",
|
||||
"enter_verify_code": "کد تایید خود را وارد کنید"
|
||||
"enter_verify_code": "کد تایید خود را وارد کنید",
|
||||
"otp_sent": "کد تایید برای شما ارسال شد"
|
||||
},
|
||||
"errors": {
|
||||
"required": "این فیلد اجباری می باشد"
|
||||
@@ -320,7 +321,8 @@
|
||||
"notification": "پیام و ایمیل",
|
||||
"password": "رمز عبور",
|
||||
"setting_email_message": "تنظیمات ارسال پیام و ایمیل",
|
||||
"notification_announcement": "اعلانات و اطلاعیه ها",
|
||||
"activity_notifications": "اعلانهای فعالیت",
|
||||
"system_notifications": "اعلانهای سیستمی",
|
||||
"email": "ایمیل",
|
||||
"sms": "پیامک",
|
||||
"change_password": "تغییر رمز عبور",
|
||||
@@ -332,7 +334,8 @@
|
||||
"min-8": "حداقل 8 کاراکتر باشد",
|
||||
"mix_charachter_number": "ترکیبی از حروف کوچک و بزرگ باشد",
|
||||
"exsist_number": "شامل اعداد باشد",
|
||||
"exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد"
|
||||
"exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد",
|
||||
"password_changed": "رمز عبور با موفقیت تغییر کرد"
|
||||
},
|
||||
"wallet": {
|
||||
"increese_wallet": "افزایش موجودی حساب اعتباری",
|
||||
|
||||
@@ -19,10 +19,10 @@ const CompanyDetail: FC = () => {
|
||||
<div className='flex gap-3'>
|
||||
<img
|
||||
src={data?.data?.company?.profileImageUrl}
|
||||
className='size-16 rounded-xl object-cover'
|
||||
className='size-14 rounded-xl object-cover'
|
||||
/>
|
||||
<div className='flex-1 items-center flex justify-between '>
|
||||
<div className='text-sm'>
|
||||
<div className='text-xs'>
|
||||
{data?.data?.company?.name}
|
||||
</div>
|
||||
<a href={data?.data?.company?.mapAddressLink} target='_blank' className='flex items-center gap-1.5 text-xs'>
|
||||
@@ -30,14 +30,14 @@ const CompanyDetail: FC = () => {
|
||||
size={20}
|
||||
color='black'
|
||||
/>
|
||||
<div>
|
||||
<div className='text-xs'>
|
||||
آدرس در نقشه
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className='mt-5 text-xs'>
|
||||
<div className='mt-5 text-[10px]'>
|
||||
<div className='flex justify-between'>
|
||||
<div className='flex gap-1 items-center'>
|
||||
<Profile
|
||||
|
||||
@@ -0,0 +1,131 @@
|
||||
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 PageLoading from '../../components/PageLoading'
|
||||
import Sms from './components/Sms'
|
||||
import ChangePassword from './components/ChangePassword'
|
||||
import TitleLine from '../../components/TitleLine'
|
||||
|
||||
// دادههای استاتیک برای نمایش در صفحه
|
||||
const mockSettings = {
|
||||
data: {
|
||||
settings: [
|
||||
{
|
||||
id: '1',
|
||||
category: 'activity_notifications',
|
||||
settings: [
|
||||
{
|
||||
id: '101',
|
||||
description: 'اطلاعرسانی فعالیتهای جدید',
|
||||
isActive: true
|
||||
},
|
||||
{
|
||||
id: '102',
|
||||
description: 'اعلانهای مربوط به تیکتها',
|
||||
isActive: false
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
category: 'system_notifications',
|
||||
settings: [
|
||||
{
|
||||
id: '201',
|
||||
description: 'بهروزرسانیهای سیستم',
|
||||
isActive: true
|
||||
},
|
||||
{
|
||||
id: '202',
|
||||
description: 'اخبار و اطلاعیهها',
|
||||
isActive: true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
};
|
||||
|
||||
const Setting: FC = () => {
|
||||
|
||||
const { t } = useTranslation('global')
|
||||
// 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'>
|
||||
{
|
||||
mockSettings.data.settings.map((item: any) => {
|
||||
return (
|
||||
<Fragment key={item.id}>
|
||||
<TitleLine
|
||||
title={t(`setting.${item.category}`)}
|
||||
/>
|
||||
{
|
||||
item.settings?.map((setting: any) => {
|
||||
return (
|
||||
<div key={setting.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'>
|
||||
{setting?.description}
|
||||
</div>
|
||||
<div className='flex xl:gap-4 gap-2'>
|
||||
<div className='flex gap-2 text-xs items-center text-description'>
|
||||
<Sms
|
||||
sms={setting.isActive}
|
||||
id={setting.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,155 @@
|
||||
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: {
|
||||
password: '',
|
||||
newPassword: '',
|
||||
repeatPassword: ''
|
||||
},
|
||||
validationSchema: Yup.object({
|
||||
password: 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 {
|
||||
// API هنوز ایجاد نشده، بنابراین فقط پیام نمایش میدهیم
|
||||
toast(t('setting.password_changed'), 'success')
|
||||
formik.resetForm()
|
||||
// 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='password'
|
||||
onChange={formik.handleChange}
|
||||
error_text={formik.touched.password && formik.errors.password ? formik.errors.password : ''}
|
||||
/>
|
||||
<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()}
|
||||
// isLoading={changePassword.isPending}
|
||||
>
|
||||
<div className='flex 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
|
||||
@@ -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
|
||||
@@ -0,0 +1,35 @@
|
||||
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)
|
||||
// فعلا API نداریم پس فقط توست نمایش میدهیم
|
||||
toast("تنظیمات با موفقیت ذخیره شد", 'success')
|
||||
// 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
|
||||
@@ -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);
|
||||
};
|
||||
@@ -0,0 +1,11 @@
|
||||
export type UpdateSettingType = {
|
||||
id: string;
|
||||
email?: boolean;
|
||||
sms?: boolean;
|
||||
};
|
||||
|
||||
export type ChangePasswordType = {
|
||||
password: string;
|
||||
newPassword: string;
|
||||
repeatPassword: string;
|
||||
};
|
||||
@@ -26,6 +26,7 @@ import TicketDetail from "../pages/ticket/Detail"
|
||||
import AnnouncementtList from "../pages/annoncement/List"
|
||||
import AnnouncementDetail from "../pages/annoncement/Detail"
|
||||
import MyCompany from "../pages/company/MyCompany"
|
||||
import Setting from "../pages/setting/Setting"
|
||||
const MainRouter = () => {
|
||||
const { slug } = useParams()
|
||||
const getSlugId = useGetSlugId()
|
||||
@@ -79,6 +80,7 @@ const MainRouter = () => {
|
||||
<Route path="tickets/messages/:id" element={<TicketDetail />} />
|
||||
<Route path="announcement" element={<AnnouncementtList />} />
|
||||
<Route path="announcement/:id" element={<AnnouncementDetail />} />
|
||||
<Route path="setting" element={<Setting />} />
|
||||
</Routes>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { FC } from 'react'
|
||||
import LogoImage from '../assets/images/logo.svg'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { DocumentText, Home2, Logout, Messages3, NotificationStatus, Receipt21 } from 'iconsax-react'
|
||||
import { DocumentText, Home2, Logout, Messages3, NotificationStatus, Receipt21, Setting2 } from 'iconsax-react'
|
||||
import SideBarItem from './SideBarItem'
|
||||
import { useLocation } from 'react-router-dom'
|
||||
import { Pages } from '../config/Pages'
|
||||
@@ -84,6 +84,14 @@ const SideBar: FC = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className='flex-1 items-end mt-14'>
|
||||
<div className='text-xs text-[#8C90A3]'>
|
||||
<SideBarItem
|
||||
icon={<Setting2 variant={isActive('setting') ? 'Bold' : 'Outline'} color={isActive('setting') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||
title={t('sidebar.setting')}
|
||||
isActive={isActive('setting')}
|
||||
link={Pages.setting}
|
||||
/>
|
||||
</div>
|
||||
<div className='text-xs text-[#8C90A3]'>
|
||||
<SideBarItem
|
||||
icon={<Logout variant={isActive('logout') ? 'Bold' : 'Outline'} color={isActive('logout') ? 'black' : '#8C90A3'} size={iconSizeSideBar} />}
|
||||
|
||||
Reference in New Issue
Block a user