From e0e2839cf381524504a334459ed6a884700ad649 Mon Sep 17 00:00:00 2001 From: Moaz Dawalibi Date: Wed, 18 Sep 2024 13:17:48 +0300 Subject: [PATCH 1/2] settings --- src/Hooks/useWindowResize.tsx | 20 +++ src/Layout/Ui/SideBar.tsx | 2 +- src/Pages/Admin/Setting/Form/FileSetting.tsx | 42 +++++ .../Form/FileSetting/AttachmentForm.tsx | 23 +++ .../ImageBoxField/ImageBoxField.scss | 38 +++++ .../ImageBoxField/ImageBoxField.tsx | 87 ++++++++++ .../ImageBoxField/ImageCancelIcon.tsx | 18 +++ .../FileSetting/ImageBoxField/ImageIcon.tsx | 18 +++ .../ImageBoxField/generateImagePreview.ts | 10 ++ .../Form/FileSetting/PersonalDetailsForm.tsx | 52 ++++++ .../Form/FileSetting/TitleDetailsForm.tsx | 30 ++++ .../Setting/Form/FileSetting/formUtils.ts | 13 ++ src/Pages/Admin/Setting/Form/Notification.tsx | 36 +++++ .../Form/Notification/NotificationCard.tsx | 27 ++++ .../Setting/Form/Notification/formUtils.ts | 13 ++ .../Admin/Setting/Form/SecuritySetting.tsx | 29 ++++ .../Form/SecuritySetting/SecurityCard.tsx | 27 ++++ .../Form/SecuritySetting/SecurityData.tsx | 47 ++++++ src/Pages/Admin/Setting/Form/SettingTabs.tsx | 51 ++++++ src/Pages/Admin/Setting/Form/TabHeader.tsx | 29 ++++ src/Pages/Admin/Setting/Page.tsx | 31 ++++ .../Profile/Form/PersonalDetailsForm.tsx | 36 +++-- src/Routes.tsx | 15 +- src/Styles/Pages/index.scss | 3 +- src/Styles/Pages/reSeller.scss | 9 +- src/Styles/Pages/setting.scss | 153 ++++++++++++++++++ src/enums/abilities.ts | 3 +- src/faker/item.ts | 10 ++ src/translate/ar.json | 35 +++- src/types/Setting.ts | 7 + 30 files changed, 893 insertions(+), 21 deletions(-) create mode 100644 src/Hooks/useWindowResize.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.scss create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageCancelIcon.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageIcon.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/generateImagePreview.ts create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx create mode 100644 src/Pages/Admin/Setting/Form/FileSetting/formUtils.ts create mode 100644 src/Pages/Admin/Setting/Form/Notification.tsx create mode 100644 src/Pages/Admin/Setting/Form/Notification/NotificationCard.tsx create mode 100644 src/Pages/Admin/Setting/Form/Notification/formUtils.ts create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx create mode 100644 src/Pages/Admin/Setting/Form/SettingTabs.tsx create mode 100644 src/Pages/Admin/Setting/Form/TabHeader.tsx create mode 100644 src/Pages/Admin/Setting/Page.tsx create mode 100644 src/Styles/Pages/setting.scss create mode 100644 src/types/Setting.ts diff --git a/src/Hooks/useWindowResize.tsx b/src/Hooks/useWindowResize.tsx new file mode 100644 index 0000000..3585264 --- /dev/null +++ b/src/Hooks/useWindowResize.tsx @@ -0,0 +1,20 @@ +import React, { useEffect, useState } from 'react' + +export const useWindowResize = () => { + const [windowWidth, setWindowWidth] = useState(window.innerWidth); + + useEffect(() => { + window.addEventListener('resize', handleResize); + // Cleanup function to remove the event listener + return () => { + window.removeEventListener('resize', handleResize); + }; + }, [windowWidth]); + + const handleResize = () => { + setWindowWidth(window.innerWidth); + }; + + return {windowWidth , handleResize}; +} + diff --git a/src/Layout/Ui/SideBar.tsx b/src/Layout/Ui/SideBar.tsx index 17de545..56566cd 100644 --- a/src/Layout/Ui/SideBar.tsx +++ b/src/Layout/Ui/SideBar.tsx @@ -66,7 +66,7 @@ const SideBar = ({

{t("sidebar.setting")}

-
+
{navigate("/setting")}}> {t("sidebar.setting")}
diff --git a/src/Pages/Admin/Setting/Form/FileSetting.tsx b/src/Pages/Admin/Setting/Form/FileSetting.tsx new file mode 100644 index 0000000..42a05ad --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting.tsx @@ -0,0 +1,42 @@ +import TabHeader from './TabHeader' +import { Form, Formik } from 'formik' +import { getInitialValues, getValidationSchema } from './FileSetting/formUtils' +import PersonalDetailsForm from './FileSetting/PersonalDetailsForm' +import TitleDetailsForm from './FileSetting/TitleDetailsForm' +import AttachmentForm from './FileSetting/AttachmentForm' +import { useTranslation } from 'react-i18next' + +const FileSetting = () => { + const {t} = useTranslation() + const handelSubmit = (values: any) => { + console.log(values, "values"); + }; + return ( +
+ +
+ + +
+
+ +
+ + + + +
+
+ ) +} + +export default FileSetting \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx b/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx new file mode 100644 index 0000000..246aabb --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx @@ -0,0 +1,23 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { FaImage, FaStore } from "react-icons/fa"; +import ImageBoxField from "./ImageBoxField/ImageBoxField"; + +const AttachmentForm = () => { + const [t] = useTranslation(); + + return ( +
+
+ +

{t("header.attachments")}

+
+
+ + +
+
+ ); +}; + +export default AttachmentForm; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.scss b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.scss new file mode 100644 index 0000000..66e2f2f --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.scss @@ -0,0 +1,38 @@ +.ImageBoxField { + .ImageBox { + width: 120px; + height: 120px; + display: flex; + align-items: center; + justify-content: center; + border: max(1.5px, 0.1vw) dashed #a9c3f1; + margin-block: 10px; + border-radius: 5px; + z-index: 9999999 !important; + .ImageBoxIcon { + cursor: pointer; + } + .imagePreview { + max-width: 99%; + height: auto; + max-height: 99%; + object-fit: contain; + border-radius: 5px; + } + } + .ImageHeader { + display: flex; + align-items: center; + justify-content: flex-end; + gap: 10px; + } + + .ImageCancelIcon { + width: 16px !important; + height: 16px !important; + } + .ImageBoxIcon { + width: 20px !important; + height: 20px !important; + } +} diff --git a/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.tsx b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.tsx new file mode 100644 index 0000000..fae09b2 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageBoxField.tsx @@ -0,0 +1,87 @@ +import { useFormikContext } from "formik"; +import { useState, useRef, useEffect } from "react"; +import "./ImageBoxField.scss"; +import ImageIcon from "./ImageIcon"; +import ImageCancelIcon from "./ImageCancelIcon"; +import { generateImagePreview } from "./generateImagePreview"; +import { getNestedValue } from "../../../../../../utils/getNestedValue"; +import { useTranslation } from "react-i18next"; + +// Helper function to generate image preview from a File + +const ImageBoxField = ({ name }: any) => { + const formik = useFormikContext(); + const value = getNestedValue(formik.values, name); + const [imagePreview, setImagePreview] = useState(null); + const fileInputRef = useRef(null); + + useEffect(() => { + if (value instanceof File) { + generateImagePreview(value, setImagePreview); + } else if (typeof value === "string") { + setImagePreview(value); + } else { + setImagePreview(null); + } + }, [value]); + + const handleFileChange = (event: any) => { + const file = event.target.files[0]; + if (file) { + generateImagePreview(file, setImagePreview); + formik.setFieldValue(name, file); + } + }; + + const handleButtonClick = () => { + const fileInput = fileInputRef.current; + if (fileInput) { + fileInput.click(); + } + }; + + const handleCancel = () => { + setImagePreview(""); + formik.setFieldValue(name, ""); + + if (fileInputRef.current) { + fileInputRef.current.value = ""; + } + }; + const [t] = useTranslation(); + return ( +
+
{t(`input.${name}`)}
+
+ {imagePreview ? ( + <> + + + + ) : ( +
hidden
+ )} +
+
+ {imagePreview ? ( + Preview + ) : ( + + )} +
+ +
+ ); +}; + +export default ImageBoxField; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageCancelIcon.tsx b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageCancelIcon.tsx new file mode 100644 index 0000000..d42ba53 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageCancelIcon.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +interface ImageCancelIconProps extends React.HTMLAttributes {} + +const ImageCancelIcon: React.FC = (props) => { + return ( +
+ + + +
+ ); +}; + +export default ImageCancelIcon; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageIcon.tsx b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageIcon.tsx new file mode 100644 index 0000000..4ca597a --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/ImageIcon.tsx @@ -0,0 +1,18 @@ +import React from "react"; + +interface ImageIconProps extends React.HTMLAttributes {} + +const ImageIcon: React.FC = (props) => { + return ( +
+ + + +
+ ); +}; + +export default ImageIcon; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/generateImagePreview.ts b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/generateImagePreview.ts new file mode 100644 index 0000000..3f754d3 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/ImageBoxField/generateImagePreview.ts @@ -0,0 +1,10 @@ +export const generateImagePreview = ( + file: File, + setImagePreview: (result: string) => void, +) => { + const reader = new FileReader(); + reader.onloadend = () => { + setImagePreview(reader.result as string); + }; + reader.readAsDataURL(file); +}; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx b/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx new file mode 100644 index 0000000..f97a36c --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx @@ -0,0 +1,52 @@ +import { useTranslation } from "react-i18next"; +import { FaStore } from "react-icons/fa"; +import ValidationField from "../../../../../Components/ValidationField/ValidationField"; +import { statusType } from "../../../../../config/statusType"; +import { Col } from "antd"; + +const PersonalDetailsForm = () => { + const [t] = useTranslation(); + return ( +
+
+ +

{t("header.personal_information")}

+
+
+ + + + + + + + +
+
+ ); +}; + +export default PersonalDetailsForm; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx b/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx new file mode 100644 index 0000000..08ccd21 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx @@ -0,0 +1,30 @@ +import { useTranslation } from "react-i18next"; +import { FaStore } from "react-icons/fa"; +import ValidationField from "../../../../../Components/ValidationField/ValidationField"; +import { nationalities } from "../../../../../types/App"; +import { CiEdit } from "react-icons/ci"; + +const TitleDetailsForm = () => { + const [t] = useTranslation(); + + return ( +
+
+ +

{t("header.address")}

+
+
+ + +
+
+ ); +}; + +export default TitleDetailsForm; diff --git a/src/Pages/Admin/Setting/Form/FileSetting/formUtils.ts b/src/Pages/Admin/Setting/Form/FileSetting/formUtils.ts new file mode 100644 index 0000000..b14915a --- /dev/null +++ b/src/Pages/Admin/Setting/Form/FileSetting/formUtils.ts @@ -0,0 +1,13 @@ +import * as Yup from "yup"; + +export const getInitialValues = (objectToEdit: Partial) => { + return { + id: objectToEdit?.id ?? null, + name: objectToEdit?.name ?? null, + }; +}; + +export const getValidationSchema = () => { + // validate input + return Yup.object().shape({}); +}; diff --git a/src/Pages/Admin/Setting/Form/Notification.tsx b/src/Pages/Admin/Setting/Form/Notification.tsx new file mode 100644 index 0000000..cca9a0d --- /dev/null +++ b/src/Pages/Admin/Setting/Form/Notification.tsx @@ -0,0 +1,36 @@ +import React from 'react' +import TabHeader from './TabHeader' +import NotificationCard from './Notification/NotificationCard' +import { NotificationData } from '../../../../faker/item' +import { Form, Formik } from 'formik' +import { getInitialValues, getValidationSchema } from './Notification/formUtils' + +const Notification = () => { + const handelSubmit = (values: any) => { + console.log(values, "values"); + }; + return ( +
+ + + +
+
+ {NotificationData?.map((not: any) => ( + + ))} +
+
+
+
+ ) +} + +export default Notification \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/Notification/NotificationCard.tsx b/src/Pages/Admin/Setting/Form/Notification/NotificationCard.tsx new file mode 100644 index 0000000..1455fc7 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/Notification/NotificationCard.tsx @@ -0,0 +1,27 @@ +import { useTranslation } from "react-i18next" +import ValidationField from "../../../../../Components/ValidationField/ValidationField"; + +const NotificationCard = ({ + name, + description, +}:{ + name:string, + description:string, +}) => { + + const {t} = useTranslation(); + + return ( +
+
+
{t(`${name}`)}
+

{t(`${description}`)}

+
+
+ +
+
+ ) +} + +export default NotificationCard \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/Notification/formUtils.ts b/src/Pages/Admin/Setting/Form/Notification/formUtils.ts new file mode 100644 index 0000000..b14915a --- /dev/null +++ b/src/Pages/Admin/Setting/Form/Notification/formUtils.ts @@ -0,0 +1,13 @@ +import * as Yup from "yup"; + +export const getInitialValues = (objectToEdit: Partial) => { + return { + id: objectToEdit?.id ?? null, + name: objectToEdit?.name ?? null, + }; +}; + +export const getValidationSchema = () => { + // validate input + return Yup.object().shape({}); +}; diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting.tsx new file mode 100644 index 0000000..8e05ed3 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting.tsx @@ -0,0 +1,29 @@ +import React from 'react' +import TabHeader from './TabHeader' +import SecurityCard from './SecuritySetting/SecurityCard' +import { Button } from 'antd' +import { SecurityData } from './SecuritySetting/SecurityData' +import { SettingType } from '../../../../types/Setting' + +const SecuritySetting = () => { + return ( +
+ + +
+ {SecurityData?.map((e:SettingType)=>( + + ))} + +
+
+ ) +} + +export default SecuritySetting \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx new file mode 100644 index 0000000..c9d1eef --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx @@ -0,0 +1,27 @@ +import { useTranslation } from "react-i18next" +import ValidationField from "../../../../../Components/ValidationField/ValidationField"; +import { ReactElement } from "react"; +import { SettingType } from "../../../../../types/Setting"; + +const SecurityCard = ({ + name, + description, + children +}:SettingType) => { + + const {t} = useTranslation(); + + return ( +
+
+
{t(`practical.${name}`)}
+

{t(`practical.${description}`)}

+
+
+ {children} +
+
+ ) +} + +export default SecurityCard \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx new file mode 100644 index 0000000..87b4433 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx @@ -0,0 +1,47 @@ +import { Button } from "antd"; +import { SettingType } from "../../../../../types/Setting"; + +export const SecurityData:SettingType[] = [ + { + name:"password", + description:"Set a unique password to protect the account", + children:, + }, + { + name:"two_factors", + description:"Receive codes via SMS or email every time you log in", + // children:, + }, + { + name:"verify_phone_number", + description:"The phone number associated with the account", + // children:, + }, + { + name:"email_address", + description:"The email address associated with the account", + // children:, + }, + { + name:"device_management", + description:"Devices associated with the account", + children:, + }, + { + name:"account_activity", + description:"account_activities", + children:, + }, + { + name:"deactivate_the_account", + description:"This will close your account. Your account will be interactive when you log in again", + children:, + }, + { + name:"delete_account", + description:"Your account will be permanently deleted", + children:, + }, + +] + diff --git a/src/Pages/Admin/Setting/Form/SettingTabs.tsx b/src/Pages/Admin/Setting/Form/SettingTabs.tsx new file mode 100644 index 0000000..5095be5 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SettingTabs.tsx @@ -0,0 +1,51 @@ +import React, { useState } from 'react'; +import type { TabsProps } from 'antd'; +import { Tabs } from 'antd'; +import { useTranslation } from 'react-i18next'; +import { CgProfile } from "react-icons/cg"; +import { useWindowResize } from '../../../../Hooks/useWindowResize'; +import Card from '../../../ReSeller/Notifications/Card'; +import FileSetting from './FileSetting'; +import SecuritySetting from './SecuritySetting'; +import Notification from './Notification'; + +type TabPosition = 'left' | 'right' | 'top' | 'bottom'; + +const SettingTabs: React.FC = () => { + const {windowWidth} = useWindowResize() + const {t} = useTranslation(); + const [tabPosition, setTabPosition] = useState(windowWidth < 800 ? 'top' : 'left'); + const items: TabsProps['items'] = [ + + { + key: '1', + label: t('table.file_setting'), + children: , + }, + { + key: '2', + label: t('table.security_setting'), + children: , + }, + { + key: '3', + label: t('table.notification'), + children: , + }, + ]; + + + return ( + <> + + + ); +}; + +export default SettingTabs; \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/TabHeader.tsx b/src/Pages/Admin/Setting/Form/TabHeader.tsx new file mode 100644 index 0000000..c6cebe3 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/TabHeader.tsx @@ -0,0 +1,29 @@ +import { ReactElement } from 'react'; +import { useTranslation } from 'react-i18next' + +const TabHeader = ({ + name, + description, + children +}:{ + name:string, + description:string, + children?:ReactElement +}) => { + + const {t} = useTranslation(); + + return ( +
+
+
{t(`table.${name}`)}
+

{t(`table.${description}`)}

+
+
+ {children} +
+
+ ) +} + +export default TabHeader \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Page.tsx b/src/Pages/Admin/Setting/Page.tsx new file mode 100644 index 0000000..6fe837f --- /dev/null +++ b/src/Pages/Admin/Setting/Page.tsx @@ -0,0 +1,31 @@ +import { FaPlus } from "react-icons/fa"; +import useModalHandler from "../../../utils/useModalHandler"; +import { ModalEnum } from "../../../enums/Model"; +import { useTranslation } from "react-i18next"; +import { lazy, Suspense } from "react"; +import { Spin } from "antd"; +import useSetPageTitle from "../../../Hooks/useSetPageTitle"; +import PageHeader from "../../../Layout/Dashboard/PageHeader"; +import SettingTabs from "./Form/SettingTabs"; + +const TableHeader = () => { + const [t] = useTranslation(); + + useSetPageTitle([ + {name:`${t(`page_header.home`)}`, path:"/"}, + {name:`${t(`page_header.setting`)}`, path:"setting"} + ]); + + return ( +
+ }> + + + +
+ ); +}; + +export default TableHeader; diff --git a/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx b/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx index 96d9ed5..da24c58 100644 --- a/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx +++ b/src/Pages/ReSeller/Profile/Form/PersonalDetailsForm.tsx @@ -18,24 +18,43 @@ const PersonalDetailsForm = () => {
+ + + + + + + { /> -
); diff --git a/src/Routes.tsx b/src/Routes.tsx index 02ddcf1..815d7af 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -37,10 +37,11 @@ const User = React.lazy(() => import("./Pages/Admin/User/Page")); const QuestionBank = React.lazy(() => import("./Pages/Admin/QuestionBank/Page")); const Notifications = React.lazy(() => import("./Pages/Admin/Notifications/Page")); const Profile = React.lazy(() => import("./Pages/Admin/Profile/Page")); +const Setting = React.lazy(() => import("./Pages/Admin/Setting/Page")); + -const Roles = React.lazy(() => import("./Pages/Admin/Roles/Page")); const Permissions = React.lazy(() => import("./Pages/Admin/Roles/Permissions/Page")); - +const Roles = React.lazy(() => import("./Pages/Admin/Roles/Page")); const Report = React.lazy(() => import("./Pages/Admin/Report/Page")); const Param = React.lazy(() => import("./Pages/Admin/Param/Page")); @@ -205,7 +206,7 @@ export const menuItems: TMenuItem[] = [ element: , icon: , text: "sidebar.profile", - path: `//${ABILITIES_ENUM?.PROFILE}`, + path: `/${ABILITIES_ENUM?.PROFILE}`, abilities: ABILITIES_ENUM?.Profile_RE_SELLER, abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, @@ -338,6 +339,14 @@ export const CrudRoute: TCrudRoute[] = [ abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, }, + { + header: "page_header.setting", + element: , + path: `/${ABILITIES_ENUM?.SETTING}`, + abilities: ABILITIES_ENUM?.SETTING, + abilities_value: ABILITIES_VALUES_ENUM.INDEX, + prevPath: 0, + }, //// RE_SELLER { header: "page_header.notifications", diff --git a/src/Styles/Pages/index.scss b/src/Styles/Pages/index.scss index a1122fc..3e2a456 100644 --- a/src/Styles/Pages/index.scss +++ b/src/Styles/Pages/index.scss @@ -13,4 +13,5 @@ @import './InfoCard.scss'; @import './notifications.scss'; @import './profile.scss'; -@import './collections.scss' +@import './collections.scss'; +@import './setting.scss'; diff --git a/src/Styles/Pages/reSeller.scss b/src/Styles/Pages/reSeller.scss index cff1520..dafc561 100644 --- a/src/Styles/Pages/reSeller.scss +++ b/src/Styles/Pages/reSeller.scss @@ -4,6 +4,13 @@ gap: 20px; background: var(--bg); padding: 40px 10px; + .ValidationField{ + label{ + font-weight: 600; + font-size: 16px !important; + color: var(--secondary) !important; + } + } > * { // max-width: 30%; flex-basis: 31%; @@ -21,7 +28,7 @@ } } -.resellerButton { +.resellerButton,.file_setting_buttons { display: flex; align-items: center; justify-content: flex-end; diff --git a/src/Styles/Pages/setting.scss b/src/Styles/Pages/setting.scss new file mode 100644 index 0000000..630f51f --- /dev/null +++ b/src/Styles/Pages/setting.scss @@ -0,0 +1,153 @@ +.setting_tabs { + border-top: 1px solid var(--border-color-2); + + .ant-tabs-nav { + width: 20%; + } + + .ant-tabs-nav-list { + width: 100% !important; + border-left: 1px solid var(--border-color-2); + padding-left: 20px; + } + + .ant-tabs-tab { + margin-top: 10px; + color: var(--secondary); + } + + .ant-tabs-tab-active { + background: #3D5EE11A !important; + border-radius: 8px 0 0 8px; + border-right: 1px solid #3D5EE11A; + margin-left: 0 !important; + } + + .ant-tabs-content-holder { + padding: 20px 0 !important; + } + + .ant-tabs-content-holder { + border: none; + margin-right: 20px; + padding-top: 20px !important; + padding-left: 0 !important; + + } + + .ant-tabs-tabpane-active { + padding-left: 0 !important; + } + + .notification { + .setting_notification_body { + padding: 10px 20px; + box-shadow: 0px 0px 32px 0px #080F3414; + border: 1.5px solid #E9EDF4; + border-radius: 10px; + background: #fff; + } + } +} + + +.setting_tab_header, +.notification_card, +.security_card { + display: flex; + justify-content: space-between; + align-items: center; + border-bottom: 1px solid var(--border-color-2); + padding-block: 10px 20px; + + div { + display: flex; + flex-direction: column; + gap: 10px; + + h5 { + color: var(--secondary); + font-size: 1.4vw; + } + + p { + font-size: 1.1vw; + color: var(--value); + } + + .file_setting_buttons { + display: flex !important; + flex-direction: row; + } + } +} + +.setting_tab_header { + width: 100%; + margin-bottom: 30px; +} + +.notification_card{ + &:nth-last-child(1) { + border: none; + } + h5 { + font-size: 1.2vw !important; + } + + p { + font-size: .9vw !important; + } + .ant-form-item, + .ant-form-item-control-input-content { + height: 3vh; + gap: 0 !important; + margin-bottom: 0 !important; + } + .ant-checkbox .ant-checkbox-inner { + width: 25px; + height: 25px; + border: 2px solid var(--border-color-2); + } +} +.security_card{ + padding: 20px 20px; + box-shadow: 0px 0px 32px 0px #080F3414; + border: 1.5px solid #E9EDF4; + border-radius: 10px; + background: #fff; + margin-block: 20px ; + h5 { + font-size: 1.2vw !important; + } + + p { + font-size: .9vw !important; + } + +} + +.security_setting_button{ + display: flex; + align-items: center; + justify-content: center; + outline: none; + border: 2px solid var(--primary); + border-radius: 7px; + padding: 22px 15px !important; + color: var(--primary); + gap: 5px; + &:hover{ + border: 2px solid var(--primary) !important; + background: var(--primary) !important; + color: var(--white) !important; + } +} +.security_setting_button_danger{ + border-color:var(--warning); + color: var(--warning); + &:hover{ + border: 2px solid var(--warning) !important; + background: var(--warning) !important; + } +} \ No newline at end of file diff --git a/src/enums/abilities.ts b/src/enums/abilities.ts index cac07e7..fe335e8 100644 --- a/src/enums/abilities.ts +++ b/src/enums/abilities.ts @@ -55,7 +55,8 @@ export enum ABILITIES_ENUM { NOTIFICATIONS_RE_SELLER = "notification_re_seller" , Profile_RE_SELLER = "profile_re_seller" , Sales = "sales", - Collections = "collections" + Collections = "collections", + SETTING = "setting" //// } diff --git a/src/faker/item.ts b/src/faker/item.ts index bb4990b..cb88067 100644 --- a/src/faker/item.ts +++ b/src/faker/item.ts @@ -4,4 +4,14 @@ export const CollectionData = [ {label:"المستحقات",value:"2.000.000"}, {label:"تم تحصيله",value:"2.000.000"}, {label:"المتبقي",value:"2.000.000"}, + ] + + + export const NotificationData = [ + {label:"إشعارات البريد الإلكتروني",value:"يمكن أن ترسل لك Substance إشعارات عبر البريد الإلكتروني لأي رسائل مباشرة جديدة"}, + {label:"إشعارات البريد الإلكتروني",value:"يمكن أن ترسل لك Substance إشعارات عبر البريد الإلكتروني لأي رسائل مباشرة جديدة"}, + {label:"إشعارات البريد الإلكتروني",value:"يمكن أن ترسل لك Substance إشعارات عبر البريد الإلكتروني لأي رسائل مباشرة جديدة"}, + {label:"إشعارات البريد الإلكتروني",value:"يمكن أن ترسل لك Substance إشعارات عبر البريد الإلكتروني لأي رسائل مباشرة جديدة"}, + {label:"إشعارات البريد الإلكتروني",value:"يمكن أن ترسل لك Substance إشعارات عبر البريد الإلكتروني لأي رسائل مباشرة جديدة"}, + ] \ No newline at end of file diff --git a/src/translate/ar.json b/src/translate/ar.json index 18c61d3..9fd64f4 100644 --- a/src/translate/ar.json +++ b/src/translate/ar.json @@ -141,7 +141,8 @@ "managers":"مدراء", "sales":"المبيعات", "hide_hint":"اخفاء الشرح", - "show_hint":"عرض الشرح" + "show_hint":"عرض الشرح", + "setting":"الإعدادات" }, "columns": { "id": "الرقم التعريفي", @@ -283,7 +284,23 @@ "sorry_something_went_wrong": "عفوا ، حدث خطأ ما", "error_404_Page_not_found._Sorry,_the_page_you_are_looking_for_does_not_exist": "خطأ 404 لم يتم العثور على الصفحة. عذرا الصفحة التي تبحث عنها غير موجودة ", "return_to_the_dashboard": "العودة إلى لوحة القيادة", - "save_changes":"حفظ التغييرات" + "save_changes":"حفظ التغييرات", + "password":"كلمة المرور", + "two_factors":"عاملان", + "verify_phone_number":"التحقق من رقم الهاتف", + "email_address":"عنوان البريد الإلكتروني", + "device_management":"إدارة الأجهزة", + "account_activity":"نشاط الحساب", + "deactivate_the_account":"إلغاء تنشيط الحساب", + "delete_account":"حذف الحساب", + "Set a unique password to protect the account":"تعيين كلمة مرور فريدة لحماية الحساب", + "Receive codes via SMS or email every time you log in":"تلقي الرموز عبر الرسائل القصيرة أو البريد الإلكتروني في كل مرة تقوم فيها بتسجيل الدخول", + "The phone number associated with the account":"رقم الهاتف المرتبط بالحساب", + "The email address associated with the account":"عنوان البريد الإلكتروني المرتبط بالحساب", + "Devices associated with the account":"الأجهزة المرتبطة بالحساب", + "account_activities":"أنشطة الحساب", + "This will close your account. Your account will be interactive when you log in again":"سيؤدي هذا إلى إغلاق حسابك. سيكون حسابك تفاعليا عند تسجيل الدخول مرة أخرى", + "Your account will be permanently deleted":"سيتم حذف حسابك نهائيا" }, "Table": { "header": "", @@ -870,7 +887,8 @@ "add_manager":"إضافة مدير", "collections": "التحصيلات", "sales":"المبيعات", - "edit_manager":"تعديل مدير" + "edit_manager":"تعديل مدير", + "setting":"الإعدادات" }, "page_header": { "home": "لوحة القيادة", @@ -916,7 +934,8 @@ "permissions":"اذونات", "managers":"مدراء", "collections": "التحصيلات", - "sales":"المبيعات" + "sales":"المبيعات", + "setting":"الإعدادات" }, "table": { "student": "قائمة الطلاب", @@ -927,7 +946,13 @@ "managers":"مدراء", "managers_list":"قائمة المدراء", "sales":"المبيعات", - "collections": "التحصيلات" + "collections": "التحصيلات", + "setting":"الإعدادات", + "file_setting":"إعدادات الملف", + "security_setting":"إعدادات الأمان", + "notification":"الاشعارات", + "upload_your_photo_and_personal_data_here":"قم بتحميل صورتك وبياناتك الشخصية هنا", + "get_notified_of_whats_happening_now_you_can_turn_it_off_at_any_time":"احصل على إشعار بما يحدث الآن ، يمكنك إيقاف تشغيله في أي وقت" }, "alphabet": { "A": "A", diff --git a/src/types/Setting.ts b/src/types/Setting.ts new file mode 100644 index 0000000..4f7324e --- /dev/null +++ b/src/types/Setting.ts @@ -0,0 +1,7 @@ +import { ReactElement } from "react"; + +export type SettingType = { + name:string; + description:string; + children?:ReactElement; +} \ No newline at end of file From 25161d4afaaab8daa639186dbfed6e38bd5afecd Mon Sep 17 00:00:00 2001 From: Moaz Dawalibi Date: Sat, 21 Sep 2024 14:43:27 +0300 Subject: [PATCH 2/2] complete setting page --- src/Components/Switch/Switch.tsx | 24 ++++++++++ .../Ui/Buttons/EditSettingButton.tsx | 18 ++++++++ .../Ui/Buttons/SecuritySettingButton.tsx | 13 ++++++ .../ValidationField/View/SelectField.tsx | 1 - .../utils/translatedOptions.ts | 1 - src/Layout/Dashboard/LayoutModel.tsx | 4 -- src/Pages/Admin/Reseller/show/Page.tsx | 2 +- .../Form/FileSetting/AttachmentForm.tsx | 3 +- .../Form/FileSetting/PersonalDetailsForm.tsx | 1 - .../Form/FileSetting/TitleDetailsForm.tsx | 1 - src/Pages/Admin/Setting/Form/Notification.tsx | 1 - .../Admin/Setting/Form/SecuritySetting.tsx | 25 +++++------ .../Form/SecuritySetting/EmailAddress.tsx | 27 ++++++++++++ .../SecuritySetting/Model/EditEmailModel.tsx | 36 +++++++++++++++ .../SecuritySetting/Model/EditPhoneModel.tsx | 36 +++++++++++++++ .../Form/SecuritySetting/Model/ModelForm.tsx | 27 ++++++++++++ .../Form/SecuritySetting/Model/formUtil.ts | 31 +++++++++++++ .../Form/SecuritySetting/SecurityCard.tsx | 5 +-- .../Form/SecuritySetting/SecurityData.tsx | 24 ++++++---- .../Form/SecuritySetting/VerifyPhone.tsx | 26 +++++++++++ src/Pages/Admin/Setting/Form/SettingTabs.tsx | 10 ++--- src/Pages/Admin/Setting/Page.tsx | 5 +-- src/Routes.tsx | 16 +++---- src/Styles/Pages/setting.scss | 44 +++++++++++++++++++ src/enums/Model.ts | 8 ++++ src/enums/abilities.ts | 5 ++- src/translate/ar.json | 7 ++- src/utils/hasAbilityFn.ts | 15 +++++++ 28 files changed, 355 insertions(+), 61 deletions(-) create mode 100644 src/Components/Switch/Switch.tsx create mode 100644 src/Components/Ui/Buttons/EditSettingButton.tsx create mode 100644 src/Components/Ui/Buttons/SecuritySettingButton.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/EmailAddress.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditEmailModel.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditPhoneModel.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/Model/ModelForm.tsx create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/Model/formUtil.ts create mode 100644 src/Pages/Admin/Setting/Form/SecuritySetting/VerifyPhone.tsx diff --git a/src/Components/Switch/Switch.tsx b/src/Components/Switch/Switch.tsx new file mode 100644 index 0000000..8fb7f1e --- /dev/null +++ b/src/Components/Switch/Switch.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { Switch } from 'antd'; +export interface SwitchProps { + onChange?: (checked: any, event: any) => any; + checked?: boolean; + } +const onSwitchChange = (checked: boolean) => { + console.log(`switch to ${checked}`); +}; + +const SwitchButton = ({onChange,checked}:SwitchProps) => { + return( + + onChange ? onChange(checked, event) : onSwitchChange(checked) + } + // checked={checked} + /> + ) +} + +export default SwitchButton; \ No newline at end of file diff --git a/src/Components/Ui/Buttons/EditSettingButton.tsx b/src/Components/Ui/Buttons/EditSettingButton.tsx new file mode 100644 index 0000000..fba86f1 --- /dev/null +++ b/src/Components/Ui/Buttons/EditSettingButton.tsx @@ -0,0 +1,18 @@ +import { Button } from 'antd' +import { useTranslation } from 'react-i18next' +import { CiEdit } from "react-icons/ci"; + +const EditSettingButton = ({buttonName,onClick}:{buttonName?:string,onClick?:() => void}) => { + const {t} = useTranslation(); + + return ( +
+ +
+ ) +} + +export default EditSettingButton \ No newline at end of file diff --git a/src/Components/Ui/Buttons/SecuritySettingButton.tsx b/src/Components/Ui/Buttons/SecuritySettingButton.tsx new file mode 100644 index 0000000..598ea69 --- /dev/null +++ b/src/Components/Ui/Buttons/SecuritySettingButton.tsx @@ -0,0 +1,13 @@ +import { Button } from 'antd' +import { useTranslation } from 'react-i18next' + +const SecuritySettingButton = ({name,danger = false}:{name:string,danger?:boolean}) => { + const {t} = useTranslation(); + return ( +
+ +
+ ) +} + +export default SecuritySettingButton \ No newline at end of file diff --git a/src/Components/ValidationField/View/SelectField.tsx b/src/Components/ValidationField/View/SelectField.tsx index ec957e3..2737020 100644 --- a/src/Components/ValidationField/View/SelectField.tsx +++ b/src/Components/ValidationField/View/SelectField.tsx @@ -28,7 +28,6 @@ const SelectField = ({ formik.setFieldValue(name, value); }; const options = translateOptions(option, t); - console.log(options); return (
diff --git a/src/Components/ValidationField/utils/translatedOptions.ts b/src/Components/ValidationField/utils/translatedOptions.ts index 66c2619..8d0bd4c 100644 --- a/src/Components/ValidationField/utils/translatedOptions.ts +++ b/src/Components/ValidationField/utils/translatedOptions.ts @@ -1,5 +1,4 @@ export const translateOptions = (options: any, t: any) => { - console.log(options); return options?.map((opt: any) => ({ ...opt, diff --git a/src/Layout/Dashboard/LayoutModel.tsx b/src/Layout/Dashboard/LayoutModel.tsx index 249ac3d..597ea72 100644 --- a/src/Layout/Dashboard/LayoutModel.tsx +++ b/src/Layout/Dashboard/LayoutModel.tsx @@ -81,10 +81,6 @@ const LayoutModel = ({ formik.resetForm(); } }, [isOpen]); - - console.log(formik.initialValues); - console.log(formik?.values); - return
diff --git a/src/Pages/Admin/Reseller/show/Page.tsx b/src/Pages/Admin/Reseller/show/Page.tsx index f487ce4..507d4ad 100644 --- a/src/Pages/Admin/Reseller/show/Page.tsx +++ b/src/Pages/Admin/Reseller/show/Page.tsx @@ -32,8 +32,8 @@ const TableHeader = () => { const handleOpenModel = () =>{ handel_open_model(ModalEnum?.RE_SELLER_COLLECTION_ADD); - } + const deleteMutation = useDeleteReseller(); return (
diff --git a/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx b/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx index 246aabb..8dc8b1a 100644 --- a/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx +++ b/src/Pages/Admin/Setting/Form/FileSetting/AttachmentForm.tsx @@ -1,6 +1,5 @@ -import React from "react"; import { useTranslation } from "react-i18next"; -import { FaImage, FaStore } from "react-icons/fa"; +import {FaStore } from "react-icons/fa"; import ImageBoxField from "./ImageBoxField/ImageBoxField"; const AttachmentForm = () => { diff --git a/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx b/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx index f97a36c..6e009c5 100644 --- a/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx +++ b/src/Pages/Admin/Setting/Form/FileSetting/PersonalDetailsForm.tsx @@ -2,7 +2,6 @@ import { useTranslation } from "react-i18next"; import { FaStore } from "react-icons/fa"; import ValidationField from "../../../../../Components/ValidationField/ValidationField"; import { statusType } from "../../../../../config/statusType"; -import { Col } from "antd"; const PersonalDetailsForm = () => { const [t] = useTranslation(); diff --git a/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx b/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx index 08ccd21..8c8a093 100644 --- a/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx +++ b/src/Pages/Admin/Setting/Form/FileSetting/TitleDetailsForm.tsx @@ -2,7 +2,6 @@ import { useTranslation } from "react-i18next"; import { FaStore } from "react-icons/fa"; import ValidationField from "../../../../../Components/ValidationField/ValidationField"; import { nationalities } from "../../../../../types/App"; -import { CiEdit } from "react-icons/ci"; const TitleDetailsForm = () => { const [t] = useTranslation(); diff --git a/src/Pages/Admin/Setting/Form/Notification.tsx b/src/Pages/Admin/Setting/Form/Notification.tsx index cca9a0d..e014d30 100644 --- a/src/Pages/Admin/Setting/Form/Notification.tsx +++ b/src/Pages/Admin/Setting/Form/Notification.tsx @@ -1,4 +1,3 @@ -import React from 'react' import TabHeader from './TabHeader' import NotificationCard from './Notification/NotificationCard' import { NotificationData } from '../../../../faker/item' diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting.tsx index 8e05ed3..7e2fc7a 100644 --- a/src/Pages/Admin/Setting/Form/SecuritySetting.tsx +++ b/src/Pages/Admin/Setting/Form/SecuritySetting.tsx @@ -1,7 +1,5 @@ -import React from 'react' import TabHeader from './TabHeader' import SecurityCard from './SecuritySetting/SecurityCard' -import { Button } from 'antd' import { SecurityData } from './SecuritySetting/SecurityData' import { SettingType } from '../../../../types/Setting' @@ -9,19 +7,18 @@ const SecuritySetting = () => { return (
+ name='security_setting' + description='upload_your_photo_and_personal_data_here' + > -
- {SecurityData?.map((e:SettingType)=>( - - ))} - -
+
+ {SecurityData?.map((e:SettingType)=>( + + ))} +
) } diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/EmailAddress.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/EmailAddress.tsx new file mode 100644 index 0000000..d3732a1 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/EmailAddress.tsx @@ -0,0 +1,27 @@ +import EditSettingButton from '../../../../../Components/Ui/Buttons/EditSettingButton' +import { useTranslation } from 'react-i18next' +import { FaCheck } from 'react-icons/fa6'; +import useModalHandler from '../../../../../utils/useModalHandler'; +import { ModalEnum } from '../../../../../enums/Model'; +import { canEditEmail } from '../../../../../utils/hasAbilityFn'; +import EditEmailModel from './Model/EditEmailModel'; + +const EmailAddress = () => { + const {t} = useTranslation(); + const { handel_open_model } = useModalHandler(); + + const handleOpenModel = () =>{ + handel_open_model(ModalEnum?.Email_EDIT); + } + + return ( +
+ {canEditEmail && } + + {t("input.verify")} +

{t("admin@example.com")}

+
+ ) +} + +export default EmailAddress \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditEmailModel.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditEmailModel.tsx new file mode 100644 index 0000000..3fd515f --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditEmailModel.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { getInitialValuesEmail, getValidationSchemaEmail } from "./formUtil"; +import { ModalEnum } from "../../../../../../enums/Model"; +import LayoutModel from "../../../../../../Layout/Dashboard/LayoutModel"; +import {EmailForm} from "./ModelForm"; +import { QueryStatusEnum } from "../../../../../../enums/QueryStatus"; +import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState"; +import { useUpdateReseller } from "../../../../../../api/reseller"; + +const EditModel: React.FC = () => { + const { mutate, status } = useUpdateReseller(); + const { objectToEdit } = useObjectToEdit((state) => state); + + const handleSubmit = (values: any) => { + mutate({ + ...values, + }); + }; + return ( + <> + + + + + ); +}; + +export default EditModel; diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditPhoneModel.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditPhoneModel.tsx new file mode 100644 index 0000000..dec1818 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/EditPhoneModel.tsx @@ -0,0 +1,36 @@ +import React from "react"; +import { getInitialValuesPhone, getValidationSchemaPhone } from "./formUtil"; +import { ModalEnum } from "../../../../../../enums/Model"; +import LayoutModel from "../../../../../../Layout/Dashboard/LayoutModel"; +import {PhoneForm} from "./ModelForm"; +import { QueryStatusEnum } from "../../../../../../enums/QueryStatus"; +import { useObjectToEdit } from "../../../../../../zustand/ObjectToEditState"; +import { useUpdateReseller } from "../../../../../../api/reseller"; + +const EditModel: React.FC = () => { + const { mutate, status } = useUpdateReseller(); + const { objectToEdit } = useObjectToEdit((state) => state); + + const handleSubmit = (values: any) => { + mutate({ + ...values, + }); + }; + return ( + <> + + + + + ); +}; + +export default EditModel; diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/Model/ModelForm.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/ModelForm.tsx new file mode 100644 index 0000000..45e21b5 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/ModelForm.tsx @@ -0,0 +1,27 @@ +import { Col, Row } from "reactstrap"; +import ValidationField from "../../../../../../Components/ValidationField/ValidationField"; + +export const PhoneForm = () => { + return ( + + + + + + ); +}; + +PhoneForm; + + +export const EmailForm = () => { + return ( + + + + + + ); +}; + +EmailForm; diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/Model/formUtil.ts b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/formUtil.ts new file mode 100644 index 0000000..f0df5bc --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/Model/formUtil.ts @@ -0,0 +1,31 @@ +import * as Yup from "yup"; +export const getInitialValuesPhone = (objectToEdit: any): any => { + return { + id: objectToEdit?.id ?? null, + phone_number: objectToEdit?.phone_number ?? null, + + }; +}; + +export const getValidationSchemaPhone = () => { + return Yup.object().shape({ + phone_number: Yup.string().required("validation.required"), + + }); +}; + + +export const getInitialValuesEmail = (objectToEdit: any): any => { + return { + id: objectToEdit?.id ?? null, + email_address: objectToEdit?.email_address ?? null, + + }; +}; + +export const getValidationSchemaEmail = () => { + return Yup.object().shape({ + email_address: Yup.string().required("validation.required"), + + }); +}; diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx index c9d1eef..a1318ce 100644 --- a/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityCard.tsx @@ -1,13 +1,10 @@ import { useTranslation } from "react-i18next" -import ValidationField from "../../../../../Components/ValidationField/ValidationField"; -import { ReactElement } from "react"; import { SettingType } from "../../../../../types/Setting"; const SecurityCard = ({ name, description, - children -}:SettingType) => { + children}:SettingType) => { const {t} = useTranslation(); diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx index 87b4433..ed8eb4b 100644 --- a/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/SecurityData.tsx @@ -1,46 +1,52 @@ -import { Button } from "antd"; import { SettingType } from "../../../../../types/Setting"; +import SwitchButton from "../../../../../Components/Switch/Switch" +import SecuritySettingButton from "../../../../../Components/Ui/Buttons/SecuritySettingButton"; +import VerifyPhone from "./VerifyPhone"; +import EmailAddress from "./EmailAddress"; export const SecurityData:SettingType[] = [ { name:"password", description:"Set a unique password to protect the account", - children:, + children:, }, { name:"two_factors", description:"Receive codes via SMS or email every time you log in", - // children:, + children:, }, { name:"verify_phone_number", description:"The phone number associated with the account", - // children:, + children:, }, { name:"email_address", description:"The email address associated with the account", - // children:, + children:, }, { name:"device_management", description:"Devices associated with the account", - children:, + children:, }, { name:"account_activity", description:"account_activities", - children:, + children:, + }, { name:"deactivate_the_account", description:"This will close your account. Your account will be interactive when you log in again", - children:, + children:, + }, { name:"delete_account", description:"Your account will be permanently deleted", - children:, + children:, + }, ] diff --git a/src/Pages/Admin/Setting/Form/SecuritySetting/VerifyPhone.tsx b/src/Pages/Admin/Setting/Form/SecuritySetting/VerifyPhone.tsx new file mode 100644 index 0000000..5b91085 --- /dev/null +++ b/src/Pages/Admin/Setting/Form/SecuritySetting/VerifyPhone.tsx @@ -0,0 +1,26 @@ +import EditSettingButton from '../../../../../Components/Ui/Buttons/EditSettingButton' +import { t } from 'i18next' +import { FaCheck } from 'react-icons/fa6' +import useModalHandler from '../../../../../utils/useModalHandler'; +import { ModalEnum } from '../../../../../enums/Model'; +import { canEditPhone } from '../../../../../utils/hasAbilityFn'; +import EditPhoneModel from './Model/EditPhoneModel'; + +const VerifyPhone = () => { + const { handel_open_model } = useModalHandler(); + + const handleOpenModel = () => { + handel_open_model(ModalEnum?.PHONE_EDIT); + } + + return ( +
+ {canEditPhone && } + + {t("input.verify")} +

{t("0965289543")}

+
+ ) +} + +export default VerifyPhone \ No newline at end of file diff --git a/src/Pages/Admin/Setting/Form/SettingTabs.tsx b/src/Pages/Admin/Setting/Form/SettingTabs.tsx index 5095be5..42f595a 100644 --- a/src/Pages/Admin/Setting/Form/SettingTabs.tsx +++ b/src/Pages/Admin/Setting/Form/SettingTabs.tsx @@ -1,13 +1,11 @@ -import React, { useState } from 'react'; +import React, { lazy, useState } from 'react'; import type { TabsProps } from 'antd'; import { Tabs } from 'antd'; import { useTranslation } from 'react-i18next'; -import { CgProfile } from "react-icons/cg"; import { useWindowResize } from '../../../../Hooks/useWindowResize'; -import Card from '../../../ReSeller/Notifications/Card'; -import FileSetting from './FileSetting'; -import SecuritySetting from './SecuritySetting'; -import Notification from './Notification'; +const FileSetting = lazy(() => import("./FileSetting")); +const SecuritySetting = lazy(() => import("./SecuritySetting")); +const Notification = lazy(() => import("./Notification")); type TabPosition = 'left' | 'right' | 'top' | 'bottom'; diff --git a/src/Pages/Admin/Setting/Page.tsx b/src/Pages/Admin/Setting/Page.tsx index 6fe837f..8f3bde5 100644 --- a/src/Pages/Admin/Setting/Page.tsx +++ b/src/Pages/Admin/Setting/Page.tsx @@ -1,8 +1,5 @@ -import { FaPlus } from "react-icons/fa"; -import useModalHandler from "../../../utils/useModalHandler"; -import { ModalEnum } from "../../../enums/Model"; import { useTranslation } from "react-i18next"; -import { lazy, Suspense } from "react"; +import { Suspense } from "react"; import { Spin } from "antd"; import useSetPageTitle from "../../../Hooks/useSetPageTitle"; import PageHeader from "../../../Layout/Dashboard/PageHeader"; diff --git a/src/Routes.tsx b/src/Routes.tsx index 815d7af..c8c15e7 100644 --- a/src/Routes.tsx +++ b/src/Routes.tsx @@ -339,14 +339,14 @@ export const CrudRoute: TCrudRoute[] = [ abilities_value: ABILITIES_VALUES_ENUM.INDEX, prevPath: 0, }, - { - header: "page_header.setting", - element: , - path: `/${ABILITIES_ENUM?.SETTING}`, - abilities: ABILITIES_ENUM?.SETTING, - abilities_value: ABILITIES_VALUES_ENUM.INDEX, - prevPath: 0, - }, + // { + // header: "page_header.setting", + // element: , + // path: `/${ABILITIES_ENUM?.SETTING}`, + // abilities: ABILITIES_ENUM?.SETTING, + // abilities_value: ABILITIES_VALUES_ENUM.INDEX, + // prevPath: 0, + // }, //// RE_SELLER { header: "page_header.notifications", diff --git a/src/Styles/Pages/setting.scss b/src/Styles/Pages/setting.scss index 630f51f..9660a5a 100644 --- a/src/Styles/Pages/setting.scss +++ b/src/Styles/Pages/setting.scss @@ -150,4 +150,48 @@ border: 2px solid var(--warning) !important; background: var(--warning) !important; } +} + + +.setting_edit_button{ + @include Flex; + background: #F2F4F8; + border: none; + color: #515B73; + svg{ + font-size: 20px; + } + &:hover{ + background: #F2F4F8 !important; + border: none !important; + color: #515B73 !important; + scale: 1 !important; + } +} + +.setting_email_address, +.setting_verify_phone{ + display: flex; align-items: center;flex-direction: row-reverse !important; + > span{ + @include Flex; + gap: 5px; + font-size: 10px; + padding: 8px ; + direction: ltr; + color: #1ABE17; + background: #E7F9E7; + border-radius: 8px; + cursor: pointer; + svg{ + font-size: 10px; + } + } + p{ + color: var(--value); + font-size: 14px !important; + } +} + +.switch_button{ + background: var(--primary) !important; } \ No newline at end of file diff --git a/src/enums/Model.ts b/src/enums/Model.ts index 8480f8c..fbef421 100644 --- a/src/enums/Model.ts +++ b/src/enums/Model.ts @@ -215,4 +215,12 @@ export enum ModalEnum { /// sales Sales_ADD = "Sales.add", + + /// phone + + PHONE_EDIT = "Phone.edit", + + /// email + + Email_EDIT = "Email.edit", } diff --git a/src/enums/abilities.ts b/src/enums/abilities.ts index fe335e8..7666ff8 100644 --- a/src/enums/abilities.ts +++ b/src/enums/abilities.ts @@ -56,8 +56,9 @@ export enum ABILITIES_ENUM { Profile_RE_SELLER = "profile_re_seller" , Sales = "sales", Collections = "collections", - SETTING = "setting" - + SETTING = "setting", + Email = "email", + Phone = "phone" //// } diff --git a/src/translate/ar.json b/src/translate/ar.json index 18cff13..17879a2 100644 --- a/src/translate/ar.json +++ b/src/translate/ar.json @@ -380,7 +380,9 @@ "managers":"مدراء", "manager":"مدير", "sale":"عملية بيع", - "collections": "التحصيلات" + "collections": "التحصيلات", + "phone_number":"رقم الهاتف", + "email_address":"عنوان البريد الإلكتروني" }, "education_class_actions": { "Student_Records": "سجلات الطلاب", @@ -508,7 +510,8 @@ "empty":"", "role":"الدور", "submit_password":"تأكيد كلمة المرور", - "join_date":"تاريخ الانضمام" + "join_date":"تاريخ الانضمام", + "verify":"التحقق" }, "select": { "enums": { diff --git a/src/utils/hasAbilityFn.ts b/src/utils/hasAbilityFn.ts index 6cc137b..664c5f6 100644 --- a/src/utils/hasAbilityFn.ts +++ b/src/utils/hasAbilityFn.ts @@ -736,3 +736,18 @@ export const canDeleteSales = hasAbility( ABILITIES_ENUM.Sales, ABILITIES_VALUES_ENUM.DELETE, ); + + +/// Phone + +export const canEditPhone = hasAbility( + ABILITIES_ENUM.Phone, + ABILITIES_VALUES_ENUM.UPDATE, +); + +/// Email + +export const canEditEmail = hasAbility( + ABILITIES_ENUM.Email, + ABILITIES_VALUES_ENUM.UPDATE, +); \ No newline at end of file