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

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.

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:
I have created a variable to hold current users' permission
I have added a property to each AllAppsCentreData's objects, which contains info about permission required to show it
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

Iterate nested object to access the key and values in react js

I have a nested object which is getting from api response, need to iterate that nested object based keys and values and the structure like this,
Have tried but not getting the expected output.
Code: Api response
{
"dashboard": "Dashboard",
"users": "Users",
"page_builder": "Page Builder",
"filemanager": {
"brand": "Brand Images",
"manufacturer": "Manufacturer Images"
},
"catalog": {
"catalog_product": "Product"
},
"coupon": "Coupon",
"egift": "E-gifting",
"paymentconfig": {
"configuration": "Gateway Config",
},
"app": {
"app_general": "General Config",
"forceupdate_config": "Force Update Config",
},
"apppayment": "Kapture Category",
"kapturecrm": "Vertical Master",
"phpinfo": "PHP Info"
}
When i tried from my end, am getting the output like this,
Tried sample code:
{Object.keys(roletest).map((key, idx) => (
<CFormCheck
id="validationrole_access"
key={idx}
name="role_access"
label={roletest[key]}
value={roletest[key]}
onChange={handleChange}
aria-describedby="inputGroupPrepend"
/>
))}
My Expected output:
Dashboard
Users
Page Builder
filemanager
Brand Images
Manufacturer Images
catalog
Product
Coupon
E-gifting
paymentconfig
Gateway Config
app
General Config
Force Update Config
Kapture Category
Vertical Master
PHP Info
My output:
Dashboard
Users
Page Builder
Coupon
E-gifting
Kapture Category
Vertical Master
PHP Info
Please do my needs
It sounds like you want to create a nested list. Which can actually be done quite easily. You were on the right track using an object method to iterate over the properties but Object.entries might be a little easier.
So, the trick is to make sure you use separate components for the list, and the list items (List/ListItem). In List, as you iterate over the items check if an item is an object. If it is create a new list with the List component, otherwise return a list item.
const data={dashboard:"Dashboard",users:"Users",page_builder:"Page Builder",filemanager:{brand:"Brand Images",manufacturer:"Manufacturer Images"},catalog:{catalog_product:"Product"},coupon:"Coupon",egift:"E-gifting",paymentconfig:{configuration:"Gateway Config"},app:{app_general:"General Config",forceupdate_config:"Force Update Config"},apppayment:"Kapture Category",kapturecrm:"Vertical Master",phpinfo:"PHP Info"};
function Example({ data }) {
return <List list={data} />;
}
function List({ list }) {
return (
<ul>
{Object.entries(list).map((item, key) => {
return <ListItem key={key} item={item} />;
})}
</ul>
);
}
function ListItem({ item }) {
const [label, value] = item;
if (typeof value === 'object') {
return (
<li>
{label}
<List list={value} />
</li>
);
}
return <li>{value}</li>;
}
ReactDOM.render(
<Example data={data} />,
document.getElementById('react')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/17.0.2/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/17.0.2/umd/react-dom.production.min.js"></script>
<div id="react"></div>

ReactJs : How to display a specific data of an object from an array of objects with a click

I have an Array with a list of objects , each object contains an ID ,title , job description and salary. I saved it in a separate file as below :
export const CareerList = [
{
id: 1,
title: "Junior Accountant",
salary: "1500$",
jobDescription: [
"Maintains financial records for subsidiary companies by analyzing balance sheets and general ledger accounts",
"Reconciles general and subsidiary bank accounts by gathering and balancing information",
"Provides financial status information by preparing special reports; completing special projects",
"Corrects errors by posting adjusting journal entries",
"Maintains general ledger accounts by reconciling accounts receivable detail and control accounts; adjusting entries for amortizations prepaids; analyzing and reconciling retainage and accounts payable ledgers; preparing fixed asset depreciation and accruals",
"Secures financial information by completing database backups; keeping information confidential",
"Maintains accounting controls by following policies and procedures; complying with federal, state, and local financial legal requirements",
"Updates job knowledge by participating in educational opportunities; reading professional publications",
"Accomplishes accounting and organization mission by completing related results as needed",
],
},
{
id: 2,
title: "Research Analyst",
salary: "3500$",
jobDescription: [
"Support the Director of Research & Impact and the Research Manager in implementing all phases of ANDE research projects",
"Design and administer surveys and conduct secondary data collection from online sources to aggregate data related to global SGB support.",
"Clean and analyze data to identify key trends, and develop reports communicating these insights to practitioners",
"Track new research developments related to SGBs and collect and synthesize this research for ANDE members.",
"Provide support in identifying and selecting consultants and interns to support research activities and coordinate with these consultants and interns to carry out research.",
"Manage the content of ANDE’s various online research portals, such as www.galidata.org, http://ecosystems.andeglobal.org, and www.andeglobal.org/knowledge-hub.",
"Manage administrative functions related to project funding (e.g. tracking expenses).",
],
},
I am trying to create two modals , one modal which only display the job titles with the button job details next to it , and if I clicked on a particular job button, the list modal should hide and another modal with that specific job details should show, any suggestion how it can be done?
import { CareerList } from "../data/Careers";
import ButtonMedium from "../UI/ButtonMedium";
import JobDetails from "./JobDetails";
const Backdrop = (props) => {
return <div className={classes.backdrop} onClick={props.onHide}></div>;
};
const CareerOverlay = () => {
const [showJobDetails, setShowJobDetails] = useState(false);
const displayJobDetails = () => {
setShowJobDetails(true);
};
return (
<>
<div className={classes.careerBox}>
{CareerList.map((job, index) => {
return (
<div className={classes.jobItem} key={index}>
<h2>{job.title}</h2>
<ButtonMedium onClick={displayJobDetails}>
Job Detail
</ButtonMedium>
{showJobDetails && (
<JobDetails careerList={CareerList} id={job.id} />
)}
</div>
);
})}
</div>
</>
);
};
const CareerOpportunities = (props) => {
return (
<>
{reactDom.createPortal(
<Backdrop onHide={props.onHide} />,
document.getElementById("backdrop")
)}
{reactDom.createPortal(
<CareerOverlay onShow={props.onShow} />,
document.getElementById("career")
)}
</>
);
};
export default CareerOpportunities;
import React from "react";
import classes from "./JobDetails.module.css";
const JobDetails = (props) => {
const particularJob = props.careerList.find((job) => job.is === props.id);
return (
<div className={classes.jobBox}>
<h1>{particularJob.title}</h1>
</div>
);
};
export default JobDetails;
Although the other answer seems alright, I'd also advise you to use portals for modals in React, and by that way you won't be bothered by stacking order when you're styling both modals.
Read more about portals here:
https://reactjs.org/docs/portals.html
You can split each item using new child component. So each item can manage modal state individually.
const CareerListItem = (props) => {
const [showJobDetails, setShowJobDetails] = useState(false);
const displayJobDetails = () => {
setShowJobDetails(true);
};
return (
<div className={classes.jobItem} key={index}>
<h2>{job.title}</h2>
<ButtonMedium onClick={displayJobDetails}>
Job Detail
</ButtonMedium>
{showJobDetails && (
<JobDetails job={props.job} />
)}
</div>
)
}
const CareerOverlay = () => {
return (
<>
<div className={classes.careerBox}>
{CareerList.map((job, index) => {
return (
<CareerListItem job={job} />
)
})}
</div>
</>
);
};
...
const JobDetails = (props) => {
return (
<div className={classes.jobBox}>
<h1>{props.job.title}</h1>
<div>details here...</div>
</div>
);
};

react tips not appearing?

I am working on a component right now that is a mapped stack of divs. Each one should have a tooltip but for the life of me I can't get the tooltip to appear
class App extends Component {
constructor() {
super();
this.state = {
options: [
{
id: '1',
label: 'Industrial Truck and Tractor Operators',
value: '53-7051',
tooltip_text: 'Operate industrial trucks or tractors equipped to move materials around a warehouse, storage yard, factory, construction site, or similar location. Excludes “Logging Equipment Operators" (45-4022).',
},
{
id: '2',
label: 'Order Clerks',
value: '43-4151',
tooltip_text: 'Receive and process incoming orders for materials, merchandise, classified ads, or services such as repairs, installations, or rental of facilities. Generally receives orders via mail, phone, fax, or other electronic means. Duties include informing customers of receipt, prices, shipping dates, and delays; preparing contracts; and handling complaints. Excludes "Dispatchers, Except Police, Fire, and Ambulance" (43-5032) who both dispatch and take orders for services.',
},
],
value: null,
className: '',
selectedClassName: '',
loading_State: true, loads
childrenCount: 0
};
this.setProps = this.setProps.bind(this);
}
setProps(newProps) { //this is going to update the state
this.setState(newProps);
}
render() {
return (
<div>
<DashControlledContainer
setProps={this.setProps}
options = {this.state.options}
value = {this.state.value}
styles = {this.state.styles}
className = {this.state.className}
selectedClassName = {this.state.selectedClassName}
loading_State = {this.state.loading_State}
childrenCount = {this.state.childrenCount}
/>
</div>
)
}
}
export default App;
//the component being returned with the tooltip
render(){
return (
<div style={this.props.styles}>
{this.props.options.map(option => (
<div key = {option} id={option.id} style={option.style}
onClick = {e=>{ //updates the props with the clicked targets value if setProps is accessible
if(this.props.setProps){
this.props.setProps({value: e.target.value})
}else{
this.setState({value:e.target.value})
}
}}
>
<span id={option.id}> {option.label} </span>
<UncontrolledTooltip placement="right" target={option}>
{option.tooltip_text}
</UncontrolledTooltip>
</div>
))}
</div>
);
}
I'm not sure where to set my target for the tooltip maybe thats the issue? I haven't been able to find many resources online. Any help would be appreciated.
I think you should provide id as a target to your UncontrolledTooltip,
<UncontrolledTooltip placement="right" target={option.id}> //set id of span as a target here
hello
</UncontrolledTooltip>
I was able to figure it out, including a string in my id fixed it for some reason?
i made my div contain id={"option"+option.id}
then I referenced it like so:
<UncontrolledTooltip placement="right" target={"option"+option.id}>
{option.tooltip_text}
</UncontrolledTooltip>

Can I specify a Divider or Header in Semantic UI React's options array for the dropdown component?

I am working with ReactJS and using SemanticUI for ReactJS to style the front end,
Is it possible to specify a header or divider from within the options array of objects for a dropdown component?
I get the impression from the documentation that this is not supported yet.
I solved this by changing to object in the options array to have more properties (which allow you to customise the content):
{
text: "YouGov Filters",
value: "yougov-header",
content: <Header content="YouGov Filters" color="teal" size="small" />,
disabled: true
},
It's probably not the ideal way to achieve what I want because I have to set disabled to true (I don't want it to be a selectable option) which means it adopts the greyed out 'disabled' style. I tried to counter this by specifying a color for the header which resulted in the disabled style being applied over the teal colour, not perfect but it will do for now.
Another workaround is to do it by map array:
const options = [
{
text: "note",
icon: 'sticky note outline',
description: 'test',
},
{
divider: true
},
{
text: "task",
icon: 'calendar check outline',
description: 'test',
},
];
return (
<Dropdown className='multicontent__button' text='add new' button>
<Dropdown.Menu>
<Dropdown.Header icon='tags' content='Tag Label' />
{options.map((option, i) => {
if (option.divider === true) return (<Dropdown.Divider key={i}/>);
return (
<Dropdown.Item
key={i}
text={option.text}
icon={option.icon}
description={option.description}
action={option.action}
onClick={this.handleOption}
/>
);
})}
</Dropdown.Menu>
</Dropdown>
);
Mr B's solution is genius. And it can be cleaner with a little modification of his:
function FragmentWithoutWarning({key, children}) {
// to get rid of the warning:
// "React.Fragment can only have `key` and `children` props."
return <React.Fragment key={key}>{children}</React.Fragment>;
}
// then just:
{
as: FragmentWithoutWarning,
content: <Header content="YouGov Filters" color="teal" size="small" />
}
Since <React.Fragment /> is not able to capture any event, you even don't have to disable the item.

React with Material UI Table Pagination show all entries

I have a react data table where I recently added pagination for when you have many entries. I wanted the option to show all entries in the table by selecting "all" in the rowsPerPageOptions dropdown menu. So far I managed to get the count of all entries to show up in the menu.
What I need now is to label the entries.length object with the string "all" and get that to show up in the menu. Is that possible?
When I try something like all.push({label: this.state.entries.length}); I get the error:
Objects are not valid as a React child (found: object with keys {label}). If you meant to render a collection of children, use an array instead.
That made me think that I can not use arrays with keys for the menu, so I have to show that value in a different way.
Code:
Edit: Moved the all variable into the render function after morteza ataiy commented and pointed out an error.
render() {
return (
let all = [5,10,25,50,(this.state.entries.length)];
<div>
<Table>
</Table>
</div>
<TablePagination
component="div"
count={this.state.entries.length}
rowsPerPage={this.state.rowsPerPage}
page={this.state.page}
backIconButtonProps={{
'aria-label': 'Previous Page',
}}
nextIconButtonProps={{
'aria-label': 'Next Page',
}}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
labelRowsPerPage="Reihen pro Seite:"
rowsPerPageOptions={all}
);
}
Image: The last entry is what I want to change
Please keep in mind that I am new to React and JavaScript, thanks in advance!
You can use label . plz refer the code
<TablePagination rowsPerPageOptions={[10, 50, { value: -1, label: 'All' }]} />
You can pass -1 to the backend and write query with respect to this.
just use it in render method:
render() {
let all = [5,10,25,50,(this.state.entries.length)];
return (
<div>
<Table>
</Table>
</div>
<TablePagination
component="div"
count={this.state.entries.length}
rowsPerPage={this.state.rowsPerPage}
page={this.state.page}
backIconButtonProps={{
'aria-label': 'Previous Page',
}}
nextIconButtonProps={{
'aria-label': 'Next Page',
}}
onChangePage={this.handleChangePage}
onChangeRowsPerPage={this.handleChangeRowsPerPage}
labelRowsPerPage="Reihen pro Seite:"
rowsPerPageOptions={all}
}

Categories

Resources