import navigationConfig from "configs/NavigationConfig";
import useAuth from "hooks/useAuth";
import useUserType from "hooks/useType";
const { SubMenu } = Menu;
const { useBreakpoint } = Grid;
const setLocale = (isLocaleOn, localeKey) =>
isLocaleOn ? <IntlMessage id={localeKey} /> : localeKey.toString();
const setDefaultOpen = (key) => {
let keyList = [];
let keyString = "";
if (key) {
const arr = key.split("-");
for (let index = 0; index < arr.length; index++) {
const elm = arr[index];
index === 0 ? (keyString = elm) : (keyString = `${keyString}-${elm}`);
keyList.push(keyString);
}
}
return keyList;
};
const useConstructor = (callBack = () => { }) => {
const [hasBeenCalled, setHasBeenCalled] = useState(false);
const data = callBack();
if (hasBeenCalled) return data;
setHasBeenCalled(true);
return data;
};
const SideNavContent = (props) => {
const {
sideNavTheme,
routeInfo,
hideGroupTitle,
localization,
onMobileNavToggle,
} = props;
const data = useConstructor(() => {
return navigationConfig;
});
const IsAuth = (premission) => {
const { isAuth: isAuthenticated } = useAuth([premission]);
return isAuthenticated;
};
const ExactAuth = (premission) => {
const { exact } = useAuth([premission]);
return exact;
};
const IsType = (requiredType) => {
const { hasRequiredType } = useUserType([requiredType]);
return hasRequiredType;
};
const isMobile = !utils.getBreakPoint(useBreakpoint()).includes("lg");
const closeMobileNav = () => {
if (isMobile) {
onMobileNavToggle(false);
}
};
return (
<Menu
theme={sideNavTheme === SIDE_NAV_LIGHT ? "light" : "dark"}
mode="inline"
style={{ height: "100%", borderRight: 0 }}
defaultSelectedKeys={[routeInfo?.key]}
defaultOpenKeys={setDefaultOpen(routeInfo?.key)}
className={hideGroupTitle ? "hide-group-title" : ""}
>
{data.map((menu) =>
menu.submenu.length > 0 ? (
menu.exactAuth !== true ? (
IsAuth(menu.premission) ? (
(menu.userType === undefined || IsType(menu.userType)) ? (
<Menu.ItemGroup
key={menu.key}
title={setLocale(localization, menu.title)}
>
{menu.submenu.map((subMenuFirst) =>
subMenuFirst.submenu.length > 0 ? (
IsAuth(subMenuFirst.premission) ? (
<SubMenu
icon={
subMenuFirst.icon ? (
<Icon type={subMenuFirst?.icon} />
) : null
}
key={subMenuFirst.key}
title={setLocale(localization, subMenuFirst.title)}
>
{subMenuFirst.submenu.map((subMenuSecond) =>
IsAuth(subMenuSecond.premission) ? (
<Menu.Item key={subMenuSecond.key}>
{subMenuSecond.icon ? (
<Icon type={subMenuSecond?.icon} />
) : null}
<span>
{setLocale(localization, subMenuSecond.title)}
</span>
<Link
onClick={() => closeMobileNav()}
to={subMenuSecond.path}
/>
</Menu.Item>
) : null
)}
</SubMenu>
) : null
) : IsAuth(subMenuFirst.premission) ? (
(subMenuFirst.userType === undefined || IsType(subMenuFirst.userType)) ? (
<Menu.Item key={subMenuFirst.key}>
{subMenuFirst.icon ? (
<Icon type={subMenuFirst.icon} />
) : null}
<span>{setLocale(localization, subMenuFirst.title)}</span>
<Link
onClick={() => closeMobileNav()}
to={subMenuFirst.path}
/>
</Menu.Item>
) : null
) : null
)}
</Menu.ItemGroup>
) : null
) : null
) : ExactAuth(menu.premission) ? (
<Menu.ItemGroup
key={menu.key}
title={setLocale(localization, menu.title)}
>
{menu.submenu.map((subMenuFirst) =>
subMenuFirst.submenu.length > 0 ? (
IsAuth(subMenuFirst.premission) ? (
<SubMenu
icon={
subMenuFirst.icon ? (
<Icon type={subMenuFirst?.icon} />
) : null
}
key={subMenuFirst.key}
title={setLocale(localization, subMenuFirst.title)}
>
{subMenuFirst.submenu.map((subMenuSecond) =>
IsAuth(subMenuSecond.premission) ? (
<Menu.Item key={subMenuSecond.key}>
{subMenuSecond.icon ? (
<Icon type={subMenuSecond?.icon} />
) : null}
<span>
{setLocale(localization, subMenuSecond.title)}
</span>
<Link
onClick={() => closeMobileNav()}
to={subMenuSecond.path}
/>
</Menu.Item>
) : null
)}
</SubMenu>
) : null
) : IsAuth(subMenuFirst.premission) ? (
<Menu.Item key={subMenuFirst.key}>
{subMenuFirst.icon ? (
<Icon type={subMenuFirst.icon} />
) : null}
<span>{setLocale(localization, subMenuFirst.title)}</span>
<Link
onClick={() => closeMobileNav()}
to={subMenuFirst.path}
/>
</Menu.Item>
) : null
)}
</Menu.ItemGroup>
) : null
) : IsAuth(menu.premission) ? (
<Menu.Item key={menu.key}>
{menu.icon ? <Icon type={menu?.icon} /> : null}
<span>{setLocale(localization, menu?.title)}</span>
{menu.path ? (
<Link onClick={() => closeMobileNav()} to={menu.path} />
) : null}
</Menu.Item>
) : null
)}
</Menu>
);
};
const MenuContent = (props) => {
return props.type === NAV_TYPE_SIDE ? (
<SideNavContent {...props} />
) : (
<TopNavContent {...props} />
);
};
const mapStateToProps = ({ theme }) => {
const { sideNavTheme, topNavColor } = theme;
return { sideNavTheme, topNavColor };
};
export default connect(mapStateToProps, { onMobileNavToggle })(MenuContent);
this is my menuContent component .
import {
DashboardOutlined,
UserOutlined,
TeamOutlined,
VideoCameraOutlined,
CalendarOutlined,
SettingOutlined,
DollarOutlined,
FileTextOutlined,
PlusOutlined,
BankOutlined
} from '@ant-design/icons';
import { APP_PREFIX_PATH } from 'configs/AppConfig'
import useUserType from "hooks/useType";
const IsType = (requiredType) => {
const { hasRequiredType } = useUserType([requiredType]);
return hasRequiredType;
};
const NewSideBar = [
{
key: 'dashboard',
path: `${APP_PREFIX_PATH}/dashboard`,
title: 'sidenav.dashboard',
icon: DashboardOutlined,
breadcrumb: false,
submenu: [
{
key: 'dashboard',
path: `${APP_PREFIX_PATH}/dashboard`,
title: 'sidenav.dashboard',
icon: DashboardOutlined,
premission:['ROLE_CLIENT'],
breadcrumb: false,
submenu: []
},
{
key: 'dashboard',
path: `${APP_PREFIX_PATH}/admin/home`,
title: 'sidenav.dashboard',
icon: DashboardOutlined,
breadcrumb: false,
premission:['ROLE_ADMIN'],
submenu: []
},
]
},
{
key: 'meetings',
path: `${APP_PREFIX_PATH}/meetings`,
title: 'meetings',
icon: DashboardOutlined,
breadcrumb: false,
premission:['ROLE_CLIENT'],
submenu: [
{
key: 'meetings',
path: `${APP_PREFIX_PATH}/meetings/all`,
title: 'meetings',
icon: VideoCameraOutlined,
breadcrumb: false,
submenu: []
},
{
key: 'calendar',
path: `${APP_PREFIX_PATH}/meetings/calendar`,
title: 'calendar',
icon: CalendarOutlined,
breadcrumb: false,
submenu: []
}
]
},
{
key: 'users',
path: `${APP_PREFIX_PATH}/users`,
title: 'Members',
icon: TeamOutlined,
breadcrumb: false,
premission:['ROLE_CLIENT'],
submenu: [
{
key: 'users',
path: `${APP_PREFIX_PATH}/users/member`,
title: 'members',
icon: UserOutlined,
submenu: [],
breadcrumb: false,
}
]
},
{
key: 'teams',
path: `${APP_PREFIX_PATH}/team`,
title: 'Teams',
icon: TeamOutlined,
breadcrumb: false,
premission:['ROLE_CLIENT'],
userType:"owner",
submenu: [
{
key: 'add_team',
path: `${APP_PREFIX_PATH}/team/add`,
title: 'Add team',
icon: PlusOutlined,
breadcrumb: false,
submenu: [],
},
{
key: 'team_list',
path: `${APP_PREFIX_PATH}/team/list`,
title: 'Team list',
icon: TeamOutlined,
breadcrumb: false,
submenu: []
},
]
},
{
key: 'users',
path: `${APP_PREFIX_PATH}/users`,
title: 'Users',
breadcrumb: false,
premission:['ROLE_ADMIN'],
submenu: [
{
key: 'add_user',
path: `${APP_PREFIX_PATH}/admin/users/add`,
title: 'Add user',
icon: PlusOutlined,
submenu: [],
breadcrumb: false,
},
{
key: 'users',
path: `${APP_PREFIX_PATH}/admin/users/list`,
title: 'User list',
icon: TeamOutlined,
submenu: [],
breadcrumb: false,
},
]
},
{
key: 'organisation',
path: `${APP_PREFIX_PATH}/organisations`,
title: 'Users',
breadcrumb: false,
premission:['ROLE_ADMIN'],
submenu: [
{
key: 'add_organisation',
path: `${APP_PREFIX_PATH}/admin/organisations/add`,
title: 'Add organisation',
icon: PlusOutlined,
submenu: [],
breadcrumb: false,
},
{
key: 'organisations',
path: `${APP_PREFIX_PATH}/admin/organisations/list`,
title: 'Organisation list',
icon: BankOutlined,
submenu: [],
breadcrumb: false,
},
]
},
{
key: 'settings',
path: `${APP_PREFIX_PATH}`,
title: 'settings',
icon: TeamOutlined,
breadcrumb: false,
premission:['ROLE_CLIENT'],
submenu: [
{
key: 'settings',
path: `${APP_PREFIX_PATH}/setting`,
title: 'settings',
icon: SettingOutlined,
breadcrumb: false,
submenu: []
}
]
},
{
key: 'pricing',
path: `${APP_PREFIX_PATH}/plan`,
title: 'pricing',
icon: DashboardOutlined,
breadcrumb: false,
premission:['ROLE_CLIENT'],
submenu: [
{
key: 'pricing',
path: `${APP_PREFIX_PATH}/plan`,
title: 'pricing',
icon: DollarOutlined,
breadcrumb: false,
submenu: []
},
]
},
{
key: 'plan',
path: `${APP_PREFIX_PATH}/admin/plan`,
title: 'Plan',
icon: DollarOutlined,
breadcrumb: false,
premission:['ROLE_ADMIN'],
submenu: [
{
key: 'add-plan',
path: `${APP_PREFIX_PATH}/admin/plan/add`,
title: 'Add plan',
icon: PlusOutlined ,
breadcrumb: false,
submenu: []
},
{
key: 'plans',
path: `${APP_PREFIX_PATH}/admin/plan/list`,
title: 'Plan list',
icon: FileTextOutlined,
breadcrumb: false,
submenu: []
},
]
},
{
key: 'feature',
path: `${APP_PREFIX_PATH}/admin/feature`,
title: 'feature',
icon: DollarOutlined,
breadcrumb: false,
premission:['ROLE_ADMIN'],
submenu: [
{
key: 'add-feature',
path: `${APP_PREFIX_PATH}/admin/feature/add`,
title: 'Add feature',
icon: PlusOutlined ,
breadcrumb: false,
submenu: []
},
{
key: 'features',
path: `${APP_PREFIX_PATH}/admin/feature/list`,
title: 'Feature list',
icon: FileTextOutlined,
breadcrumb: false,
submenu: []
},
]
},
]
const navigationConfig = [
...NewSideBar
]
export default navigationConfig;
and this is the NavigationConfig component.
import React, { useEffect, useState } from "react";
import { Menu, Dropdown, Button, Avatar, Modal, Form, Input, message, Upload, Tooltip } from "antd";
import { useHistory } from "react-router-dom/cjs/react-router-dom.min";
import { connect, useDispatch } from "react-redux";
import { ChangeOrganisation } from "redux/actions";
import { PlusOutlined, RollbackOutlined, BankOutlined } from '@ant-design/icons';
import OrganisationService from "services/OrganisationService";
import { env } from "configs/EnvironmentConfig";
export const NavOrganisation = (props) => {
const { ChangeOrganisation, name, icon, id, username } = props;
const [ownedOrganisation, setOwnedOrganisation] = useState([]);
const [joinedOrganisation, setJoinedOrganisation] = useState([]);
const history = useHistory();
const dispatch = useDispatch();
const [showJoinModel, setShowJoinModel] = useState(false)
useEffect(() => {
OrganisationService.GetOwnedOrganisation().then((response) => {
setOwnedOrganisation(response.data);
});
OrganisationService.GetJoinedOrganisations().then((response) => {
setJoinedOrganisation(response.data);
});
}, [id]);
const userTypeInOrganisation = (organisation) => {
return organisation.members.find((member) => member.username === username).type;
}
const JoinModel = props => {
const { visible, onCancel } = props
const [form] = Form.useForm()
const [join, setJoin] = useState(true)
const handleSendRequest = () => {
const { code } = form.getFieldsValue(['code']);
OrganisationService.SendJoinRequest(code).then((resp) => {
message.success(resp.data.message);
onCancel();
form.resetFields();
});
}
const handleCreateOrganisation = () => {
const { name, phone, adresse, icon } = form.getFieldsValue(['name', 'phone', 'adresse', 'icon']);
const formData = new FormData();
formData.append('name', name);
formData.append('phone', phone);
formData.append('adresse', adresse);
if (icon && icon.fileList.length > 0) {
formData.append('icon', icon.fileList[0].originFileObj);
}
OrganisationService.CreateOrganisation(formData).then((resp) => {
message.success('Organisation created successfully');
onCancel();
form.resetFields();
dispatch(ChangeOrganisation({
id: resp.data.id,
name: resp.data.name,
isOwner: false,
userType: "owner",
"code": resp.data.code,
"icon": resp.data.icon
}));
history.push("/app/home");
});
}
return (
<Modal
visible={visible}
width={650}
closable={false}
title={join ? 'Join Organisation' : 'Create Organisation'}
footer={[
<Button key="back" className="btn btn-light" onClick={onCancel}>
Cancel
</Button>,
<Button type={'primary'} onClick={() => {
form.validateFields(['code'])
.then((values) => {
if (Object.values(values).every(value => value)) {
handleSendRequest();
setJoin(true);
} else {
setJoin(true);
}
})
.catch(() => {
message.error('Please put the code');
setJoin(true);
});
}} >
Join
</Button>,
<Button
type={'primary'}
ghost
onClick={() => {
form.validateFields(['name', 'phone', 'adresse'])
.then((values) => {
if (Object.values(values).every(value => value)) {
handleCreateOrganisation();
setJoin(false);
} else {
setJoin(false);
}
})
.catch(() => {
message.error('Please fill all the fields');
setJoin(false);
});
}}
>
Create
</Button>
]}
centered
>
<Form form={form} labelCol={{ span: 8 }}>
{join ?
<Form.Item name={'code'} label={'Organisation Code'} rules={[
{
required: true,
message: 'Please input the organisation code'
}
]}>
<Input />
</Form.Item>
:
<>
<Form.Item name={'name'} label={'Organisation Name'} rules={[
{
required: true,
message: 'Please input the organisation name'
}
]}>
<Input />
</Form.Item>
<Form.Item name={'phone'} label={'Organisation phone'} rules={[
{
required: true,
message: 'Please input the organisation phone'
}
]}>
<Input />
</Form.Item>
<Form.Item name={'adresse'} label={'Organisation addresse'} rules={[
{
required: true,
message: 'Please input the organisation address'
}
]}>
<Input />
</Form.Item>
<Form.Item name={'icon'} label={'Organisation icon'}>
<Upload action="/upload.do" listType="picture-card">
<button
style={{
border: 0,
background: 'none',
}}
type="button"
>
<PlusOutlined />
<div
style={{
marginTop: 8,
}}
>
Upload
</div>
</button>
</Upload>
</Form.Item>
</>
}
</Form>
</Modal>
)
}
const organisationMenu = (
<div className="nav-profile nav-dropdown">
<div className="nav-profile-header d-flex justify-content-between">
<div className="ml-2" onClick={() => setShowJoinModel(true)} style={{ cursor: 'pointer' }}>
<Avatar size={30}><PlusOutlined /></Avatar>
<span className="ml-2">Join Organisation</span>
</div>
<div className="ml-2" onClick={(e) => {
dispatch(ChangeOrganisation({
id: "",
name: "",
isOwner: false,
userType: "",
"code": "",
}))
}}
style={{ cursor: 'pointer' }}
>
<Tooltip title="Back to Profile">
<Avatar size={30} style={{ backgroundColor: 'white' }}>
<RollbackOutlined style={{ color: 'black' }} />
</Avatar>
</Tooltip>
</div>
</div>
<div className="nav-profile-body">
<Menu>
{ownedOrganisation.map((organisation) => (
<Menu.Item
key={organisation.id}
onClick={(e) => {
dispatch(ChangeOrganisation({
id: organisation.id,
name: organisation.name,
isOwner: true,
userType: "owner",
"code": organisation.code,
"icon": organisation.icon,
"phone": organisation.phone,
}));
history.push("/app/home");
}}
className="d-flex justify-content-between"
>
<div>
{organisation.icon ?
<Avatar src={env.IMG_SRC + organisation.icon}></Avatar>
:
<Avatar>{organisation.name}</Avatar>
}
<span className="ml-2">{organisation.name}</span>
</div>
{/* <span>{organisation.code}</span> */}
</Menu.Item>
))}
{joinedOrganisation.map((organisation) => (
<Menu.Item
key={organisation.id}
onClick={(e) => {
dispatch(ChangeOrganisation({
id: organisation.id,
name: organisation.name,
isOwner: false,
userType: userTypeInOrganisation(organisation),
"code": organisation.code,
"icon": organisation.icon,
"phone": organisation.phone,
}));
history.push("/app/home");
}}
className="d-flex justify-content-between"
>
<div>
{organisation.icon ?
<Avatar src={env.IMG_SRC + organisation.icon}></Avatar>
:
<Avatar>{organisation.name}</Avatar>
}
<span className="ml-2">{organisation.name}</span>
</div>
{/* <span>{organisation.code}</span> */}
</Menu.Item>
))}
</Menu>
</div>
</div>
);
return (
<>
{ownedOrganisation.length === 0 && joinedOrganisation.length === 0 ?
<Button className="mt-3" onClick={() => setShowJoinModel(true)}>
<PlusOutlined />
<span>New</span>
</Button>
:
<Dropdown
placement="bottomRight"
overlay={organisationMenu}
trigger={["click"]}
>
<Menu className="d-flex align-item-center" mode="horizontal">
<Menu.Item>
<Tooltip title="Organisations">
{id ?
icon ?
<Avatar src={env.IMG_SRC + icon}></Avatar>
:
<Avatar>{name}</Avatar>
:
<Avatar size={50} className="mb-1 bg-white" icon={<BankOutlined style={{ color: 'black' }} />} />
}
</Tooltip>
</Menu.Item>
</Menu>
</Dropdown>
}
<JoinModel
visible={showJoinModel}
onCancel={() => setShowJoinModel(false)}
/>
</>
);
};
const mapStateToProps = ({ org, auth }) => {
const { id, name, type, isOwner, icon } = org;
const { username } = auth;
return { id, name, type, isOwner, icon, username };
};
export default connect(mapStateToProps, { ChangeOrganisation })(NavOrganisation);
and this is the NavOrganisation component wher ei change the org state
the issue is when i wanna chage in the org state in the redux store i expect the menu content to change i mean hide or show items , but instead i get this error :Error: Rendered more hooks than during the previous render.
2
Answers
i change the menuContent component to this , and still get the same error
React hooks should be called on the component level, not inside the function that is defined inside the React component.
For example:
here useAuth is a hook that is being used inside the function that is not a component by itself. The right approach would be to move useAuth to the SideNavContent component.
If this is not solving your issue share me the complete stack trace.