fix(react-tree-orgchart): crash on init when requesting undefined user

The app was crashing on init when requesting users that do not exist. This adds a couple of checks to fallback to rendering a blank card instead.
This commit is contained in:
Jake Stanger 2020-01-28 14:07:59 +00:00
parent 27878aa71f
commit 7cf7f69d22
1 changed files with 163 additions and 81 deletions

View File

@ -2,28 +2,38 @@
// Author: João Mendes
// Fev 2019
//
import * as React from 'react';
import styles from './TreeOrgChart.module.scss';
import { ITreeOrgChartProps } from './ITreeOrgChartProps';
import { ITreeOrgChartState } from './ITreeOrgChartState';
import SortableTree from 'react-sortable-tree';
import 'react-sortable-tree/style.css';
import { IPersonaSharedProps, Persona, PersonaSize } from 'office-ui-fabric-react/lib/Persona';
import { IconButton } from 'office-ui-fabric-react/lib/Button';
import * as React from "react";
import styles from "./TreeOrgChart.module.scss";
import { ITreeOrgChartProps } from "./ITreeOrgChartProps";
import { ITreeOrgChartState } from "./ITreeOrgChartState";
import SortableTree from "react-sortable-tree";
import "react-sortable-tree/style.css";
import {
IPersonaSharedProps,
Persona,
PersonaSize
} from "office-ui-fabric-react/lib/Persona";
import { IconButton } from "office-ui-fabric-react/lib/Button";
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import spservice from '../../../services/SPServices';
import { ITreeChildren } from './ITreeChildren';
import { ITreeData } from './ITreeData';
import { Spinner, SpinnerSize } from 'office-ui-fabric-react/lib/components/Spinner';
import SPService from "../../../services/SPServices";
import { ITreeChildren } from "./ITreeChildren";
import { ITreeData } from "./ITreeData";
import {
Spinner,
SpinnerSize
} from "office-ui-fabric-react/lib/components/Spinner";
export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, ITreeOrgChartState> {
export default class TreeOrgChart extends React.Component<
ITreeOrgChartProps,
ITreeOrgChartState
> {
private treeData: ITreeData[];
private SPService: spservice;
private SPService: SPService;
constructor(props) {
super(props);
this.SPService = new spservice(this.props.context);
this.SPService = new SPService(this.props.context);
this.state = {
treeData: [],
isLoading: true
@ -34,8 +44,14 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
this.setState({ treeData });
}
public async componentDidUpdate(prevProps: ITreeOrgChartProps, prevState: ITreeOrgChartState) {
if (this.props.currentUserTeam !== prevProps.currentUserTeam || this.props.maxLevels !== prevProps.maxLevels) {
public async componentDidUpdate(
prevProps: ITreeOrgChartProps,
prevState: ITreeOrgChartState
) {
if (
this.props.currentUserTeam !== prevProps.currentUserTeam ||
this.props.maxLevels !== prevProps.maxLevels
) {
await this.loadOrgchart();
}
}
@ -49,18 +65,23 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
public async loadOrgchart() {
this.setState({ treeData: [], isLoading: true });
const currentUser = `i:0#.f|membership|${this.props.context.pageContext.user.loginName}`;
const currentUserProperties = await this.SPService.getUserProperties(currentUser);
const currentUserProperties = await this.SPService.getUserProperties(
currentUser
);
this.treeData = [];
// Test if show only my Team or All Organization Chart
if (!this.props.currentUserTeam) {
const treeManagers = await this.buildOrganizationChart(currentUserProperties);
if (treeManagers)
this.treeData.push(treeManagers);
const treeManagers = await this.buildOrganizationChart(
currentUserProperties
);
if (treeManagers) this.treeData.push(treeManagers);
} else {
const treeManagers = await this.buildMyTeamOrganizationChart(currentUserProperties);
const treeManagers = await this.buildMyTeamOrganizationChart(
currentUserProperties
);
if (treeManagers)
this.treeData.push({
title: (treeManagers.person),
title: treeManagers.person,
expanded: true,
children: treeManagers.treeChildren
});
@ -76,8 +97,13 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
public async buildOrganizationChart(currentUserProperties: any) {
// Get Managers
let treeManagers: ITreeData | null = null;
if (currentUserProperties.ExtendedManagers && currentUserProperties.ExtendedManagers.length > 0) {
treeManagers = await this.getUsers(currentUserProperties.ExtendedManagers[0]);
if (
currentUserProperties.ExtendedManagers &&
currentUserProperties.ExtendedManagers.length > 0
) {
treeManagers = await this.getUsers(
currentUserProperties.ExtendedManagers[0]
);
}
return treeManagers;
}
@ -85,51 +111,73 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
// Get user from Top Manager
*/
private async getUsers(manager: string) {
let person: any;
let spUser: IPersonaSharedProps = {};
// Get User Properties
const managerProperties = await this.SPService.getUserProperties(manager);
const imageInitials: string[] = managerProperties.DisplayName.split(' ');
const imageInitials: string[] = managerProperties.DisplayName.split(" ");
// Persona Card Properties
spUser.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
spUser.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
spUser.imageInitials = `${imageInitials[0]
.substring(0, 1)
.toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
spUser.text = managerProperties.DisplayName;
spUser.tertiaryText = managerProperties.Email;
spUser.secondaryText = managerProperties.Title;
// PersonaCard component
person = <Persona {...spUser} hidePersonaDetails={false} size={PersonaSize.size40} />;
person = (
<Persona
{...spUser}
hidePersonaDetails={false}
size={PersonaSize.size40}
/>
);
// Has DirectReports
if (managerProperties.DirectReports && managerProperties.DirectReports.length > 0) {
const usersDirectReports: any[] = await this.getChildren(managerProperties.DirectReports);
if (
managerProperties.DirectReports &&
managerProperties.DirectReports.length > 0
) {
const usersDirectReports: any[] = await this.getChildren(
managerProperties.DirectReports
);
// return treeData
return { title: (person), expanded: true, children: usersDirectReports };
return { title: person, expanded: true, children: usersDirectReports };
// Don't have DirectReports
} else {
// return treeData
return { title: (person) };
return { title: person };
}
}
// Get Children (user DirectReports)
private async getChildren(userDirectReports: any[]) {
let treeChildren: ITreeChildren[] = [];
let spUser: IPersonaSharedProps = {};
for (const user of userDirectReports) {
const managerProperties = await this.SPService.getUserProperties(user);
const imageInitials: string[] = managerProperties.DisplayName.split(' ');
const imageInitials: string[] = managerProperties.DisplayName.split(" ");
spUser.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
spUser.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
spUser.imageInitials = `${imageInitials[0]
.substring(0, 1)
.toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
spUser.text = managerProperties.DisplayName;
spUser.tertiaryText = managerProperties.Email;
spUser.secondaryText = managerProperties.Title;
const person = <Persona {...spUser} hidePersonaDetails={false} size={PersonaSize.size40} />;
const usersDirectReports = await this.getChildren(managerProperties.DirectReports);
const person = (
<Persona
{...spUser}
hidePersonaDetails={false}
size={PersonaSize.size40}
/>
);
const usersDirectReports = await this.getChildren(
managerProperties.DirectReports
);
usersDirectReports ? treeChildren.push({ title: (person), children: usersDirectReports }) :
treeChildren.push({ title: (person) });
usersDirectReports
? treeChildren.push({ title: person, children: usersDirectReports })
: treeChildren.push({ title: person });
}
return treeChildren;
}
@ -139,75 +187,109 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
@parm: currentUserProperties
*/
private async buildMyTeamOrganizationChart(currentUserProperties: any) {
let manager: IPersonaSharedProps = {};
let me: IPersonaSharedProps = {};
let treeChildren: ITreeChildren[] = [];
let peer: IPersonaSharedProps = {};
let imageInitials: string[];
let hasManager:boolean = false;
let managerCard: any;
let hasManager: boolean = false;
let managerCard: any;
// Get My Manager
const myManager = await this.SPService.getUserProfileProperty(currentUserProperties.AccountName, 'Manager');
const myManager = await this.SPService.getUserProfileProperty(
currentUserProperties.AccountName,
"Manager"
);
// Get My Manager Properties
if (myManager){
const managerProperties = await this.SPService.getUserProperties(myManager);
imageInitials = managerProperties.DisplayName.split(' ');
// PersonaCard Props
manager.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
manager.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
manager.text = managerProperties.DisplayName;
manager.tertiaryText = managerProperties.Email;
manager.secondaryText = managerProperties.Title;
// PersonaCard Component
managerCard = <Persona {...manager} hidePersonaDetails={false} size={PersonaSize.size40} />;
if (myManager) {
const managerProperties = await this.SPService.getUserProperties(
myManager
);
imageInitials = managerProperties.DisplayName?.split(" ").map(name => name[0]);
// PersonaCard Props
manager.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
if (imageInitials)
manager.imageInitials = `${imageInitials[0]}${imageInitials[1]}`.toUpperCase();
manager.text = managerProperties.DisplayName;
manager.tertiaryText = managerProperties.Email;
manager.secondaryText = managerProperties.Title;
// PersonaCard Component
managerCard = (
<Persona
{...manager}
hidePersonaDetails={false}
size={PersonaSize.size40}
/>
);
hasManager = true;
}
// Get my Properties
const meImageInitials: string[] = currentUserProperties.DisplayName.split(' ');
const meImageInitials: string[] = currentUserProperties.DisplayName.split(
" "
);
me.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${currentUserProperties.Email}`;
me.imageInitials = `${meImageInitials[0].substring(0, 1).toUpperCase()}${meImageInitials[1].substring(0, 1).toUpperCase()}`;
me.imageInitials = `${meImageInitials[0]
.substring(0, 1)
.toUpperCase()}${meImageInitials[1].substring(0, 1).toUpperCase()}`;
me.text = currentUserProperties.DisplayName;
me.tertiaryText = currentUserProperties.Email;
me.secondaryText = currentUserProperties.Title;
const meCard = <Persona {...me} hidePersonaDetails={false} size={PersonaSize.size40} />;
const usersDirectReports: any[] = await this.getChildren(currentUserProperties.DirectReports);
const meCard = (
<Persona {...me} hidePersonaDetails={false} size={PersonaSize.size40} />
);
const usersDirectReports: any[] = await this.getChildren(
currentUserProperties.DirectReports
);
// Current USer Has Manager
if (hasManager) {
treeChildren.push({ title: (meCard), expanded: true, children: usersDirectReports });
}else{
treeChildren = usersDirectReports;
managerCard = meCard;
treeChildren.push({
title: meCard,
expanded: true,
children: usersDirectReports
});
} else {
treeChildren = usersDirectReports;
managerCard = meCard;
}
// Get MyPeers
for (const userPeer of currentUserProperties.Peers) {
const peerProperties = await this.SPService.getUserProperties(userPeer);
imageInitials = peerProperties.DisplayName.split(' ');
imageInitials = peerProperties.DisplayName.split(" ");
peer.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${peerProperties.Email}`;
peer.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
peer.imageInitials = `${imageInitials[0]
.substring(0, 1)
.toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
peer.text = peerProperties.DisplayName;
peer.tertiaryText = peerProperties.Email;
peer.secondaryText = peerProperties.Title;
const peerCard = <Persona {...peer} hidePersonaDetails={false} size={PersonaSize.size40} />;
treeChildren.push({ title: (peerCard) });
const peerCard = (
<Persona
{...peer}
hidePersonaDetails={false}
size={PersonaSize.size40}
/>
);
treeChildren.push({ title: peerCard });
}
// Return
return { 'person': managerCard, 'treeChildren': treeChildren };
return { person: managerCard, treeChildren: treeChildren };
}
// Render
public render(): React.ReactElement<ITreeOrgChartProps> {
return (
<div className={styles.treeOrgChart}>
<WebPartTitle displayMode={this.props.displayMode}
<WebPartTitle
displayMode={this.props.displayMode}
title={this.props.title}
updateProperty={this.props.updateProperty} />
{
this.state.isLoading ? <Spinner size={SpinnerSize.large} label="Loading Organization Chart ..."></Spinner> : null
}
updateProperty={this.props.updateProperty}
/>
{this.state.isLoading ? (
<Spinner
size={SpinnerSize.large}
label="Loading Organization Chart ..."
></Spinner>
) : null}
<div className={styles.treeContainer}>
<SortableTree
treeData={this.state.treeData}
@ -221,20 +303,20 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
<IconButton
disabled={false}
checked={false}
iconProps={{ iconName: 'ContactInfo' }}
iconProps={{ iconName: "ContactInfo" }}
title="Contact Info"
ariaLabel="Contact"
onClick={(ev) => {
window.open(`https://eur.delve.office.com/?p=${rowInfo.node.title.props.tertiaryText}&v=work`);
onClick={ev => {
window.open(
`https://eur.delve.office.com/?p=${rowInfo.node.title.props.tertiaryText}&v=work`
);
}}
/>
],
]
})}
/>
</div>
</div>
);
}
}