Use Case: In this document we are going to cover how to implement a specific use case in which a dashboard is created with certain panels. Then based on the logged role of the person, the dashboard will show some reports to some users, other set of reports to other role. In a similar way this method can also be implemented for organization, profile etc as well.
Implementation Steps:
- When creating the dashboard, set up all the reports that are going to be present in the dashboard, irrespective of which role the user belongs to.
- The number of reports visible at a time can be maximum up to all the reports that are added while creating dashboard
- Create the dashboard with all the reports that needs to displayed.
- Now right click on empty space on dashboard and select option Advance and go to JS section
- Paste the below given code in JS Editor , tick the Enable button and click on Apply button.
Code :
var reportDivIds = ['#html-item-pYOvN','#html-item-hp8DH','#html-item-JZFZp','#html-item-NZ2kG' , '#html-item-zfCrQ'];
var removedDivs = {};
var roles_reports_obj = {
'ROLE_ADMIN' : ['#html-item-pYOvN','#html-item-hp8DH','#html-item-JZFZp','#html-item-NZ2kG' , '#html-item-zfCrQ'],
'ROLE_USER' : ['#html-item-zfCrQ' , '#html-item-NZ2kG'],
'ROLE_VIEWER' : ['#html-item-JZFZp']
}
var reports_to_display = [];
const stylesObj = {};
let roles_of_current_user = [];
function positionTracker(){
const items = document.querySelectorAll('.react-grid-item.grid-item');
items.forEach((el, index) => {
stylesObj[index + 1] = el.getAttribute('style') || '';
});
console.log(stylesObj);
}
const removeReportsPromise = () => {
return new Promise((resolve) => {
const result = removeReports();
if (result) {
resolve();
} else {
setTimeout(resolve, 1000);
}
});
};
const roleFetching = async () => {
const stores = new Set();
const traverse = (element) => {
let store =
element?.memoizedState?.element?.props?.store
|| element?.pendingProps?.store
|| element?.stateNode?.store;
if (store) {
stores.add(store);
}
if (element.child) {
traverse(element.child);
}
};
const reactRoot = Array.from(document.querySelectorAll("*[id]")).find((el) => el?._reactRootContainer?._internalRoot?.current);
const internalRoot = reactRoot._reactRootContainer._internalRoot.current;
traverse(internalRoot);
let loggedInUserDetails = [...stores][0].getState().app.applicationSettingsData.userData.user
roles_of_current_user = loggedInUserDetails.roles ;
roles_of_current_user.forEach(function(role) {
if (roles_reports_obj[role]) {
console.log(roles_reports_obj[role] , 'roles_reports_obj[role]');
reports_to_display = reports_to_display.concat(roles_reports_obj[role]);
}
});
reports_to_display = [...new Set(reports_to_display)];
reports_to_display.sort((a, b) => {
return reportDivIds.indexOf(a) - reportDivIds.indexOf(b);
});
setTimeout(() =>{
addReportsBack()
},1000)
}
function addReportsBack() {
console.log(reports_to_display, 'reports_to_display');
reports_to_display.forEach(function(selector, index) {
var div = document.querySelector(selector);
if (!div && removedDivs[selector.substring(6)]) {
var restoredDiv = removedDivs[selector.substring(6)];
const styleString = stylesObj[String(index + 1)];
if (styleString) {
styleString.split(';').forEach((styleRule) => {
const [property, value] = styleRule.split(':').map(s => s && s.trim());
if (property && value) {
restoredDiv.style[property] = value;
}
});
}
document.querySelector('.react-grid-layout.layout').appendChild(restoredDiv);
div = document.querySelector(selector);
}
});
}
function removeReports(){
reportDivIds.forEach(function(selector) {
var div = document.querySelector(selector);
if (div) {
var removableDiv = document.getElementById(selector.substring(6));
console.log(removableDiv, 'removableDiv');
removableDiv.style.position = '';
removableDiv.style.left = '';
removableDiv.style.top = '';
removableDiv.style.right = '';
removableDiv.style.bottom = '';
removableDiv.style.width = '';
removableDiv.style.height = '';
removedDivs[removableDiv.id] = removableDiv;
removableDiv.remove();
}
});
layoutHeightAdjusting()
return true ;
}
setTimeout(() =>{
positionTracker();
removeReportsPromise();
},1000)
function layoutHeightAdjusting(){
var layout = document.querySelector('.react-grid-layout.layout');
if (layout) {
layout.setAttribute('style', 'height: auto !important');
}
}
window.addEventListener('resize', function() {
layoutHeightAdjusting()
});
roleFetching()
Code Explanation and Changes :
The above code handles the complete functionality for displaying reports dynamically. However, you may need to make a few adjustments to ensure it works with your specific dashboard setup.
Change 1 :
var reportDivIds = ['#html-item-pYOvN','#html-item-hp8DH','#html-item-JZFZp','#html-item-NZ2kG' , '#html-item-zfCrQ'];
Here reportDivIds holds all the ids of the all the reports that are present on the dashboard. Replace them with your reports id’s. You can get a specific id of a report by following below steps.
- Right click on the report and go to CSS section from Advance options. There you can see Component Id of the reports. That is the Id we are looking for. There is a copy button as well which can be used.
- Make sure to prefix each ID with #html-, as shown in the sample entries.
- Paste them in the same sequence you want the reports to appear on the dashboard.
Change 2 :
var roles_reports_obj = {
'ROLE_ADMIN' : ['#html-item-pYOvN','#html-item-hp8DH','#html-item-JZFZp','#html-item-NZ2kG' , '#html-item-zfCrQ'],
'ROLE_USER' : ['#html-item-zfCrQ' , '#html-item-NZ2kG'],
'ROLE_VIEWER' : ['#html-item-JZFZp']
}
- This object is mapping of role and the reports that he is going to see. From the above sample you can ‘ROLE_ADMIN’ can view all the reports so we have mentioned all the report id’s here. Whereas ‘ROLE_USER’ can only see two report, so he has only 2 report id’s. Similarly ‘ROLE_VIEWER’ has got access to only one report
- You can modify this as per your requirements.
Code Responsible for fetching Roles (No changes required) :
const roleFetching = async () => {
const stores = new Set();
const traverse = (element) => {
let store =
element?.memoizedState?.element?.props?.store
|| element?.pendingProps?.store
|| element?.stateNode?.store;
if (store) {
stores.add(store);
}
if (element.child) {
traverse(element.child);
}
};
const reactRoot = Array.from(document.querySelectorAll("*[id]")).find((el) => el?._reactRootContainer?._internalRoot?.current);
const internalRoot = reactRoot._reactRootContainer._internalRoot.current;
traverse(internalRoot);
let loggedInUserDetails = [...stores][0].getState().app.applicationSettingsData.userData.user
roles_of_current_user = loggedInUserDetails.roles ;
roles_of_current_user.forEach(function(role) {
if (roles_reports_obj[role]) {
console.log(roles_reports_obj[role] , 'roles_reports_obj[role]');
reports_to_display = reports_to_display.concat(roles_reports_obj[role]);
}
});
reports_to_display = [...new Set(reports_to_display)];
reports_to_display.sort((a, b) => {
return reportDivIds.indexOf(a) - reportDivIds.indexOf(b);
});
setTimeout(() =>{
addReportsBack()
},1000)
}
This function is responsible for fetching roles of the currently logged in user which are defined while creating the user.
Functionality of the Entire Code :
- When the dashboard is opened, the roleFetching() function is triggered, which calls all other functions. This includes removing reports and adding them back based on the currently logged-in user.
- While removing reports, we strip all position-related styling and store them in another object. The key of this object is the sequence in which the report IDs appear in reportDivIds. After this, all removed reports are moved to removedDivs.
- When adding the reports back based on the role, we assign the position styling to each report based on the order they will appear on the screen.
- For example, if a ROLE_USER is meant to see the reports starting from the 2nd report in the full list of report IDs, the 2nd report will get the styling of the first report (position, height, width), the 3rd report will get the styling of the 2nd report, and so on. This ensures there is no gap at the beginning or in between the reports.