How to exclude certain item in a map with a certain condition?

Viewed 86

I'm developing a menu page, where users can see the menu's items based on their roles.

Currently, I have 3 pages: Bulletin Board, Info Hub and Settings

So naturally, I have 3 roles: Admin Role (can access all 3 of the pages), Bulletin Board (can only access Bulletin Board), Info Hub (can only access Info Hub)

So users can have a different roles, for example, if they have Bulletin Board and Info Hub, then they can access both of them, but not the Settings page (only "Admin Role" can see Settings), so I want to hide the Settings in this menu that I've already developed and rendered using map.

Or if the user has all 3 roles including Admin Role, then they can see everything as well.

I'm taking the loginList prop from an API and passing it into the AllAppsCentre.js to determine which menu items to show, but I just can't figure out the logic to do a filter or indexOf at the map.

In the codesandbox that I've created, the user has all 3 roles.

AllAppsCentre.js(map function to display the menu items)

useEffect(() => {
    const loginListFromParent = loginList;

    const showAll = loginListFromParent.some(
      (item) =>
        item.permissionName.includes("Admin Role") &&
        item.permissionType.includes("view")
    );

    const showBulletin = loginListFromParent.some(
      (item) =>
        item.permissionName.includes("Bulletin Board") &&
        item.permissionType.includes("view")
    );

    const showInfoHub = loginListFromParent.some(
      (item) =>
        item.permissionName.includes("Info Hub") &&
        item.permissionType.includes("view")
    );

    if (loginListFromParent) {
      setShowAll(showAll);
      setShowBulletin(showBulletin);
      setShowInfoHub(showInfoHub);
    }
  }, [loginList]);

return (
{AllAppsCentreData
   .filter((item) => {
       .map((item, index) => {
           return (
              <Col key={index} xs={6} md={3}>
                  <div className={item.className}>
                      <Link to={item.path}>
                           {item.icon}
                           <Row>
                              <span className='apps-title'>
                                 {item.title}
                              </span>
                           </Row>
                      </Link>
                  </div>
              </Col>
           )
})}
)

AllAppsCentreData.js

import * as IoIcons from 'react-icons/io'
import * as MdIcons from 'react-icons/md'

export const AllAppsCentreData = [
    {
        title: 'Bulletin Board',
        path: '/bulletinboard',
        icon: <IoIcons.IoIosPaper size={80} />,
        className: 'row text-center apps-centre-icon'
    },
    {
        title: 'Info Hub',
        path: '/infohub',
        icon: <MdIcons.MdDeviceHub size={80} />,
        className: 'row text-center apps-centre-icon'
    },
    {
        title: 'Settings',
        path: '/settings',
        icon: <IoIcons.IoMdSettings size={80} />,
        className: 'row text-center apps-centre-icon'
    },
]

I've been trying to figure out how to deal with this but I just couldn't think of a solution, if all else fails, I might just remove the map method and just copy and paste my AllAppsCentreData's items and move it directly into the AllAppsCentre page instead so I can do ternary operations at the menu items.

If there is any better way to do this menu with the role-based display, feel free to let me know as I also want to learn the optimal way to do something like this.

Edit affectionate-danny-qvnwj

1 Answers

To start off, there is no variable in your code that is responsible for holding current user's permissions (or I can't see one). So I made it up:

const currentUsersPermissions = useGetPermissions() // get users permissions
// the above can be "admin", "bulletin" or "info-hub"

Then, you need to first filter your array and then map it (currently, you are using map inside of filter). To do it easier and correctly, you could add a property called requiredPermission (or something simillar) to your AllAppsCentreData objects:

{
    title: 'Bulletin Board',
    path: '/bulletinboard',
    icon: <IoIcons.IoIosPaper size={80} />,
    className: 'row text-center apps-centre-icon',
    requiredPermission: "bulletin" // and info-hub for Info Hub
},

Then, while rendering and mapping, you can do it like this:

return (
{AllAppsCentreData
   .filter(
       // below, you return true if permission is admin because he can see everything
       // and true or false depending if permissions are other than that but
       // the same as required by AllAppsCentreData array objects
       (item) => currentUsersPermissions === "admin" ? true : currentUsersPermissions === item.requiredPermission
   ).map((item, index) => {
       return (
          <Col key={index} xs={6} md={3}>
              <div className={item.className}>
                  <Link to={item.path}>
                       {item.icon}
                       <Row>
                          <span className='apps-title'>
                             {item.title}
                          </span>
                       </Row>
                  </Link>
              </div>
          </Col>
       )
)

So, to sum up:

  1. I have created a variable to hold current users' permission
  2. I have added a property to each AllAppsCentreData's objects, which contains info about permission required to show it
  3. I have filtered the array right before mapping it and rendering, so it contains only the data I want to render
    Also, I think you are lost in using filter method, so perhaps read this and try doing some easier excercises to get a feeling how it works: MDN on Array.filter method. The thing you need to pay attention is that you pass a function into filter, and this function should return true or false depending on whether you want your item included in the output array.
Related