Skip to content

Commit

Permalink
Improved RoleGraph error handling, added a "Top 2 levels" select (#3667)
Browse files Browse the repository at this point in the history
* Improved RoleGraph error handling, added a "Top 2 levels" select

* Deleted logs

* CR with Lee
  • Loading branch information
jskupsik authored Jun 6, 2024
1 parent edcc869 commit ea1c7f7
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 29 deletions.
9 changes: 7 additions & 2 deletions admin/tabs/userData/roles/graph/RoleGraph.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@
* Copyright © 2024 Extremely Heavy Industries Inc.
*/
import {chart} from '@xh/hoist/cmp/chart';
import {errorBoundary} from '@xh/hoist/cmp/error';
import {div, hspacer, placeholder} from '@xh/hoist/cmp/layout';
import {creates, hoistCmp} from '@xh/hoist/core';
import {button} from '@xh/hoist/desktop/cmp/button';
import {buttonGroupInput, slider} from '@xh/hoist/desktop/cmp/input';
import {buttonGroupInput, slider, switchInput} from '@xh/hoist/desktop/cmp/input';
import {panel} from '@xh/hoist/desktop/cmp/panel';
import {toolbar} from '@xh/hoist/desktop/cmp/toolbar';
import {Icon} from '@xh/hoist/icon';
Expand All @@ -31,7 +32,7 @@ export const roleGraph = hoistCmp.factory({
item: div({
item: div({
style: {margin: 'auto'},
item: content()
item: errorBoundary(content())
}),
style: {
display: 'flex',
Expand Down Expand Up @@ -78,6 +79,10 @@ export const roleGraph = hoistCmp.factory({
max: 2,
stepSize: 0.005,
labelRenderer: false
}),
'Limit to one level',
switchInput({
bind: 'limitToOneLevel'
})
],
omit: !role
Expand Down
67 changes: 40 additions & 27 deletions admin/tabs/userData/roles/graph/RoleGraphModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {ChartModel} from '@xh/hoist/cmp/chart';
import {HoistModel, lookup, managed, PlainObject} from '@xh/hoist/core';
import {bindable, computed} from '@xh/hoist/mobx';
import {wait} from '@xh/hoist/promise';
import {isEmpty, isMatch, sortBy, sumBy} from 'lodash';
import {compact, isEmpty, isMatch, sortBy, sumBy} from 'lodash';
import {RoleModel} from '../RoleModel';
import {EffectiveRoleMember, HoistRole} from '../Types';

Expand All @@ -22,6 +22,8 @@ export class RoleGraphModel extends HoistModel {

@bindable widthScale: number = 1.0;

@bindable limitToOneLevel: boolean = true;

get relatedRoles(): EffectiveRoleMember[] {
const {role, relationship} = this;
if (!role) return [];
Expand All @@ -35,7 +37,7 @@ export class RoleGraphModel extends HoistModel {

@computed
get size() {
const {inverted, maxDepth, leafCount, widthScale} = this;
const {inverted, leafCount, maxDepth, widthScale} = this;
if (inverted) {
const AVG_WIDTH = 150,
AVG_HEIGHT = 26;
Expand All @@ -57,7 +59,7 @@ export class RoleGraphModel extends HoistModel {
const {chartModel} = this;
this.addReaction(
{
track: () => [this.role, this.relationship],
track: () => [this.role, this.relationship, this.limitToOneLevel],
run: async ([role]) => {
chartModel.clear(); // avoid HC rendering glitches
await wait();
Expand Down Expand Up @@ -86,15 +88,15 @@ export class RoleGraphModel extends HoistModel {
// Implementation
// -------------------------------
private getSeriesData(): PlainObject[] {
const {role, relatedRoles} = this,
{name} = role;
const {role, relatedRoles, limitToOneLevel} = this,
{name: rootName} = role;
if (isEmpty(relatedRoles)) return [];
const alreadyAdded = new Set<string>();
return [
{
id: name,
id: rootName,
// Replace spaces with non-breaking spaces to prevent wrapping.
name: name.replaceAll(' ', '&nbsp'),
name: rootName.replaceAll(' ', '&nbsp'),
dataLabels: {
style: {
fontWeight: 600
Expand All @@ -105,24 +107,28 @@ export class RoleGraphModel extends HoistModel {
fillColor: 'var(--xh-bg-alt)'
}
},
...sortBy(relatedRoles, 'name').flatMap(({name, sourceRoles}) =>
[...sourceRoles]
.sort((a, b) => {
if (a === role.name) return -1;
if (b === role.name) return 1;
return a > b ? 1 : -1;
})
.map(source => {
// Adds a space to the id to differentiate subsequent nodes from the single expanded one.
const id = alreadyAdded.has(name) ? `${name} ` : name;
alreadyAdded.add(name);
return {
id,
// Replace spaces with non-breaking spaces to prevent wrapping.
name: name.replaceAll(' ', '&nbsp'),
parent: source
};
})
...compact(
sortBy(relatedRoles, 'name').flatMap(({name, sourceRoles}) =>
[...sourceRoles]
.sort((a, b) => {
if (a === role.name) return -1;
if (b === role.name) return 1;
return a > b ? 1 : -1;
})
.map(source => {
// Omit all non-root nodes if limitToOneLevel is true
if (limitToOneLevel && source !== rootName) return null;
// Adds a space to the id to differentiate subsequent nodes from the single expanded one.
const id = alreadyAdded.has(name) ? `${name} ` : name;
alreadyAdded.add(name);
return {
id,
// Replace spaces with non-breaking spaces to prevent wrapping.
name: name.replaceAll(' ', '&nbsp'),
parent: source
};
})
)
)
];
}
Expand Down Expand Up @@ -184,7 +190,11 @@ export class RoleGraphModel extends HoistModel {

@computed
private get leafCount(): number {
const {relatedRoles} = this;
const {relatedRoles, limitToOneLevel, role} = this;
// Limit to one level means that we only show the direct children of the root role.
if (limitToOneLevel)
return sumBy(relatedRoles, it => (it.sourceRoles.includes(role.name) ? 1 : 0));

return sumBy(relatedRoles, it => {
const hasChildren = relatedRoles.some(other => other.sourceRoles.includes(it.name)),
parentCount = it.sourceRoles.length;
Expand All @@ -195,8 +205,11 @@ export class RoleGraphModel extends HoistModel {

@computed
private get maxDepth(): number {
const {role: root, relatedRoles} = this;
const {role: root, relatedRoles, limitToOneLevel} = this;
// Only the root node.
if (isEmpty(relatedRoles)) return 1;
// Limit to one level means that we only show two levels.
if (limitToOneLevel) return 2;

const maxDepthRecursive = (roleName: string) => {
if (roleName === root.name) return 1;
Expand Down

0 comments on commit ea1c7f7

Please sign in to comment.