complete setting page

This commit is contained in:
Moaz Dawalibi 2024-09-21 14:43:27 +03:00
parent 887b3a5e75
commit 25161d4afa
28 changed files with 355 additions and 61 deletions

View File

@ -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(
<Switch
className='switch_button'
defaultChecked
onChange={(checked: any, event: any) =>
onChange ? onChange(checked, event) : onSwitchChange(checked)
}
// checked={checked}
/>
)
}
export default SwitchButton;

View File

@ -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 (
<div>
<Button className=' setting_edit_button' onClick={onClick}>
<CiEdit/>
{t(`header.edit`) ?? (`header.${buttonName}`)}
</Button>
</div>
)
}
export default EditSettingButton

View File

@ -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 (
<div>
<Button className={`security_setting_button ${danger ? "security_setting_button_danger" :""}`}>{t(name)}</Button>
</div>
)
}
export default SecuritySettingButton

View File

@ -28,7 +28,6 @@ const SelectField = ({
formik.setFieldValue(name, value); formik.setFieldValue(name, value);
}; };
const options = translateOptions(option, t); const options = translateOptions(option, t);
console.log(options);
return ( return (
<div className="ValidationField w-100"> <div className="ValidationField w-100">

View File

@ -1,5 +1,4 @@
export const translateOptions = (options: any, t: any) => { export const translateOptions = (options: any, t: any) => {
console.log(options);
return options?.map((opt: any) => ({ return options?.map((opt: any) => ({
...opt, ...opt,

View File

@ -81,10 +81,6 @@ const LayoutModel = ({
formik.resetForm(); formik.resetForm();
} }
}, [isOpen]); }, [isOpen]);
console.log(formik.initialValues);
console.log(formik?.values);
return <Form className="w-100"> return <Form className="w-100">

View File

@ -32,8 +32,8 @@ const TableHeader = () => {
const handleOpenModel = () =>{ const handleOpenModel = () =>{
handel_open_model(ModalEnum?.RE_SELLER_COLLECTION_ADD); handel_open_model(ModalEnum?.RE_SELLER_COLLECTION_ADD);
} }
const deleteMutation = useDeleteReseller(); const deleteMutation = useDeleteReseller();
return ( return (
<div className="TableWithHeader single_student"> <div className="TableWithHeader single_student">

View File

@ -1,6 +1,5 @@
import React from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { FaImage, FaStore } from "react-icons/fa"; import {FaStore } from "react-icons/fa";
import ImageBoxField from "./ImageBoxField/ImageBoxField"; import ImageBoxField from "./ImageBoxField/ImageBoxField";
const AttachmentForm = () => { const AttachmentForm = () => {

View File

@ -2,7 +2,6 @@ import { useTranslation } from "react-i18next";
import { FaStore } from "react-icons/fa"; import { FaStore } from "react-icons/fa";
import ValidationField from "../../../../../Components/ValidationField/ValidationField"; import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { statusType } from "../../../../../config/statusType"; import { statusType } from "../../../../../config/statusType";
import { Col } from "antd";
const PersonalDetailsForm = () => { const PersonalDetailsForm = () => {
const [t] = useTranslation(); const [t] = useTranslation();

View File

@ -2,7 +2,6 @@ import { useTranslation } from "react-i18next";
import { FaStore } from "react-icons/fa"; import { FaStore } from "react-icons/fa";
import ValidationField from "../../../../../Components/ValidationField/ValidationField"; import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { nationalities } from "../../../../../types/App"; import { nationalities } from "../../../../../types/App";
import { CiEdit } from "react-icons/ci";
const TitleDetailsForm = () => { const TitleDetailsForm = () => {
const [t] = useTranslation(); const [t] = useTranslation();

View File

@ -1,4 +1,3 @@
import React from 'react'
import TabHeader from './TabHeader' import TabHeader from './TabHeader'
import NotificationCard from './Notification/NotificationCard' import NotificationCard from './Notification/NotificationCard'
import { NotificationData } from '../../../../faker/item' import { NotificationData } from '../../../../faker/item'

View File

@ -1,7 +1,5 @@
import React from 'react'
import TabHeader from './TabHeader' import TabHeader from './TabHeader'
import SecurityCard from './SecuritySetting/SecurityCard' import SecurityCard from './SecuritySetting/SecurityCard'
import { Button } from 'antd'
import { SecurityData } from './SecuritySetting/SecurityData' import { SecurityData } from './SecuritySetting/SecurityData'
import { SettingType } from '../../../../types/Setting' import { SettingType } from '../../../../types/Setting'
@ -9,19 +7,18 @@ const SecuritySetting = () => {
return ( return (
<div className='security_setting'> <div className='security_setting'>
<TabHeader <TabHeader
name='security_setting' name='security_setting'
description='upload_your_photo_and_personal_data_here' description='upload_your_photo_and_personal_data_here'
> >
</TabHeader> </TabHeader>
<div className='security_setting_body'> <div className='security_setting_body'>
{SecurityData?.map((e:SettingType)=>( {SecurityData?.map((e:SettingType)=>(
<SecurityCard <SecurityCard
name={e?.name} name={e?.name}
description={e?.description} description={e?.description}
children={e?.children}/> children={e?.children}/>
))} ))}
</div>
</div>
</div> </div>
) )
} }

View File

@ -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 (
<div className='setting_email_address'>
{canEditEmail && <EditSettingButton onClick={handleOpenModel}/>}
<EditEmailModel />
<span>{t("input.verify")} <FaCheck/></span>
<p>{t("admin@example.com")}</p>
</div>
)
}
export default EmailAddress

View File

@ -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 (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.Email_EDIT}
modelTitle="email_address"
handleSubmit={handleSubmit}
getInitialValues={getInitialValuesEmail(objectToEdit)}
getValidationSchema={getValidationSchemaEmail}
isAddModal={false}
>
<EmailForm />
</LayoutModel>
</>
);
};
export default EditModel;

View File

@ -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 (
<>
<LayoutModel
status={status as QueryStatusEnum}
ModelEnum={ModalEnum.PHONE_EDIT}
modelTitle="phone_number"
handleSubmit={handleSubmit}
getInitialValues={getInitialValuesPhone(objectToEdit)}
getValidationSchema={getValidationSchemaPhone}
isAddModal={false}
>
<PhoneForm />
</LayoutModel>
</>
);
};
export default EditModel;

View File

@ -0,0 +1,27 @@
import { Col, Row } from "reactstrap";
import ValidationField from "../../../../../../Components/ValidationField/ValidationField";
export const PhoneForm = () => {
return (
<Row className="w-100">
<Col>
<ValidationField placeholder="phone_number" label="phone_number" name="phone_number" />
</Col>
</Row>
);
};
PhoneForm;
export const EmailForm = () => {
return (
<Row className="w-100">
<Col>
<ValidationField placeholder="email_address" label="email_address" name="email_address" />
</Col>
</Row>
);
};
EmailForm;

View File

@ -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"),
});
};

View File

@ -1,13 +1,10 @@
import { useTranslation } from "react-i18next" import { useTranslation } from "react-i18next"
import ValidationField from "../../../../../Components/ValidationField/ValidationField";
import { ReactElement } from "react";
import { SettingType } from "../../../../../types/Setting"; import { SettingType } from "../../../../../types/Setting";
const SecurityCard = ({ const SecurityCard = ({
name, name,
description, description,
children children}:SettingType) => {
}:SettingType) => {
const {t} = useTranslation(); const {t} = useTranslation();

View File

@ -1,46 +1,52 @@
import { Button } from "antd";
import { SettingType } from "../../../../../types/Setting"; 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[] = [ export const SecurityData:SettingType[] = [
{ {
name:"password", name:"password",
description:"Set a unique password to protect the account", description:"Set a unique password to protect the account",
children:<Button className='security_setting_button'>تغيير كلمة المرور</Button>, children:<SecuritySettingButton name="تغيير كلمة المرور"/>,
}, },
{ {
name:"two_factors", name:"two_factors",
description:"Receive codes via SMS or email every time you log in", description:"Receive codes via SMS or email every time you log in",
// children:<Button className='button'> change</Button>, children:<SwitchButton />,
}, },
{ {
name:"verify_phone_number", name:"verify_phone_number",
description:"The phone number associated with the account", description:"The phone number associated with the account",
// children:<Button className='button'> change</Button>, children:<VerifyPhone/>,
}, },
{ {
name:"email_address", name:"email_address",
description:"The email address associated with the account", description:"The email address associated with the account",
// children:<Button className='button'> change</Button>, children:<EmailAddress/>,
}, },
{ {
name:"device_management", name:"device_management",
description:"Devices associated with the account", description:"Devices associated with the account",
children:<Button className='security_setting_button'>إدارة</Button>, children:<SecuritySettingButton name="إدارة"/>,
}, },
{ {
name:"account_activity", name:"account_activity",
description:"account_activities", description:"account_activities",
children:<Button className='security_setting_button'>عرض</Button>, children:<SecuritySettingButton name="عرض"/>,
}, },
{ {
name:"deactivate_the_account", name:"deactivate_the_account",
description:"This will close your account. Your account will be interactive when you log in again", description:"This will close your account. Your account will be interactive when you log in again",
children:<Button className='security_setting_button'>الغاء تنشيط </Button>, children:<SecuritySettingButton name="الغاء تنشيط"/>,
}, },
{ {
name:"delete_account", name:"delete_account",
description:"Your account will be permanently deleted", description:"Your account will be permanently deleted",
children:<Button className='security_setting_button security_setting_button_danger'>حذف</Button>, children:<SecuritySettingButton name="حذف" danger={true}/>,
}, },
] ]

View File

@ -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 (
<div className='setting_verify_phone'>
{canEditPhone && <EditSettingButton onClick={handleOpenModel} />}
<EditPhoneModel/>
<span>{t("input.verify")} <FaCheck/></span>
<p>{t("0965289543")}</p>
</div>
)
}
export default VerifyPhone

View File

@ -1,13 +1,11 @@
import React, { useState } from 'react'; import React, { lazy, useState } from 'react';
import type { TabsProps } from 'antd'; import type { TabsProps } from 'antd';
import { Tabs } from 'antd'; import { Tabs } from 'antd';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CgProfile } from "react-icons/cg";
import { useWindowResize } from '../../../../Hooks/useWindowResize'; import { useWindowResize } from '../../../../Hooks/useWindowResize';
import Card from '../../../ReSeller/Notifications/Card'; const FileSetting = lazy(() => import("./FileSetting"));
import FileSetting from './FileSetting'; const SecuritySetting = lazy(() => import("./SecuritySetting"));
import SecuritySetting from './SecuritySetting'; const Notification = lazy(() => import("./Notification"));
import Notification from './Notification';
type TabPosition = 'left' | 'right' | 'top' | 'bottom'; type TabPosition = 'left' | 'right' | 'top' | 'bottom';

View File

@ -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 { useTranslation } from "react-i18next";
import { lazy, Suspense } from "react"; import { Suspense } from "react";
import { Spin } from "antd"; import { Spin } from "antd";
import useSetPageTitle from "../../../Hooks/useSetPageTitle"; import useSetPageTitle from "../../../Hooks/useSetPageTitle";
import PageHeader from "../../../Layout/Dashboard/PageHeader"; import PageHeader from "../../../Layout/Dashboard/PageHeader";

View File

@ -339,14 +339,14 @@ export const CrudRoute: TCrudRoute[] = [
abilities_value: ABILITIES_VALUES_ENUM.INDEX, abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, prevPath: 0,
}, },
{ // {
header: "page_header.setting", // header: "page_header.setting",
element: <Setting />, // element: <Setting />,
path: `/${ABILITIES_ENUM?.SETTING}`, // path: `/${ABILITIES_ENUM?.SETTING}`,
abilities: ABILITIES_ENUM?.SETTING, // abilities: ABILITIES_ENUM?.SETTING,
abilities_value: ABILITIES_VALUES_ENUM.INDEX, // abilities_value: ABILITIES_VALUES_ENUM.INDEX,
prevPath: 0, // prevPath: 0,
}, // },
//// RE_SELLER //// RE_SELLER
{ {
header: "page_header.notifications", header: "page_header.notifications",

View File

@ -150,4 +150,48 @@
border: 2px solid var(--warning) !important; border: 2px solid var(--warning) !important;
background: 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;
} }

View File

@ -215,4 +215,12 @@ export enum ModalEnum {
/// sales /// sales
Sales_ADD = "Sales.add", Sales_ADD = "Sales.add",
/// phone
PHONE_EDIT = "Phone.edit",
/// email
Email_EDIT = "Email.edit",
} }

View File

@ -56,8 +56,9 @@ export enum ABILITIES_ENUM {
Profile_RE_SELLER = "profile_re_seller" , Profile_RE_SELLER = "profile_re_seller" ,
Sales = "sales", Sales = "sales",
Collections = "collections", Collections = "collections",
SETTING = "setting" SETTING = "setting",
Email = "email",
Phone = "phone"
//// ////
} }

View File

@ -380,7 +380,9 @@
"managers":"مدراء", "managers":"مدراء",
"manager":"مدير", "manager":"مدير",
"sale":"عملية بيع", "sale":"عملية بيع",
"collections": "التحصيلات" "collections": "التحصيلات",
"phone_number":"رقم الهاتف",
"email_address":"عنوان البريد الإلكتروني"
}, },
"education_class_actions": { "education_class_actions": {
"Student_Records": "سجلات الطلاب", "Student_Records": "سجلات الطلاب",
@ -508,7 +510,8 @@
"empty":"", "empty":"",
"role":"الدور", "role":"الدور",
"submit_password":"تأكيد كلمة المرور", "submit_password":"تأكيد كلمة المرور",
"join_date":"تاريخ الانضمام" "join_date":"تاريخ الانضمام",
"verify":"التحقق"
}, },
"select": { "select": {
"enums": { "enums": {

View File

@ -736,3 +736,18 @@ export const canDeleteSales = hasAbility(
ABILITIES_ENUM.Sales, ABILITIES_ENUM.Sales,
ABILITIES_VALUES_ENUM.DELETE, 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,
);