Merge pull request #1127 from JakeStanger/fix-orgchart

[react-tree-orgchart] Updates and bugfixes
This commit is contained in:
Laura Kokkarinen 2020-02-05 09:57:14 +02:00 committed by GitHub
commit 7a9bbdda46
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 9278 additions and 6035 deletions

View File

@ -3,7 +3,7 @@
"solution": {
"name": "react-tree-orgchart-client-side-solution",
"id": "d76a0c4f-d669-42eb-9533-68d5cec5e9d3",
"version": "1.0.0.0",
"version": "1.0.1.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false

File diff suppressed because it is too large Load Diff

View File

@ -11,10 +11,10 @@
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.7.1-plusbeta",
"@microsoft/sp-lodash-subset": "1.7.1-plusbeta",
"@microsoft/sp-office-ui-fabric-core": "1.7.1-plusbeta",
"@microsoft/sp-webpart-base": "1.7.1-plusbeta",
"@microsoft/sp-core-library": "1.10.0",
"@microsoft/sp-lodash-subset": "1.10.0",
"@microsoft/sp-office-ui-fabric-core": "1.10.0",
"@microsoft/sp-webpart-base": "1.10.0",
"@pnp/common": "^1.2.9",
"@pnp/graph": "^1.2.9",
"@pnp/logging": "^1.2.9",
@ -34,10 +34,11 @@
"@types/react": "16.4.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.7.1-plusbeta",
"@microsoft/sp-tslint-rules": "1.7.1-plusbeta",
"@microsoft/sp-module-interfaces": "1.7.1-plusbeta",
"@microsoft/sp-webpart-workbench": "1.7.1-plusbeta",
"@microsoft/rush-stack-compiler-3.7": "^0.2.3",
"@microsoft/sp-build-web": "1.10.0",
"@microsoft/sp-tslint-rules": "1.10.0",
"@microsoft/sp-module-interfaces": "1.10.0",
"@microsoft/sp-webpart-workbench": "1.10.0",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",

View File

@ -0,0 +1,27 @@
import {WebPartContext} from "@microsoft/sp-webpart-base";
import {sp} from "@pnp/sp";
export default class SPServices {
constructor(private context: WebPartContext) {
sp.setup({
spfxContext: this.context
});
}
public async getUserProperties(user: string) {
return await sp.profiles.getPropertiesFor(user);
}
/**
* async GetUserProfileProperty
* user:string
*/
public async getUserProfileProperty(user: string, property: string) {
let UserProperty: any = await sp.profiles.getUserProfilePropertyFor(
user,
property
);
return UserProperty;
}
}

View File

@ -1,39 +0,0 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { graph } from "@pnp/graph";
import { sp } from '@pnp/sp';
import { ITreeChildren } from "../webparts/treeOrgChart/components/ITreeChildren";
export default class spservices {
constructor(private context:WebPartContext) {
sp.setup({
spfxContext: this.context
});
}
public async getUserProperties(user:string){
let currentUserProperties:any = await sp.profiles.getPropertiesFor(user);
console.log(currentUserProperties);
return currentUserProperties;
}
/**
* async GetUserProfileProperty
user:string */
public async getUserProfileProperty(user:string,property:string) {
let UserProperty:any = await sp.profiles.getUserProfilePropertyFor(user, property);
console.log(UserProperty);
return UserProperty;
}
}

View File

@ -2,30 +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 { escape } from '@microsoft/sp-lodash-subset';
import SortableTree from 'react-sortable-tree';
import 'react-sortable-tree/style.css';
import { IPersonaSharedProps, Persona, PersonaSize, PersonaPresence } from 'office-ui-fabric-react/lib/Persona';
import { IconButton, IButtonProps } 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 treeChildren: ITreeChildren[];
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
@ -36,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();
}
}
@ -51,25 +65,27 @@ 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);
treeManagers ?
this.treeData.push(treeManagers)
: null;
const treeManagers = await this.buildOrganizationChart(
currentUserProperties
);
if (treeManagers) this.treeData.push(treeManagers);
} else {
const treeManagers = await this.buildMyTeamOrganizationChart(currentUserProperties);
treeManagers ?
const treeManagers = await this.buildMyTeamOrganizationChart(
currentUserProperties
);
if (treeManagers)
this.treeData.push({
title: (treeManagers.person),
title: treeManagers.person,
expanded: true,
children: treeManagers.treeChildren
})
: null;
});
}
console.log(JSON.stringify(this.treeData));
this.setState({ treeData: this.treeData, isLoading: false });
}
@ -79,9 +95,14 @@ export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, IT
*/
public async buildOrganizationChart(currentUserProperties: any) {
// Get Managers
let treeManagers: ITreeData = null;
if (currentUserProperties.ExtendedManagers && currentUserProperties.ExtendedManagers.length > 0) {
treeManagers = await this.getUsers(currentUserProperties.ExtendedManagers[0]);
let treeManagers: ITreeData | null = null;
if (
currentUserProperties.ExtendedManagers &&
currentUserProperties.ExtendedManagers.length > 0
) {
treeManagers = await this.getUsers(
currentUserProperties.ExtendedManagers[0]
);
}
return treeManagers;
}
@ -89,51 +110,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;
}
@ -143,75 +186,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}
@ -225,20 +302,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={() => {
window.open(
`https://eur.delve.office.com/?p=${rowInfo.node.title.props.tertiaryText}&v=work`
);
}}
/>
],
]
})}
/>
</div>
</div>
);
}
}

View File

@ -1,4 +1,5 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,