Admiral. Roles and access division in the menu
Table of contents
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 usecases we can break down for you, share ideas in the comments!
Subscribe to my newsletter
Read articles from dev.family directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
dev.family
dev.family
Hi! We're dev.family or family of developers. It doesn't mean that we're all blood kin, but we are all united by the love of cool projects, complex and interesting tasks and technologies. dev.family is outsourcing company with a huge background and team of 30 wonderful peolple. We are focused on Custom Web & Mobile. Our main focus: e-commerce & food tech. Our capabilities range from product strategy, product design & development, and ongoing product maintenance services.