Hi everyone, just a reminder that we are developing Admiral - an open source solution on React, which allows you to quickly develop beautiful CRUDs in admin panels, but create fully custom interfaces if you want. From developers for developers with love, as the saying goes.
Our project is available here - https://github.com/dev-family/admiral
Today we want to share small tricks on how to create a menu in the control panel that will be generated depending on the user's role. This is quite a popular use case.
Static Menu
From the documentation you can see that out of the box comes an example of building a static menu. Nothing unusual - a menu component with Item (menu items) and SubItem (menu sub-items) with a title, a link to go to and an icon inside it.
import { Menu, SubMenu, MenuItemLink } from '../../admiral'
const CustomMenu = () => {
return (
<Menu>
<MenuItemLink icon="FiCircle" name="First Menu Item" to="/first" />
<SubMenu icon="FiCircle" name="Second Menu Item">
<MenuItemLink icon="FiCircle" name="Sub Menu Item" to="/second" />
</SubMenu>
</Menu>
)
}
export default CustomMenu
What if we want to manage this menu? Separate the menu by role? Make it available to guests? That's where creating a Dynamic Menu can help.
Dynamic Menu
In order to convert the menu from static to dynamic, we have to get it from somewhere. Let's turn to the documentation, more precisely to the section on interacting with API. Let's use the getIdentity method, which gives information about the authorized user, in this case each user will receive the menu available for him along with information about him. Let's go to the file menu.tsx.
Let's first define the basic structure of the menu.
interface ISubMenuItemChildren {
name: string
to: string
badge?: number
icon?: keyof typeof Icons
}
interface ISubMenuItem {
to?: string
name: string
badge?: number
icon?: keyof typeof Icons
children?: Array<ISubMenuItemChildren>
}
The fields from the name are self-explanatory, but let's break it down just in case:
- name - name of the menu item
- to - jump link
- badge - optional field that will display a number (total number of records in the menu item, it depends on what number you want to display).
- icon - icon
- children - array with sub-items
In the end, we plan to get such data using the getIdentity method:
name: "Dev Family",
user: {id: 4, name: "Dev Family", email: "info@dev.family",…},
email: "info@dev.family",
id: 4,
menu: [
{name: "Users", icon: "FiUsers", badge: null, to: "/users"},
{name: "Channels", icon: "FiPlayCircle", badge: null, to: "/channels"}
],
Let's try to parse the array of menu objects in the menu.tsx file, the resulting file looks like this:
import React, { useCallback, useEffect, useState } from 'react'
import { Menu, MenuItemLink, SubMenu, useGetIdentity } from '@devfamily/admiral'
import * as Icons from 'react-icons/fi'
interface ISubMenuItemChildren {
name: string
to: string
badge?: number
icon?: keyof typeof Icons
}
interface ISubMenuItem {
to?: string
name: string
badge?: number
icon?: keyof typeof Icons
children?: Array<ISubMenuItemChildren>
}
type SubMenuItemsType = Array<ISubMenuItem>
const CustomMenu = () => {
const { identity } = useGetIdentity()
const [filteredSubMenuItems, setFilteredSubMenuItems] = useState<SubMenuItemsType>([])
useEffect(() => {
if (identity?.menu && Array.isArray(identity?.menu)) {
setFilteredSubMenuItems(identity?.menu)
}
}, [identity?.menu])
const renderSubMenuItems = useCallback(
function () {
return (
<>
{filteredSubMenuItems.map(function (submenu, i) {
if (submenu.to) {
return (
<MenuItemLink
key={`${i}`}
icon={submenu.icon}
name={submenu.name}
to={submenu.to}
badge={{ count: submenu.badge, status: 'error' }}
/>
)
}
if (submenu.children) {
return (
<SubMenu
key={i}
icon={submenu.icon}
name={submenu.name}
badge={{ count: submenu.badge, status: 'error' }}
>
{submenu.children.map(function (contentItem, j) {
return (
<MenuItemLink
badge={{ count: contentItem.badge, status: 'warning' }}
key={`${i}_${j}`}
icon={contentItem.icon}
name={contentItem.name}
to={contentItem.to}
/>
)
})}
</SubMenu>
)
}
})}
</>
)
},
[filteredSubMenuItems],
)
return <Menu>{renderSubMenuItems()}</Menu>
}
export default CustomMenu
Now our menu is fetched from the API method getIdentity and generated dynamically. If you have other use cases we can break down for you, share ideas in the comments!
Top comments (0)