From 1106952e2257395e14253ff0a202f27a5c276d02 Mon Sep 17 00:00:00 2001 From: hamid zarghami Date: Sun, 20 Jul 2025 12:59:04 +0330 Subject: [PATCH] change password + setting + notification + header --- src/components/Switch.tsx | 43 +++++ src/components/TitleLine.tsx | 18 +++ src/langs/fa.json | 79 +++------ src/pages/notification/Notification.tsx | 6 +- src/pages/setting/Setting.tsx | 101 ++++++++++++ .../setting/components/ChangePassword.tsx | 152 ++++++++++++++++++ src/pages/setting/components/Email.tsx | 27 ++++ src/pages/setting/components/Sms.tsx | 33 ++++ src/pages/setting/hooks/useSettingData.ts | 23 +++ src/pages/setting/service/SettingService.ts | 15 ++ src/pages/setting/types/SettingTypes.ts | 31 ++++ src/router/AppRouter.tsx | 2 + 12 files changed, 470 insertions(+), 60 deletions(-) create mode 100644 src/components/Switch.tsx create mode 100644 src/components/TitleLine.tsx create mode 100644 src/pages/setting/Setting.tsx create mode 100644 src/pages/setting/components/ChangePassword.tsx create mode 100644 src/pages/setting/components/Email.tsx create mode 100644 src/pages/setting/components/Sms.tsx create mode 100644 src/pages/setting/hooks/useSettingData.ts create mode 100644 src/pages/setting/service/SettingService.ts create mode 100644 src/pages/setting/types/SettingTypes.ts diff --git a/src/components/Switch.tsx b/src/components/Switch.tsx new file mode 100644 index 0000000..36e8956 --- /dev/null +++ b/src/components/Switch.tsx @@ -0,0 +1,43 @@ +import { Fragment } from 'react' +import { Switch } from '@headlessui/react' + +interface Props { + active: boolean, + onChange: (value: boolean) => void, + isLoading?: boolean, + label?: string, +} + +const SwitchComponent = (props: Props) => { + + const handleChange = (value: boolean) => { + props.onChange(value) + } + + return ( +
+ + {({ checked }) => ( + + )} + + { + props.label && +
+ : {props.label} +
+ } +
+ ) +} + +export default SwitchComponent diff --git a/src/components/TitleLine.tsx b/src/components/TitleLine.tsx new file mode 100644 index 0000000..8c9c9ea --- /dev/null +++ b/src/components/TitleLine.tsx @@ -0,0 +1,18 @@ +import { FC } from 'react' + +type Props = { + title: string, +} + +const TitleLine: FC = (props: Props) => { + return ( +
+
{props.title}
+ + + +
+ ) +} + +export default TitleLine \ No newline at end of file diff --git a/src/langs/fa.json b/src/langs/fa.json index 21522e9..669e14b 100644 --- a/src/langs/fa.json +++ b/src/langs/fa.json @@ -70,65 +70,30 @@ "all_read": "خواندن همه" }, "setting": { - "title": "تنظیمات", - "personality": "شخصی سازی", - "mail_server": "تنظیمات میل سرور", - "domain": "تنظیمات دامنه", - "address": "نشانی ها", "setting": "تنظیمات", - "background": "رنگ زمینه", - "upload_background": "تصویر پس زمینه مورد نظر را آپلود کنید", - "size": "سایز", - "horizontal_position": "موقعیت افقی", - "vertical_position": "موقعیت عمودی", - "text": "متن", - "add_text": "اضافه کردن متن", - "button": "دکمه", - "button_text": "متن دکمه", - "button_link": "لینک", - "select": "انتخاب", - "border": "حاشیه", - "text_color": "رنگ متن", - "background_color": "رنگ زمینه", - "border_color": "رنگ حاشیه", - "add_button": "اضافه کردن دکمه", - "image": "تصویر", - "upload_image": "تصویر مورد نظر را آپلود کنید", - "add_image": "اضافه کردن تصویر", - "setting_fetch": "تنظیمات فچ", - "setting_fetch_description": "تنظیمات فچ خود را با دقت انتخاب کنید.", - "setting_smtp": "تنظیمات SMTP", - "setting_smtp_description": "تنظیمات SMTP خود را با دقت وارد کنید.", - "smtp_server": "SMTP سرور", - "smtp_port": "SMTP پورت", - "username": "نام کاربری", - "password": "پسورد", - "record_dns": "رکوردهای DNS", - "record_dns_description": "در این قسمت میتوانید وضعیت رکوردهای DNS سرویس ایمیل خود را بررسی کنید.", - "your_domain": "دامنه شما", - "submit": "ثبت", - "status": "وضعیت", - "search": "جستجو", - "address_title": "عنوان نشانی", + "notification": "نوتیفیکیشن", + "password": "رمز عبور", + "setting_email_message": "تنظیمات نوتیفیکیشن", + "notification_announcement": "اعلانات و اطلاعیه ها", "email": "ایمیل", - "marketing": "بازاریابی", - "actions": "عملیات", - "delete": "حذف کردن ایمیل", - "active": "فعال", - "inactive": "غیرفعال", - "add_address": "اضافه کردن نشانی", - "domain1": "دامنه", - "create": "ساخت", - "header_email": "هدر ایمیل شما", - "content_email": "محتوای ایمیل شما", - "footer_email": "پاورقی ایمیل شما", - "signature": "امضا", - "signature_description": "تصویر و توضیحات امضای خود را با دقت وارد کنید", - "upload_sign": "آپلود امضا", - "description_sign": "توضیحات امضا", - "select_section": "لطفا یک بخش انتخاب کنید", - "title1": "عنوان", - "quota": "فضا/ استفاده شده " + "sms": "پیامک", + "change_password": "تغییر رمز عبور", + "current_password": "رمز عبور فعلی", + "new_password": "رمز عبور جدید", + "reapeat_password": "تکرار رمز عبور", + "save_change": "ذخیره تغییرات", + "password_is": "رمز عبور می‌بایست:", + "min-8": "حداقل 8 کاراکتر باشد", + "mix_charachter_number": "ترکیبی از حروف کوچک و بزرگ باشد", + "exsist_number": "شامل اعداد باشد", + "exsist_special_character": "شامل کاراکتر های خاص (نماد ها) باشد", + "password_changed": "رمز عبور با موفقیت تغییر یافت", + "SUPPORT": "پشتیبانی", + "SERVICE": "سرویس", + "INVOICE": "صورتحساب", + "ACCOUNT": "حساب", + "NOTIFICATION": "اعلان", + "FINANCE": "مالی" }, "attachment_select": "اضافه کردن فایل ضمیمه", "domain": { diff --git a/src/pages/notification/Notification.tsx b/src/pages/notification/Notification.tsx index 4c0be16..3b085c3 100644 --- a/src/pages/notification/Notification.tsx +++ b/src/pages/notification/Notification.tsx @@ -82,9 +82,9 @@ const Notifications: FC = () => {
setShowModal(!showModal)} className='relative cursor-pointer'> - {/*
- {getDashboard.data?.data?.notificationCount} -
*/} +
+ {data?.pages[0]?.data?.notificationCount} +
{showModal && ( diff --git a/src/pages/setting/Setting.tsx b/src/pages/setting/Setting.tsx new file mode 100644 index 0000000..5768862 --- /dev/null +++ b/src/pages/setting/Setting.tsx @@ -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('notification') + + return ( +
+
+ {t('setting.setting')} +
+ +
+ , + label: t('setting.notification'), + value: 'notification' + }, + { + icon: , + label: t('setting.password'), + value: 'password' + }, + ]} + active={activeTab} + onChange={setActiveTab} + /> +
+ + { + activeTab === 'notification' ? +
+
+ {t('setting.setting_email_message')} +
+ +
+ { + getSettings.isPending ? +
+
+ : + getSettings.data?.data?.settings?.map((item: SettingCategoryType) => { + return ( + + + { + item.settings?.map((settingItem: SettingItemType) => { + return ( +
+
+ {settingItem?.description} +
+
+ {/*
+
+ {t('setting.email')} +
+ +
*/} +
+ +
+
+
+ ) + }) + } +
+ ) + }) + } +
+
+
+ : + + } + +
+ ) +} + +export default Setting \ No newline at end of file diff --git a/src/pages/setting/components/ChangePassword.tsx b/src/pages/setting/components/ChangePassword.tsx new file mode 100644 index 0000000..3cf3b77 --- /dev/null +++ b/src/pages/setting/components/ChangePassword.tsx @@ -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({ + 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 ( +
+
+ {t('setting.change_password')} +
+ +
+
+ + + + +
+ +
+
+ +
+
+ {t('setting.password_is')} +
+
    +
  • + {t('setting.min-8')} +
  • +
  • + {t('setting.mix_charachter_number')} +
  • +
  • + {t('setting.exsist_number')} +
  • +
  • + {t('setting.exsist_special_character')} +
  • +
+
+ +
+ +
+ +
+ +
+
+ +
+
+ ) +} + +export default ChangePassword \ No newline at end of file diff --git a/src/pages/setting/components/Email.tsx b/src/pages/setting/components/Email.tsx new file mode 100644 index 0000000..958fb1d --- /dev/null +++ b/src/pages/setting/components/Email.tsx @@ -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) => { + const updateSetting = useUpdateSetting() + const [email, setEmail] = useState(props.email) + + const handleChange = (value: boolean) => { + setEmail(value) + updateSetting.mutate({ email: value, id: props.id }) + } + + return ( + + ) +} + +export default Email \ No newline at end of file diff --git a/src/pages/setting/components/Sms.tsx b/src/pages/setting/components/Sms.tsx new file mode 100644 index 0000000..eb16fa1 --- /dev/null +++ b/src/pages/setting/components/Sms.tsx @@ -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) => { + const updateSetting = useUpdateSetting() + const [sms, setSms] = useState(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 ( + + ) +} + +export default Sms \ No newline at end of file diff --git a/src/pages/setting/hooks/useSettingData.ts b/src/pages/setting/hooks/useSettingData.ts new file mode 100644 index 0000000..00811b1 --- /dev/null +++ b/src/pages/setting/hooks/useSettingData.ts @@ -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), + }); +}; diff --git a/src/pages/setting/service/SettingService.ts b/src/pages/setting/service/SettingService.ts new file mode 100644 index 0000000..5c4dd5c --- /dev/null +++ b/src/pages/setting/service/SettingService.ts @@ -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); +}; diff --git a/src/pages/setting/types/SettingTypes.ts b/src/pages/setting/types/SettingTypes.ts new file mode 100644 index 0000000..a8bd792 --- /dev/null +++ b/src/pages/setting/types/SettingTypes.ts @@ -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[]; + }; +}; diff --git a/src/router/AppRouter.tsx b/src/router/AppRouter.tsx index 9c111ea..bc0c8c4 100644 --- a/src/router/AppRouter.tsx +++ b/src/router/AppRouter.tsx @@ -11,6 +11,7 @@ import SpamList from '@/pages/spam/List' import SearchList from '@/pages/search/List' import DetailEmail from '@/pages/received/Detail' import Login from '@/pages/auth/Login' +import Setting from '@/pages/setting/Setting' const AppRouter: FC = () => { return ( @@ -26,6 +27,7 @@ const AppRouter: FC = () => { } /> } /> } /> + } /> ) }