Merge pull request #1743 from petkir/petkir-react-tree-orgchartgraph

This commit is contained in:
Hugo Bernier 2021-02-28 16:09:21 -05:00 committed by GitHub
commit 60cd3649bf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 717 additions and 358 deletions

View File

@ -3,15 +3,20 @@
## Summary
The Tree Organization WebPart shows the Organization Chart of the or the team, the web part reads infomation from current user to build the Organization Chart.
We can configure in the webpart properties if it show all Organization Chart or the only user team, (same manager and peers).
You can configure in the webpart properties:
* show all Organization Chart
* the only user team, (same manager and peers).
* show Organization Chart by picking up user
![Organization Chart Web Part](./assets/react-tree-orgchart.gif)
![Organization Chart Web Part](./assets/Screenshot1.png)
![Organization Chart Web Part](./assets/Screenshot2.png)
![Organization Chart Web Part](./assets/Screenshot3.png)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-1.10.0-green.svg)
@ -26,24 +31,37 @@ We can configure in the webpart properties if it show all Organization Chart or
Property |Type|Required| comments
--------------------|----|--------|----------
WebPart Title| Text| no|
Show Only My Team| Boolean | true
MaxDepth | Number| yes | Maximum number of levels to show
title | Text| no| WebPart Title
teamLeader |Text|no|UPN of manager if viewType is 4
viewType|viewType(number)|yes|if currentUserTeam is set it's not required
maxLevels | Number| yes | Maximum number of levels to show
detailBehavoir |boolean|no|Delve or Live Persona Card
excludefilter|boolean|no|Filter contains/not contains
filter|string|no|Filter Value
currentUserTeam|boolean|no|only in Interface to handle previouse installations
### viewType Enum:
```
MyTeam = 1,
CompanyHierarchy = 2,
ShowOtherTeam = 4
```
## Solution
Solution|Author(s)
--------|---------
Tree Organization WebPart|João Mendes
Tree Organization WebPart|Peter Paul Kirschner ([@petkir_at](https://twitter.com/petkir_at))
## Version history
Version|Date|Comments
-------|----|--------
1.0.2|June 12, 2020|Added exception handler for profiles missing display name
1.1.0|Feb 28, 2021|Added <ul><li>Show Other Team</li><li>Live Contact Card</li><li>Filter user by userPrincipalName</li><li>Graph API</li><li>PNPJS updates</li></ul>
1.0.2|June 12, 2020|Added exception handler for profiles missing display name
1.0.1|Jan 28, 2020|Update to SPFx 1.10, minor fixes and refactoring.
1.0.0|Feb 25, 2019|Initial release
1.0.0|Feb 25, 2019|Initial release
## Disclaimer
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 MiB

View File

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

View File

@ -1,6 +1,6 @@
{
"name": "react-tree-orgchart",
"version": "0.0.1",
"version": "1.0.2",
"lockfileVersion": 1,
"requires": true,
"dependencies": {
@ -2265,9 +2265,9 @@
}
},
"@microsoft/microsoft-graph-types": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.5.0.tgz",
"integrity": "sha512-eit9LMlhHLyzCByATGL8d7izWNRVBlzrPMfXqy2a9qIVUUNIGxyqkGJNrpEh3EVDXW2r+/ASsBAqzHkm8zWJiQ=="
"version": "1.7.0",
"resolved": "https://registry.npmjs.org/@microsoft/microsoft-graph-types/-/microsoft-graph-types-1.7.0.tgz",
"integrity": "sha512-Mxu5H+69F8T5NzV4+U8FkTvpIYYWHsmRZzfAuOlIO0zJJGlVyRIVqpq4NmOdUXGC00vZ73ONgCuzuaksxqDm/Q=="
},
"@microsoft/node-core-library": {
"version": "3.15.1",
@ -4013,12 +4013,12 @@
}
},
"@pnp/common": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/common/-/common-1.2.9.tgz",
"integrity": "sha512-wVu8yJZvdnN99hhpmHxhK0HbUVMg61y8o8vC2w3MK+nql65GH9YaxkDQotNSKlYjDakzG/HYa8cZW3TIaCFPcg==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/common/-/common-1.3.11.tgz",
"integrity": "sha512-RhYKcfMP+h0pAzORZRHSPPLOBB58djN/pfnorpWPjsx6ZxMqbiDqTzAtTF4m8z/mdNnxJr0Q3kwt4ImU3FjwnA==",
"requires": {
"adal-angular": "1.0.17",
"tslib": "1.9.3"
"tslib": "1.10.0"
},
"dependencies": {
"adal-angular": {
@ -4027,118 +4027,119 @@
"integrity": "sha1-bpNuDkH5HTsqiOf/ypwvb29WLMQ="
},
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/graph": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/graph/-/graph-1.2.9.tgz",
"integrity": "sha512-n1aH2HWmXXc0ZRBTnU9U3oz5Vua7jzw+zjRhkdQYh+AAnBcVD2drT+CaAJNB9RND5SpzajJ31GR+0cmDx7J2qw==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/graph/-/graph-1.3.11.tgz",
"integrity": "sha512-AbJdiCdI+SZTk7LI0NP5snw8D0udQWQbFRhCmI1e+j6k/Wt/FYEfXkgiAqXKc+f8+PpUtt1/ezab3sbrxs9btA==",
"requires": {
"@microsoft/microsoft-graph-types": "1.5.0",
"tslib": "1.9.3"
"@microsoft/microsoft-graph-types": "1.7.0",
"tslib": "1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/logging": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-1.2.9.tgz",
"integrity": "sha512-JNxoHkPhPnnC1PHnU+wQVkDkaBzrskDvLHn7bq84Uz+ppZEnKp0IuOxvmkAPMwD1NVP5qRbbiBh7dIHu8b9rNw==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/logging/-/logging-1.3.11.tgz",
"integrity": "sha512-hADlIXwvF/wjee7425nFJ6NhqaWpWTJ5yg02bpwBUsiSuFqEUf+LwuAcyHQre2lMs6KyNa65FWoRQok9BlZuxA==",
"requires": {
"tslib": "1.9.3"
"tslib": "1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/odata": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-1.2.9.tgz",
"integrity": "sha512-mE4bY5PQXxNYJIyh5Mgqda4AeX5DWEjT9NuUH7G05JOKwV51DbUX+UPIIy7AqBXDjo05S3QvOJw6+4OfeMQuZQ==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/odata/-/odata-1.3.11.tgz",
"integrity": "sha512-yMaRiuVZRei2pkryCOqsw3ZXD2Lw30IJv136WQmQPQPOxG4cvsS9+woXkfMqbWV2KQ1evFUqVXbitIz6eDVfNA==",
"requires": {
"tslib": "1.9.3"
"tslib": "1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/sp": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-1.2.9.tgz",
"integrity": "sha512-O+V/IrI/dqDtc/d42HsTlPbqNqLut6/UiTkWwuZMwbhJeNmb9S/CyygTaa2luyvVw2UaVa1x70zdoslxU0Itew==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/sp/-/sp-1.3.11.tgz",
"integrity": "sha512-NjdeGe81aukiSPelSPjgAFRC1+SrNPTXvTdEqTH+Q1ZvgNtk8bdZp6K6xf9emfeM2qZDOu9GpKZpg0W/emq++g==",
"requires": {
"tslib": "1.9.3"
"tslib": "1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/sp-clientsvc": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/sp-clientsvc/-/sp-clientsvc-1.2.9.tgz",
"integrity": "sha512-zaInYyQgbyk5a4+2KMr9qHRKHexqRQMiQk5eu6SM6fIEVyC5H422Tr5WREVzNTMp4YVl8MKuXkeTNxLhG7iTJQ==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/sp-clientsvc/-/sp-clientsvc-1.3.11.tgz",
"integrity": "sha512-eIUnmDWjizcWJzhWxAbfsxEyHF1dabkGlihnDnlcYGhtvh8BwuM67A57qc5fbxzCS59c0YU57szB1EucoNmV4A==",
"requires": {
"tslib": "1.9.3"
"tslib": "1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/sp-taxonomy": {
"version": "1.2.9",
"resolved": "https://registry.npmjs.org/@pnp/sp-taxonomy/-/sp-taxonomy-1.2.9.tgz",
"integrity": "sha512-pJCuikAI2ntplc1VoyIo5wbe2F44t9B6sA66L7LqOPc/i+qU8ywjkelHznmshUomOzPycuFXqwgYcnHyxJVCXw==",
"version": "1.3.11",
"resolved": "https://registry.npmjs.org/@pnp/sp-taxonomy/-/sp-taxonomy-1.3.11.tgz",
"integrity": "sha512-shzCSjmOlr6mojCXJkfD8Xf9lJnhphq4Fj6mdUQGwpak+VIU+Fogf6AI0j6AReCKtKsKyqfud9X7C8tH07C3DA==",
"requires": {
"tslib": "1.9.3"
"tslib": "1.10.0"
},
"dependencies": {
"tslib": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz",
"integrity": "sha512-4krF8scpejhaOgqzBEcGM7yDIEfi0/8+8zDRZhNZZ2kjmHJ4hv3zCbQWxoJGz1iw5U0Jl0nma13xzHXcncMavQ=="
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.10.0.tgz",
"integrity": "sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ=="
}
}
},
"@pnp/spfx-controls-react": {
"version": "1.11.0",
"resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.11.0.tgz",
"integrity": "sha512-7i2g3l95/CXpiugfM8V0m8aEHeyoRFBlTiZfoQiue5LW9O3XC+okDSbf8vOBNgddYTRLnRZKEo/TMKcuNEi3zQ==",
"version": "1.21.1",
"resolved": "https://registry.npmjs.org/@pnp/spfx-controls-react/-/spfx-controls-react-1.21.1.tgz",
"integrity": "sha512-HpBZtrtNfAMn9vySTEZutFovnwiYGg7skptLhf2gZmhSOUIBB0THweBRTDPqT8zBSkxc1ffVHAOArVgaMb+zMQ==",
"requires": {
"@pnp/common": "1.0.1",
"@pnp/logging": "1.0.1",
"@pnp/odata": "1.0.1",
"@pnp/sp": "1.0.1",
"@pnp/telemetry-js": "1.0.0",
"@pnp/telemetry-js": "2.0.0",
"@types/chart.js": "2.7.40",
"chart.js": "2.7.3",
"color": "^3.1.0",
"lodash": "4.17.4",
"office-ui-fabric-react": "5.131.0"
"color": "^3.1.2",
"lodash": "4.17.13",
"office-ui-fabric-react": "5.131.0",
"react-quill": "1.3.3"
},
"dependencies": {
"@pnp/common": {
@ -4173,35 +4174,30 @@
"tslib": "1.8.1"
}
},
"color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/color/-/color-3.1.0.tgz",
"integrity": "sha512-CwyopLkuRYO5ei2EpzpIh6LqJMt6Mt+jZhO5VI5f/wJLZriXQE32/SSqzmrh+QB+AZT81Cj8yv+7zwToW8ahZg==",
"@pnp/telemetry-js": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@pnp/telemetry-js/-/telemetry-js-2.0.0.tgz",
"integrity": "sha512-qFNm3mTerTnxgTR6c/4iMMt8EUKrQn5z0XG/IQtpNlp6m7KXRDFR87mQKeBVtSv2LhxGO0VNFndKJIibBw52zQ==",
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
}
},
"color-string": {
"version": "1.5.3",
"resolved": "https://registry.npmjs.org/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha512-dC2C5qeWoYkxki5UAXapdjqO672AM4vZuPGRQfO8b5HKuKGBbKWpITyDYN7TOFKvRW7kOgAn3746clDBMDJyQw==",
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
"whatwg-fetch": "2.0.4"
}
},
"lodash": {
"version": "4.17.4",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz",
"integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4="
"version": "4.17.13",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.13.tgz",
"integrity": "sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA=="
},
"whatwg-fetch": {
"version": "2.0.4",
"resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz",
"integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng=="
}
}
},
"@pnp/spfx-property-controls": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/@pnp/spfx-property-controls/-/spfx-property-controls-1.14.1.tgz",
"integrity": "sha512-GN3mmqpBJtOJJX/tCYXU1SZPCm25HGFfLkhRo+C3PEZ7NzmpbLqaIjrMD2ePG4C+UEkKOubmh1sn/9LiYNtOSg==",
"version": "1.20.0",
"resolved": "https://registry.npmjs.org/@pnp/spfx-property-controls/-/spfx-property-controls-1.20.0.tgz",
"integrity": "sha512-+V7IUNHABJd62lXZzyDbqBqZVeeHBPuy1L+X98aIa2pagCvSuDNQOz6MHvP6iGExTK+MrSB0XgZZk1u4kfz6BQ==",
"requires": {
"@pnp/common": "^1.2.8",
"@pnp/logging": "^1.2.8",
@ -4209,15 +4205,17 @@
"@pnp/sp": "^1.2.8",
"@pnp/sp-clientsvc": "^1.2.8",
"@pnp/sp-taxonomy": "^1.2.8",
"@pnp/telemetry-js": "1.0.0",
"@pnp/telemetry-js": "2.0.0",
"lodash.omit": "^4.5.0",
"markdown-to-jsx": "^6.11.4",
"office-ui-fabric-react": "5.131.0",
"react-ace": "5.8.0"
}
},
"@pnp/telemetry-js": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@pnp/telemetry-js/-/telemetry-js-1.0.0.tgz",
"integrity": "sha512-7Lr22EB6DgB8MZi0WDuou8VuXru3wHdiOQPenB2B2gdhp7bJj2ijN9vTiqF8aL1nh80uJxIyX269Ix/PSnJC7g==",
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/@pnp/telemetry-js/-/telemetry-js-2.0.0.tgz",
"integrity": "sha512-qFNm3mTerTnxgTR6c/4iMMt8EUKrQn5z0XG/IQtpNlp6m7KXRDFR87mQKeBVtSv2LhxGO0VNFndKJIibBw52zQ==",
"requires": {
"whatwg-fetch": "2.0.4"
},
@ -4525,6 +4523,14 @@
"integrity": "sha1-aQoUdbhPKohP0HzXl8APXzE1bqg=",
"dev": true
},
"@types/quill": {
"version": "1.3.10",
"resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz",
"integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==",
"requires": {
"parchment": "^1.1.2"
}
},
"@types/ramda": {
"version": "0.25.51",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/@types/ramda/-/ramda-0.25.51.tgz",
@ -6791,6 +6797,15 @@
"unset-value": "^1.0.0"
}
},
"call-bind": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz",
"integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==",
"requires": {
"function-bind": "^1.1.1",
"get-intrinsic": "^1.0.2"
}
},
"caller-callsite": {
"version": "2.0.0",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/caller-callsite/-/caller-callsite-2.0.0.tgz",
@ -6926,25 +6941,18 @@
}
},
"chartjs-color": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.2.0.tgz",
"integrity": "sha1-hKL7dVeH7YXDndbdjHsdiEKbrq4=",
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/chartjs-color/-/chartjs-color-2.4.1.tgz",
"integrity": "sha512-haqOg1+Yebys/Ts/9bLo/BqUcONQOdr/hoEr2LLTRl6C5LXctUdHxsCYfvQVg5JIxITrfCNUDr4ntqmQk9+/0w==",
"requires": {
"chartjs-color-string": "^0.5.0",
"color-convert": "^0.5.3"
},
"dependencies": {
"color-convert": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-0.5.3.tgz",
"integrity": "sha1-vbbGnOZg+t/+CwAHzER+G59ygr0="
}
"chartjs-color-string": "^0.6.0",
"color-convert": "^1.9.3"
}
},
"chartjs-color-string": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.5.0.tgz",
"integrity": "sha512-amWNvCOXlOUYxZVDSa0YOab5K/lmEhbFNKI55PWc4mlv28BDzA7zaoQTGxSBgJMHIW+hGX8YUrvw/FH4LyhwSQ==",
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/chartjs-color-string/-/chartjs-color-string-0.6.0.tgz",
"integrity": "sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==",
"requires": {
"color-name": "^1.0.0"
}
@ -7247,7 +7255,6 @@
"version": "3.1.2",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/color/-/color-3.1.2.tgz",
"integrity": "sha1-aBSOf4XUGtdknF+oyBBvCY0inhA=",
"dev": true,
"requires": {
"color-convert": "^1.9.1",
"color-string": "^1.5.2"
@ -7270,7 +7277,6 @@
"version": "1.5.3",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/color-string/-/color-string-1.5.3.tgz",
"integrity": "sha1-ybvF8BtYtUkvPWhXRZy2WQziBMw=",
"dev": true,
"requires": {
"color-name": "^1.0.0",
"simple-swizzle": "^0.2.2"
@ -7558,6 +7564,15 @@
"sha.js": "^2.4.8"
}
},
"create-react-class": {
"version": "15.7.0",
"resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz",
"integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==",
"requires": {
"loose-envify": "^1.3.1",
"object-assign": "^4.1.1"
}
},
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/cross-spawn/-/cross-spawn-6.0.5.tgz",
@ -8104,6 +8119,19 @@
"esprima": "4.0.1"
}
},
"deep-equal": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz",
"integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==",
"requires": {
"is-arguments": "^1.0.4",
"is-date-object": "^1.0.1",
"is-regex": "^1.0.4",
"object-is": "^1.0.1",
"object-keys": "^1.1.1",
"regexp.prototype.flags": "^1.2.0"
}
},
"deep-is": {
"version": "0.1.3",
"resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
@ -8166,7 +8194,6 @@
"version": "1.1.3",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
@ -9214,6 +9241,11 @@
"through": "^2.3.8"
}
},
"eventemitter3": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
"integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
},
"events": {
"version": "3.1.0",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/events/-/events-3.1.0.tgz",
@ -9453,8 +9485,7 @@
"extend": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"dev": true
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
},
"extend-shallow": {
"version": "3.0.2",
@ -9577,6 +9608,11 @@
"integrity": "sha1-VFFFB3xQFJHjOxXsQIwpQ3bpSuQ=",
"dev": true
},
"fast-diff": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
"integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
},
"fast-json-stable-stringify": {
"version": "2.1.0",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@ -10111,7 +10147,8 @@
"ansi-regex": {
"version": "2.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"aproba": {
"version": "1.2.0",
@ -10132,12 +10169,14 @@
"balanced-match": {
"version": "1.0.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"brace-expansion": {
"version": "1.1.11",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"balanced-match": "^1.0.0",
"concat-map": "0.0.1"
@ -10152,17 +10191,20 @@
"code-point-at": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"concat-map": {
"version": "0.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"console-control-strings": {
"version": "1.1.0",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"core-util-is": {
"version": "1.0.2",
@ -10279,7 +10321,8 @@
"inherits": {
"version": "2.0.4",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"ini": {
"version": "1.3.5",
@ -10291,6 +10334,7 @@
"version": "1.0.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"number-is-nan": "^1.0.0"
}
@ -10305,6 +10349,7 @@
"version": "3.0.4",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"brace-expansion": "^1.1.7"
}
@ -10312,12 +10357,14 @@
"minimist": {
"version": "0.0.8",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"minipass": {
"version": "2.9.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
@ -10336,6 +10383,7 @@
"version": "0.5.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"minimist": "0.0.8"
}
@ -10425,7 +10473,8 @@
"number-is-nan": {
"version": "1.0.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"object-assign": {
"version": "4.1.1",
@ -10437,6 +10486,7 @@
"version": "1.4.0",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"wrappy": "1"
}
@ -10522,7 +10572,8 @@
"safe-buffer": {
"version": "5.1.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"safer-buffer": {
"version": "2.1.2",
@ -10558,6 +10609,7 @@
"version": "1.0.2",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
@ -10577,6 +10629,7 @@
"version": "3.0.1",
"bundled": true,
"dev": true,
"optional": true,
"requires": {
"ansi-regex": "^2.0.0"
}
@ -10620,12 +10673,14 @@
"wrappy": {
"version": "1.0.2",
"bundled": true,
"dev": true
"dev": true,
"optional": true
},
"yallist": {
"version": "3.1.1",
"bundled": true,
"dev": true
"dev": true,
"optional": true
}
}
},
@ -10644,8 +10699,7 @@
"function-bind": {
"version": "1.1.1",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=",
"dev": true
"integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0="
},
"functional-red-black-tree": {
"version": "1.0.1",
@ -10713,6 +10767,16 @@
"integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=",
"dev": true
},
"get-intrinsic": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz",
"integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==",
"requires": {
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1"
}
},
"get-stdin": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-4.0.1.tgz",
@ -11331,7 +11395,6 @@
"version": "1.0.3",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/has/-/has-1.0.3.tgz",
"integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
@ -11363,8 +11426,7 @@
"has-symbols": {
"version": "1.0.1",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg=",
"dev": true
"integrity": "sha1-n1IUdYpEGWxAbZvXbOv4HsLdMeg="
},
"has-unicode": {
"version": "2.0.1",
@ -12004,6 +12066,14 @@
}
}
},
"is-arguments": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.0.tgz",
"integrity": "sha512-1Ij4lOMPl/xB5kBDn7I+b2ttPMKa8szhEIrXDuXQD/oe3HJLTLhqhgGspwgyGd6MOywBUqVvYicF72lkgDnIHg==",
"requires": {
"call-bind": "^1.0.0"
}
},
"is-arrayish": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
@ -12077,8 +12147,7 @@
"is-date-object": {
"version": "1.0.2",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha1-vac28s2P0G0yhE53Q7+nSUw7/X4=",
"dev": true
"integrity": "sha1-vac28s2P0G0yhE53Q7+nSUw7/X4="
},
"is-descriptor": {
"version": "0.1.6",
@ -12252,7 +12321,6 @@
"version": "1.0.5",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/is-regex/-/is-regex-1.0.5.tgz",
"integrity": "sha1-OdWJo1i/GJZ/cmlnEguPwa7XTq4=",
"dev": true,
"requires": {
"has": "^1.0.3"
}
@ -14833,6 +14901,11 @@
"integrity": "sha1-VYqlO0O2YeGSWgr9+japoQhf5Xo=",
"dev": true
},
"lodash.omit": {
"version": "4.5.0",
"resolved": "https://registry.npmjs.org/lodash.omit/-/lodash.omit-4.5.0.tgz",
"integrity": "sha1-brGa5aHuHdnfC5aeZs4Lf6MLXmA="
},
"lodash.restparam": {
"version": "3.6.1",
"resolved": "https://registry.npmjs.org/lodash.restparam/-/lodash.restparam-3.6.1.tgz",
@ -15015,6 +15088,15 @@
"object-visit": "^1.0.0"
}
},
"markdown-to-jsx": {
"version": "6.11.4",
"resolved": "https://registry.npmjs.org/markdown-to-jsx/-/markdown-to-jsx-6.11.4.tgz",
"integrity": "sha512-3lRCD5Sh+tfA52iGgfs/XZiw33f7fFX9Bn55aNnVNUd2GzLDkOWyKYYD8Yju2B1Vn+feiEdgJs8T6Tg0xNokPw==",
"requires": {
"prop-types": "^15.6.2",
"unquote": "^1.1.0"
}
},
"matchdep": {
"version": "2.0.0",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/matchdep/-/matchdep-2.0.0.tgz",
@ -15381,9 +15463,9 @@
}
},
"moment": {
"version": "2.24.0",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.24.0.tgz",
"integrity": "sha512-bV7f+6l2QigeBBZSM/6yTNq4P2fNpSWj/0e7jQcy87A8e7o2nAfP/34/2ky5Vw4B9S446EtIhodAzkFCcR4dQg=="
"version": "2.29.1",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.29.1.tgz",
"integrity": "sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ=="
},
"move-concurrently": {
"version": "1.0.1",
@ -15950,11 +16032,19 @@
"integrity": "sha1-9Pa9GBrXfwBrXs5gvQtvOY/3Smc=",
"dev": true
},
"object-is": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.5.tgz",
"integrity": "sha512-3cyDsyHgtmi7I7DfSSI2LDp6SK2lwvtbg0p0R1e0RvTqF5ceGx+K2dfSjm1bKDMVCFEDAQvy+o8c6a7VujOddw==",
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4=",
"dev": true
"integrity": "sha1-HEfyct8nfzsdrwYWd9nILiMixg4="
},
"object-visit": {
"version": "1.0.1",
@ -16369,6 +16459,11 @@
"no-case": "^2.2.0"
}
},
"parchment": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
"integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
},
"parent-module": {
"version": "1.0.1",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/parent-module/-/parent-module-1.0.1.tgz",
@ -17595,6 +17690,36 @@
"integrity": "sha1-nsYfeQSYdXB9aUFFlv2Qek1xHnM=",
"dev": true
},
"quill": {
"version": "1.3.7",
"resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
"integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
"requires": {
"clone": "^2.1.1",
"deep-equal": "^1.0.1",
"eventemitter3": "^2.0.3",
"extend": "^3.0.2",
"parchment": "^1.1.4",
"quill-delta": "^3.6.2"
},
"dependencies": {
"clone": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
"integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
}
}
},
"quill-delta": {
"version": "3.6.3",
"resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
"integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
"requires": {
"deep-equal": "^1.0.1",
"extend": "^3.0.2",
"fast-diff": "1.1.2"
}
},
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
@ -17755,6 +17880,11 @@
"prop-types": "^15.6.0"
}
},
"react-dom-factories": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.2.tgz",
"integrity": "sha1-63cFxNs2+1AbOqOP91lhaqD/luA="
},
"react-is": {
"version": "16.8.2",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.2.tgz",
@ -17765,6 +17895,27 @@
"resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
"integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
},
"react-quill": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/react-quill/-/react-quill-1.3.3.tgz",
"integrity": "sha512-T9RubLaWJ8gCfp7sOqmFupjiTiEp/EdGqhCG+PWGKc5UHiK6xIWNKWYsOHHEhQ+sZCKs8u/DPx47gc1VfFmcLg==",
"requires": {
"@types/quill": "1.3.10",
"@types/react": "*",
"create-react-class": "^15.6.0",
"lodash": "^4.17.4",
"prop-types": "^15.5.10",
"quill": "^1.2.6",
"react-dom-factories": "^1.0.0"
},
"dependencies": {
"lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
}
}
},
"react-sortable-tree": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/react-sortable-tree/-/react-sortable-tree-2.6.0.tgz",
@ -18022,6 +18173,15 @@
"safe-regex": "^1.1.0"
}
},
"regexp.prototype.flags": {
"version": "1.3.1",
"resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.1.tgz",
"integrity": "sha512-JiBdRBq91WlY7uRJ0ds7R+dU02i6LKi8r3BuQhNXn+kmeLN+EfHhfjqMRis1zJxnlu88hq/4dx0P2OP3APRTOA==",
"requires": {
"call-bind": "^1.0.2",
"define-properties": "^1.1.3"
}
},
"regexpp": {
"version": "2.0.1",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/regexpp/-/regexpp-2.0.1.tgz",
@ -20479,8 +20639,7 @@
"unquote": {
"version": "1.1.1",
"resolved": "https://admdev.pkgs.visualstudio.com/_packaging/Artifacts/npm/registry/unquote/-/unquote-1.1.1.tgz",
"integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ=",
"dev": true
"integrity": "sha1-j97XMk7G6IoP+LkF58CYzcCG1UQ="
},
"unset-value": {
"version": "1.0.0",

View File

@ -1,6 +1,6 @@
{
"name": "react-tree-orgchart",
"version": "1.0.2",
"version": "1.1.0",
"private": true,
"engines": {
"node": ">=0.10.0"
@ -15,13 +15,13 @@
"@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",
"@pnp/odata": "^1.2.9",
"@pnp/sp": "^1.2.9",
"@pnp/spfx-controls-react": "^1.11.0",
"@pnp/spfx-property-controls": "^1.14.1",
"@pnp/common": "1.3.11",
"@pnp/graph": "1.3.11",
"@pnp/logging": "1.3.11",
"@pnp/odata": "1.3.11",
"@pnp/sp": "1.3.11",
"@pnp/spfx-controls-react": "1.21.1",
"@pnp/spfx-property-controls": "1.20.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.4.2",
"@types/react-dom": "16.0.5",

View File

@ -0,0 +1,58 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { graph } from "@pnp/graph";
export interface IGraphUser {
mail?: string;
displayName?: string;
jobTitle?: string;
userPrincipalName?: string;
}
const graphUserSelect: string[] = ['displayName', 'mail', 'jobTitle', 'userPrincipalName'];
export default class GraphService {
constructor(private context: WebPartContext) {
graph.setup({
spfxContext: this.context
});
}
public async getUser(upn: string): Promise<IGraphUser> {
return await graph.users.getById(upn).select(...graphUserSelect).get() as IGraphUser;
}
public async getUserManger(upn: string): Promise<IGraphUser> {
return await graph.users.getById(upn).manager.select(...graphUserSelect).get() as IGraphUser;
}
public async getUserDirectReports(upn: string, excludefilter?: boolean, filter?: string) {
/*
odata filter
"code": "Request_UnsupportedQuery",
"message": "The specified filter to the reference property query is currently not supported.",
*/
const directReports = await graph.users.getById(upn).directReports.select(...graphUserSelect).get() as IGraphUser[];
if (filter && filter.length > 0) {
if (excludefilter) {
return directReports.filter((user) =>
user.userPrincipalName?.toLowerCase().indexOf(filter.toLowerCase()) === -1
);
} else {
return directReports.filter((user) =>
user.userPrincipalName?.toLowerCase().indexOf(filter.toLowerCase()) !== -1
);
}
}
return directReports;
}
}

View File

@ -12,7 +12,7 @@
// Components that allow authors to embed arbitrary script code should set this to true.
// https://support.office.com/en-us/article/Turn-scripting-capabilities-on-or-off-1f2c515f-5d7e-448a-9fd7-835da935584f
"requiresCustomScript": false,
//"viewType": 1,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
@ -22,6 +22,7 @@
"properties": {
"title": "My Team Organization Chart",
"currentUserTeam": true,
"maxLevels": 5
}
}]

View File

@ -1,36 +1,48 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField,
PropertyPaneToggle
} from '@microsoft/sp-webpart-base';
import * as strings from 'TreeOrgChartWebPartStrings';
import TreeOrgChart from './components/TreeOrgChart';
import TreeOrgChart, { TreeOrgChartType } from './components/TreeOrgChart';
import { ITreeOrgChartProps } from './components/ITreeOrgChartProps';
import { PropertyFieldNumber } from '@pnp/spfx-property-controls/lib/PropertyFieldNumber';
import { setup as pnpSetup } from '@pnp/common';
import { BaseClientSideWebPart, IPropertyPaneConfiguration, PropertyPaneDropdown, PropertyPaneTextField, PropertyPaneToggle } from '@microsoft/sp-webpart-base';
import { graph } from "@pnp/graph";
export interface ITreeOrgChartWebPartProps {
title: string;
currentUserTeam: boolean;
teamLeader: string;
maxLevels: number;
viewType: TreeOrgChartType;
filter: string;
excludefilter: boolean;
detailBehavoir: boolean;
}
export default class TreeOrgChartWebPart extends BaseClientSideWebPart<ITreeOrgChartWebPartProps> {
public onInit(): Promise<void> {
pnpSetup({
spfxContext: this.context
});
graph.setup(this.context as any);
//Migration old Config Settings
if (!this.properties.viewType) {
const treetype = this.properties.currentUserTeam ? TreeOrgChartType.MyTeam : TreeOrgChartType.CompanyHierarchy;
this.properties.viewType = treetype;
}
return Promise.resolve();
}
public render(): void {
const element: React.ReactElement<ITreeOrgChartProps> = React.createElement(
TreeOrgChart,
{
@ -39,8 +51,16 @@ export default class TreeOrgChartWebPart extends BaseClientSideWebPart<ITreeOrgC
updateProperty: (value: string) => {
this.properties.title = value;
},
currentUserTeam: this.properties.currentUserTeam,
viewType: this.properties.viewType,
teamLeader: this.properties.teamLeader,
updateTeamLeader: (loginname: string) => {
this.properties.teamLeader = loginname;
this.render();
},
maxLevels: this.properties.maxLevels,
filter: this.properties.filter,
excludefilter: this.properties.excludefilter,
detailBehavoir: this.properties.detailBehavoir,
context: this.context
}
);
@ -70,9 +90,16 @@ export default class TreeOrgChartWebPart extends BaseClientSideWebPart<ITreeOrgC
PropertyPaneTextField('title', {
label: strings.TitleFieldLabel
}),
PropertyPaneToggle('currentUserTeam', {
label: strings.CurrentUserTeamFieldLabel
PropertyPaneDropdown('viewType', {
label: strings.ViewType,
options: [
{ key: TreeOrgChartType.MyTeam, text: strings.TreeOrgChartTypeMyTeam },
{ key: TreeOrgChartType.CompanyHierarchy, text: strings.TreeOrgChartTypeCompany },
{ key: TreeOrgChartType.ShowOtherTeam, text: strings.TreeOrgChartTypeShowOtherTeam },
],
selectedKey: this.properties.viewType
}),
PropertyFieldNumber("maxLevels", {
key: "numberValue",
label: strings.MaxLevels,
@ -81,9 +108,29 @@ export default class TreeOrgChartWebPart extends BaseClientSideWebPart<ITreeOrgC
maxValue: 10,
minValue: 1,
disabled: false
})
}),
PropertyPaneToggle('detailBehavoir', {
label: strings.DetailBehavoir,
onText: strings.LivePersonaCard,
offText: strings.DelveLink
}),
]
},
{
groupName: strings.FilterGroupName,
groupFields: [
PropertyPaneToggle('excludefilter', {
label: strings.ExcludeFilter,
onText: strings.ExcludeFilterOnText,
offText: strings.ExcludeFilterOffText
}),
PropertyPaneTextField('filter', {
label: strings.FilterLabel,
description: strings.FilterDescription,
}),
]
}
]
}
]

View File

@ -1,5 +0,0 @@
export interface ITreeChildren {
title ? : any;
expanded ? :boolean;
children ? : any;
}

View File

@ -1,6 +1,6 @@
import {ITreeChildren } from './ITreeChildren';
export interface ITreeData {
title: any;
expanded ?: boolean;
children ? : ITreeChildren[];
children ? : ITreeData[]|null;
}

View File

@ -1,10 +1,16 @@
import { DisplayMode } from '@microsoft/sp-core-library';
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { TreeOrgChartType } from './TreeOrgChart';
export interface ITreeOrgChartProps {
title: string;
currentUserTeam:boolean;
maxLevels:number;
maxLevels: number;
displayMode: DisplayMode;
updateProperty: (value: string) => void;
viewType: TreeOrgChartType;
teamLeader?: string;
updateTeamLeader: (loginname: string) => void;
context: WebPartContext;
filter: string;
excludefilter: boolean;
detailBehavoir: boolean;
}

View File

@ -1,4 +1,5 @@
export interface ITreeOrgChartState {
treeData: any;
isLoading: boolean;
livePersonaCard?: any;
}

View File

@ -16,24 +16,39 @@ import {
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 { DisplayMode, Environment, EnvironmentType } from "@microsoft/sp-core-library";
import { PeoplePicker, PrincipalType } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import { SPComponentLoader } from '@microsoft/sp-loader';
import * as strings from 'TreeOrgChartWebPartStrings';
import GraphServices, { IGraphUser } from "../../../services/GraphService";
import GraphService from "../../../services/GraphService";
export enum TreeOrgChartType {
MyTeam = 1,
CompanyHierarchy = 2,
ShowOtherTeam = 4
}
const LIVE_PERSONA_COMPONENT_ID: string = '914330ee-2df2-4f6e-a858-30c23a812408';
export default class TreeOrgChart extends React.Component<
ITreeOrgChartProps,
ITreeOrgChartState
> {
> {
private treeData: ITreeData[];
private SPService: SPService;
private GraphService: GraphService;
constructor(props) {
super(props);
this.SPService = new SPService(this.props.context);
this.GraphService = new GraphServices(this.props.context);
this.state = {
treeData: [],
isLoading: true
@ -49,43 +64,97 @@ export default class TreeOrgChart extends React.Component<
prevState: ITreeOrgChartState
) {
if (
this.props.currentUserTeam !== prevProps.currentUserTeam ||
this.props.maxLevels !== prevProps.maxLevels
this.props.viewType !== prevProps.viewType ||
this.props.maxLevels !== prevProps.maxLevels ||
this.props.teamLeader !== prevProps.teamLeader ||
this.props.excludefilter !== prevProps.excludefilter ||
this.props.filter !== prevProps.filter ||
this.props.detailBehavoir !== prevProps.detailBehavoir
) {
await this.loadOrgchart();
}
}
public async componentDidMount() {
if (Environment.type !== EnvironmentType.Local) {
const sharedLibrary = await this._loadSPComponentById(
LIVE_PERSONA_COMPONENT_ID
);
const livePersonaCard: any = sharedLibrary.LivePersonaCard;
this.setState({ livePersonaCard: livePersonaCard });
}
await this.loadOrgchart();
}
private async _loadSPComponentById(componentId: string): Promise<any> {
try {
const component: any = await SPComponentLoader.loadComponentById(
componentId
);
return component;
} catch (error) {
Promise.reject(error);
}
}
private livePersonaCard(user: IGraphUser): JSX.Element {
return React.createElement(
this.state.livePersonaCard,
{
serviceScope: this.props.context.serviceScope,
upn: user.userPrincipalName,
onCardOpen: () => {
console.log('LivePersonaCard Open');
},
onCardClose: () => {
console.log('LivePersonaCard Close');
},
}, this.buildDefaultPersonaCard(user)
);
}
/*
// Load Organization Chart
*/
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 currentUser = this.props.context.pageContext.user.loginName;
let currentUserProperties = null;
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);
} else {
const treeManagers = await this.buildMyTeamOrganizationChart(
currentUserProperties
);
if (treeManagers)
this.treeData.push({
title: treeManagers.person,
expanded: true,
children: treeManagers.treeChildren
});
switch (this.props.viewType) {
case TreeOrgChartType.CompanyHierarchy:
const spcurrentlogin = `i:0#.f|membership|${currentUser}`;
currentUserProperties = await this.SPService.getUserProperties(
spcurrentlogin
);
const treeManagers = await this.buildOrganizationChart(
currentUserProperties
);
if (treeManagers) this.treeData.push(treeManagers);
break;
case TreeOrgChartType.MyTeam:
const myteam = await this.buildMyTeamOrganizationChart(
currentUser
);
if (myteam)
this.treeData = [{ ...myteam }];
break;
case TreeOrgChartType.ShowOtherTeam:
if (this.props.teamLeader && this.props.teamLeader.length > 0) {
const otherteam = await this.buildTeamLeaderOrganizationChart(
this.props.teamLeader
);
if (otherteam)
this.treeData = [{ ...otherteam }];
}
break;
}
this.setState({ treeData: this.treeData, isLoading: false });
}
@ -100,185 +169,131 @@ export default class TreeOrgChart extends React.Component<
currentUserProperties.ExtendedManagers &&
currentUserProperties.ExtendedManagers.length > 0
) {
treeManagers = await this.getUsers(
currentUserProperties.ExtendedManagers[0]
);
const upn: string | undefined = this.claimUserToUPN(currentUserProperties.ExtendedManagers[0]);
if (upn) {
treeManagers = await this.getUsers(
upn
);
}
}
return treeManagers;
}
/*
// Get user from Top Manager
*/
private async getUsers(manager: string) {
let person: any;
private claimUserToUPN(claim: string): string | undefined {
if (!claim) { return undefined; }
const claimuser: string[] = claim.split('|');
if (claimuser.length > 1) {
const upn = claimuser[claimuser.length - 1];
if (upn && upn.length > 0 && upn.indexOf('@') !== -1) {
return upn;
}
}
return undefined;
}
public buildPersonaCard(user: IGraphUser): JSX.Element {
if (this.props.detailBehavoir) {
if (this.state.livePersonaCard) {
return (this.livePersonaCard(user));
}
return this.buildDefaultPersonaCard(user);
} else {
return this.buildDefaultPersonaCard(user);
}
}
public buildDefaultPersonaCard(user: IGraphUser): JSX.Element {
let spUser: IPersonaSharedProps = {};
// Get User Properties
const managerProperties = await this.SPService.getUserProperties(manager);
let imageInitials: string[] = managerProperties.DisplayName.split(" ");
let imageInitials: string[] = user.displayName ? user.displayName.split(" ") : [];
//https://graph.microsoft.com/v1.0/users/${upn}/photo/$value
// Persona Card Properties
spUser.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
spUser.imageInitials = imageInitials && imageInitials.length > 0 ? `${imageInitials[0]
spUser.imageUrl = user.userPrincipalName ? `/_layouts/15/userphoto.aspx?size=L&username=${user.userPrincipalName}` : undefined;
spUser.imageInitials = imageInitials && imageInitials.length > 0 ? `${imageInitials[0]
.substring(0, 1)
.toUpperCase()}${imageInitials[1] ? imageInitials[1].substring(0, 1).toUpperCase():''}` : '';
spUser.text = managerProperties.DisplayName;
spUser.tertiaryText = managerProperties.Email;
spUser.secondaryText = managerProperties.Title;
.toUpperCase()}${imageInitials[1] ? imageInitials[1].substring(0, 1).toUpperCase() : ''}` : '';
spUser.text = user.displayName;
spUser.tertiaryText = user.mail;
spUser.secondaryText = user.jobTitle;
// PersonaCard component
person = (
return (
<Persona
{...spUser}
hidePersonaDetails={false}
size={PersonaSize.size40}
/>
);
// Has 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 };
// Don't have DirectReports
}
private async getUsers(upn: string): Promise<ITreeData | null> {
const managerUser = await this.GraphService.getUser(upn);
const person = this.buildPersonaCard(managerUser);
if (managerUser.userPrincipalName) {
return ({
title: person,
expanded: true,
children: await this.getDirectReportsUsers(managerUser.userPrincipalName)
});
} else {
// return treeData
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(" ");
private async getDirectReportsUsers(upn?: string, level: number = 1, expanded: boolean = false): Promise<ITreeData[] | null> {
if (!upn) { return null; }
spUser.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
spUser.imageInitials = imageInitials && imageInitials.length > 0 ? `${imageInitials[0]
.substring(0, 1)
.toUpperCase()}${imageInitials[1] ? 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 directReportsUser = await this.GraphService.getUserDirectReports(upn,this.props.excludefilter,this.props.filter);
//this is already level 1
if (directReportsUser && directReportsUser.length > 0) {
return await Promise.all(directReportsUser.map(async (dr) => {
const children = ((level +1) <= this.props.maxLevels) ? await this.getDirectReportsUsers(dr.userPrincipalName, level + 1) : null;
return ({
title: this.buildPersonaCard(dr),
expanded: expanded,
children: children
});
usersDirectReports
? treeChildren.push({ title: person, children: usersDirectReports })
: treeChildren.push({ title: person });
}));
}
return treeChildren;
return null;
}
//buildTeamLeaderOrganizationChart
private async buildTeamLeaderOrganizationChart(upn: string): Promise<ITreeData | null> {
const tmpupn: string | undefined = this.claimUserToUPN(upn);
return await this.getUsers(tmpupn ? tmpupn : upn);
}
/*
Build My Team Organization Chart
@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;
// Get My 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(" ").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] ? 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;
}
private async buildMyTeamOrganizationChart(upn: string): Promise<ITreeData | null> {
// Get my Properties
const meImageInitials: string[] = currentUserProperties.DisplayName.split(
" "
);
me.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${currentUserProperties.Email}`;
me.imageInitials = me.imageInitials && me.imageInitials.length > 0 ?`${meImageInitials[0]
.substring(0, 1)
.toUpperCase()}${meImageInitials[1] ? 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
);
// Current USer Has Manager
if (hasManager) {
treeChildren.push({
title: meCard,
expanded: true,
children: usersDirectReports
});
} else {
treeChildren = usersDirectReports;
managerCard = meCard;
const mymanager = await this.GraphService.getUserManger(upn);
if (mymanager && mymanager.userPrincipalName) {
return await this.getUsers(mymanager.userPrincipalName);
}
// Get MyPeers
for (const userPeer of currentUserProperties.Peers) {
const peerProperties = await this.SPService.getUserProperties(userPeer);
imageInitials = peerProperties.DisplayName.split(" ");
peer.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${peerProperties.Email}`;
peer.imageInitials = peer.imageInitials && peer.imageInitials.length>0 ? `${imageInitials[0]
.substring(0, 1)
.toUpperCase()}${imageInitials[1] ? 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 });
}
// Return
return { person: managerCard, treeChildren: treeChildren };
return await this.getUsers(upn);
}
// Render
public render(): React.ReactElement<ITreeOrgChartProps> {
const showEditOther: boolean = this.props.displayMode === DisplayMode.Edit && this.props.viewType === TreeOrgChartType.ShowOtherTeam;
let selectedTeamleader: string | undefined = undefined;
if (showEditOther && this.props.teamLeader && this.props.teamLeader.length > 0) {
selectedTeamleader = this.claimUserToUPN(this.props.teamLeader);
if (!selectedTeamleader) {
selectedTeamleader == this.props.teamLeader;
}
}
return (
<div className={styles.treeOrgChart}>
<WebPartTitle
@ -286,6 +301,32 @@ export default class TreeOrgChart extends React.Component<
title={this.props.title}
updateProperty={this.props.updateProperty}
/>
{showEditOther && (<div>
<PeoplePicker
context={this.props.context}
titleText={strings.TeamLeaderHeadline}
personSelectionLimit={1}
groupName={""} // Leave this blank in case you want to filter from all users
isRequired={true}
disabled={false}
defaultSelectedUsers={selectedTeamleader ? [selectedTeamleader] : undefined}
selectedItems={(items: any) => {
if (this.props.updateTeamLeader) {
if (items.length > 0) {
const teamleaderupn: string | undefined = this.claimUserToUPN(items[0].loginName);
if (teamleaderupn) {
this.props.updateTeamLeader(teamleaderupn);
return;
}
}
this.props.updateTeamLeader('');
}
}}
showHiddenInUI={false}
principalTypes={[PrincipalType.User]}
resolveDelay={1000} />
</div>)}
{this.state.isLoading ? (
<Spinner
size={SpinnerSize.large}
@ -297,25 +338,28 @@ export default class TreeOrgChart extends React.Component<
treeData={this.state.treeData}
onChange={this.handleTreeOnChange.bind(this)}
canDrag={false}
canDrop={false}
rowHeight={70}
maxDepth={this.props.maxLevels}
generateNodeProps={rowInfo => ({
buttons: [
<IconButton
disabled={false}
checked={false}
iconProps={{ iconName: "ContactInfo" }}
title="Contact Info"
ariaLabel="Contact"
onClick={() => {
window.open(
`https://eur.delve.office.com/?p=${rowInfo.node.title.props.tertiaryText}&v=work`
);
}}
/>
]
})}
generateNodeProps={rowInfo => {
return !this.props.detailBehavoir ?
({
buttons: [
<IconButton
disabled={false}
checked={false}
iconProps={{ iconName: "ContactInfo" }}
title={strings.ContactInfoTitle}
ariaLabel={strings.ContactInfoTitle}
onClick={() => {
window.open(
`https://eur.delve.office.com/?p=${rowInfo.node.title.props.tertiaryText}&v=work`
);
}}
/>
]
}) : undefined;
}
}
/>
</div>
</div>

View File

@ -3,7 +3,21 @@ define([], function () {
"PropertyPaneDescription": "Tree Organization Chart",
"BasicGroupName": "Properties",
"TitleFieldLabel": "WebPart Title",
"CurrentUserTeamFieldLabel": "Show only my team ?",
"MaxLevels": "Max Depth"
"MaxLevels": "Max Depth",
"ViewType": "View options",
"TreeOrgChartTypeMyTeam": "Show my team",
"TreeOrgChartTypeCompany": "Show company hirachy",
"TreeOrgChartTypeShowOtherTeam": "Show other team",
"FilterGroupName":"Account Name Filter",
"ExcludeFilter": "Filter Logic",
"ExcludeFilterOnText": "Exclude",
"ExcludeFilterOffText": "Include",
"FilterLabel": "Filter (contains)",
"FilterDescription": "currently only contains or not contains filter logic can be switched with the toggle above",
"TeamLeaderHeadline":"Please Select a Teamleader",
"ContactInfoTitle":"Contact Info",
"DetailBehavoir":"Detail link behavior ",
"LivePersonaCard":"Live Persona Card",
"DelveLink":"Link to Delve"
}
});

View File

@ -2,8 +2,24 @@ declare interface ITreeOrgChartWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
TitleFieldLabel: string;
CurrentUserTeamFieldLabel:string;
MaxLevels: string;
ViewType: string;
TreeOrgChartTypeMyTeam: string;
TreeOrgChartTypeCompany: string;
TreeOrgChartTypeShowOtherTeam: string;
FilterGroupName:string;
ExcludeFilter: string;
ExcludeFilterOnText: string;
ExcludeFilterOffText: string;
FilterLabel: string;
FilterDescription: string;
TeamLeaderHeadline:string;
ContactInfoTitle:string;
DetailBehavoir:string;
LivePersonaCard:string;
DelveLink:string;
}
declare module 'TreeOrgChartWebPartStrings' {