diff --git a/samples/react-organization-chart/.devcontainer/devcontainer.json b/samples/react-organization-chart/.devcontainer/devcontainer.json index 50d2cd4b9..a7ecafcf3 100644 --- a/samples/react-organization-chart/.devcontainer/devcontainer.json +++ b/samples/react-organization-chart/.devcontainer/devcontainer.json @@ -1,39 +1,38 @@ -// For more information on how to run this SPFx project in a VS Code Remote Container, please visit https://aka.ms/spfx-devcontainer { - "name": "SPFx 1.4.1", - "image": "docker.io/m365pnp/spfx:1.4.1", - // Set *default* container specific settings.json values on container create. - "settings": {}, - // Add the IDs of extensions you want installed when the container is created. - "extensions": [ - "editorconfig.editorconfig", - "dbaeumer.vscode-eslint" - ], - // Use 'forwardPorts' to make a list of ports inside the container available locally. - "forwardPorts": [ - 4321, - 35729, - 5432 - ], - "portsAttributes": { - "4321": { - "protocol": "https", - "label": "Manifest", - "onAutoForward": "silent", - "requireLocalPort": true - }, - "5432": { - "protocol": "https", - "label": "Workbench", - "onAutoForward": "silent" - }, - "35729": { - "protocol": "https", - "label": "LiveReload", - "onAutoForward": "silent", - "requireLocalPort": true - } - }, - "postCreateCommand": "bash .devcontainer/spfx-startup.sh", - "remoteUser": "node" -} \ No newline at end of file + "name": "SPFx 1.18.2", + "image": "docker.io/m365pnp/spfx:1.18.2", + "customizations": { + "vscode": { + "extensions": [ + "editorconfig.editorconfig", + "dbaeumer.vscode-eslint" + ] + } + }, + "forwardPorts": [ + 4321, + 35729, + 5432 + ], + "portsAttributes": { + "4321": { + "protocol": "https", + "label": "Manifest", + "onAutoForward": "silent", + "requireLocalPort": true + }, + "5432": { + "protocol": "https", + "label": "Workbench", + "onAutoForward": "silent" + }, + "35729": { + "protocol": "https", + "label": "LiveReload", + "onAutoForward": "silent", + "requireLocalPort": true + } + }, + "postCreateCommand": "bash .devcontainer/spfx-startup.sh", + "remoteUser": "node" +} diff --git a/samples/react-organization-chart/.devcontainer/spfx-startup.sh b/samples/react-organization-chart/.devcontainer/spfx-startup.sh index ca531bdf2..456d6aea8 100644 --- a/samples/react-organization-chart/.devcontainer/spfx-startup.sh +++ b/samples/react-organization-chart/.devcontainer/spfx-startup.sh @@ -7,9 +7,11 @@ echo echo -e "\e[1;94mGenerating dev certificate\e[0m" gulp trust-dev-cert +# Convert the generated PEM certificate to a CER certificate +openssl x509 -inform PEM -in ~/.rushstack/rushstack-serve.pem -outform DER -out ./spfx-dev-cert.cer -cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.cer -cp ~/.gcb-serve-data/gcb-serve.cer ./spfx-dev-cert.pem +# Copy the PEM ecrtificate for non-Windows hosts +cp ~/.rushstack/rushstack-serve.pem ./spfx-dev-cert.pem ## add *.cer to .gitignore to prevent certificates from being saved in repo if ! grep -Fxq '*.cer' ./.gitignore diff --git a/samples/react-organization-chart/README.md b/samples/react-organization-chart/README.md index b9845c988..edaaa496a 100644 --- a/samples/react-organization-chart/README.md +++ b/samples/react-organization-chart/README.md @@ -14,14 +14,18 @@ This web part shows an organization chart based on specified user, and user can | :warning: Important | |:---------------------------| -| Every SPFx version is only compatible with specific version(s) of Node.js. In order to be able to build this sample, please ensure that the version of Node on your workstation matches one of the versions listed in this section. This sample will not work on a different version of Node.| +| Every SPFx version is optimally compatible with specific versions of Node.js. In order to be able to build this sample, you need to ensure that the version of Node on your workstation matches one of the versions listed in this section. This sample will not work on a different version of Node.| |Refer to for more information on SPFx compatibility. | -![SPFx 1.4.1](https://img.shields.io/badge/SPFx-1.4.1-green.svg) -![Node.js v6 | v8](https://img.shields.io/badge/Node.js-LTS%206.x%20%7C%20v8-green.svg) -![SharePoint 2019 | Online](https://img.shields.io/badge/SharePoint-2019%20%7C%20Online-yellow.svg) -![Teams No: Not designed for Microsoft Teams](https://img.shields.io/badge/Teams-No-red.svg "Not designed for Microsoft Teams") -![Workbench Hosted: Does not work with local workbench](https://img.shields.io/badge/Workbench-Hosted-yellow.svg "Does not work with local workbench") +This sample is optimally compatible with the following environment configuration: + +![SPFx 1.18.2](https://img.shields.io/badge/SPFx-1.18.2-green.svg) +![Node.js v16 | v18](https://img.shields.io/badge/Node.js-v16%20%7C%20v18-green.svg) +![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg) +![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower") +![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1") +![Local Workbench Unsupported](https://img.shields.io/badge/Local%20Workbench-Unsupported-red.svg "Local workbench is no longer available as of SPFx 1.13 and above") +![Hosted Workbench Compatible](https://img.shields.io/badge/Hosted%20Workbench-Compatible-green.svg) ![Compatible with Remote Containers](https://img.shields.io/badge/Remote%20Containers-Compatible-green.svg) ## Applies to @@ -30,14 +34,14 @@ This web part shows an organization chart based on specified user, and user can ## Contributors -- [João Mendes](https://github.com/joaojmendes), Storm Technology, ([@joaojmendes](https://twitter.com/joaojmendes)) -- [Passoli Mirko](https://github.com/Paxol), Sicim S.p.A., ([Linkedin](https://www.linkedin.com/in/mirko-passoli)) +- [João Mendes](https://github.com/joaojmendes) +- [Passoli Mirko](https://github.com/Paxol) ## Version history |Version|Date|Comments| |-------|----|--------| -|1.1|Feb, 2024|Guest user filter + update to latest sharepoint template| +|1.1|Feb, 2024|Guest user filter + update to SPFx 1.18.2| |1.0|May, 2021|Initial release| ## Prerequisites diff --git a/samples/react-organization-chart/assets/sample.json b/samples/react-organization-chart/assets/sample.json index 96e496d90..2a458be6a 100644 --- a/samples/react-organization-chart/assets/sample.json +++ b/samples/react-organization-chart/assets/sample.json @@ -10,7 +10,7 @@ "Can be installed on SharePoint Server 2019, and SharePoint Online." ], "creationDateTime": "2021-05-03", - "updateDateTime": "2021-05-03", + "updateDateTime": "2024-02-25", "products": [ "SharePoint" ], @@ -21,7 +21,7 @@ }, { "key": "SPFX-VERSION", - "value": "1.4.1" + "value": "1.18.2" } ], "thumbnails": [ @@ -51,6 +51,10 @@ "pictureUrl": "https://github.com/joaojmendes.png", "name": "João Mendes", "twitter": "joaojmendes" + }, + { + "gitHubAccount": "Paxol", + "pictureUrl": "https://github.com/Paxol.png" } ], "references": [ diff --git a/samples/react-organization-chart/src/common/Utils.ts b/samples/react-organization-chart/src/common/Utils.ts index 0c9197cc5..e5b2d9b40 100644 --- a/samples/react-organization-chart/src/common/Utils.ts +++ b/samples/react-organization-chart/src/common/Utils.ts @@ -18,8 +18,10 @@ export const getUserPhoto = async (userId: string): Promise => { const personaImgUrl = PROFILE_IMAGE_URL + userId; // tslint:disable-next-line: no-use-before-declare + // eslint-disable-next-line @typescript-eslint/no-use-before-define const url: string = await getImageBase64(personaImgUrl); + // eslint-disable-next-line @typescript-eslint/no-use-before-define const newHash = await getMd5HashForUrl(url); if ( @@ -37,7 +39,7 @@ export const getUserPhoto = async (userId: string): Promise => { * @param url */ export const getMd5HashForUrl = async (url: string): Promise> => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any + // eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/no-use-before-define const library : any = await loadSPComponentById(MD5_MODULE_ID) ; try { const md5Hash = library.Md5Hash; @@ -71,6 +73,7 @@ export const getImageBase64 = async (pictureUrl: string): Promise => { const image = new Image(); image.addEventListener("load", () => { const tempCanvas = document.createElement("canvas"); + // eslint-disable-next-line no-unused-expressions, no-sequences (tempCanvas.width = image.width), (tempCanvas.height = image.height), tempCanvas.getContext("2d")?.drawImage(image, 0, 0); diff --git a/samples/react-organization-chart/src/components/HoverCard/ExpandedCard.tsx b/samples/react-organization-chart/src/components/HoverCard/ExpandedCard.tsx index cb98dc180..c4b6f55a1 100644 --- a/samples/react-organization-chart/src/components/HoverCard/ExpandedCard.tsx +++ b/samples/react-organization-chart/src/components/HoverCard/ExpandedCard.tsx @@ -34,6 +34,7 @@ export const ExpandedCard: React.FunctionComponent = ( return; } + // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { if (!user.manager) return; @@ -67,7 +68,7 @@ export const ExpandedCard: React.FunctionComponent = ( {user.email} -
+
) } @@ -90,7 +91,7 @@ export const ExpandedCard: React.FunctionComponent = ( {user.workPhone} -
+
)} {user.location && ( @@ -133,7 +134,7 @@ export const ExpandedCard: React.FunctionComponent = ( pictureUrl={manager.pictureUrl} text={manager.displayName} secondaryText={manager.title} - > + /> )} diff --git a/samples/react-organization-chart/src/components/HoverCard/useHoverCardStyles.ts b/samples/react-organization-chart/src/components/HoverCard/useHoverCardStyles.ts index b2bd6603c..01665ea03 100644 --- a/samples/react-organization-chart/src/components/HoverCard/useHoverCardStyles.ts +++ b/samples/react-organization-chart/src/components/HoverCard/useHoverCardStyles.ts @@ -2,7 +2,7 @@ import { IButtonStyles, IDocumentCardActionsStyles, IStackStyles, mergeStyles, m import type { Theme } from "spfx-uifabric-themes"; const currentTheme: Theme = window.__themeState__.theme; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type export const useHoverCardStyles = () => { const stackPersonaStyles: Partial = { diff --git a/samples/react-organization-chart/src/components/OrgChart/OrgChart.tsx b/samples/react-organization-chart/src/components/OrgChart/OrgChart.tsx index 34f833427..5394388da 100644 --- a/samples/react-organization-chart/src/components/OrgChart/OrgChart.tsx +++ b/samples/react-organization-chart/src/components/OrgChart/OrgChart.tsx @@ -99,11 +99,11 @@ export const OrgChart: React.FunctionComponent = ( onUserSelected={onUserSelected} selectedUser={currentUser} showActionsBar={showActionsBar} - > + />
+ /> ); } @@ -117,7 +117,7 @@ export const OrgChart: React.FunctionComponent = ( onUserSelected={onUserSelected} selectedUser={currentUser} showActionsBar={showActionsBar} - > + /> ); } @@ -158,6 +158,7 @@ export const OrgChart: React.FunctionComponent = ( ); React.useEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { try { if (startFromUserId === undefined) return; @@ -177,6 +178,7 @@ export const OrgChart: React.FunctionComponent = ( } const profileResponse = await getUserProfile(startFromUserId); const wCurrentUser: IUserInfo = await manpingUserProperties( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion profileResponse!.currentUserProfile ); dispatch({ @@ -205,6 +207,7 @@ export const OrgChart: React.FunctionComponent = ( }, [getUserProfile, startFromUserId]); React.useEffect(() => { + // eslint-disable-next-line @typescript-eslint/no-floating-promises (async () => { if (!currentUser || !currentUser.id) return; dispatch({ @@ -251,7 +254,7 @@ export const OrgChart: React.FunctionComponent = ( size={SpinnerSize.large} label={"loading Organization Chart..."} labelPosition={"bottom"} - > + /> ); @@ -284,15 +287,16 @@ export const OrgChart: React.FunctionComponent = ( {renderManagers} + /> {renderDirectReports.length && ( <> -
-
+
+
)} diff --git a/samples/react-organization-chart/src/components/OrgChart/useOrgChartStyles.ts b/samples/react-organization-chart/src/components/OrgChart/useOrgChartStyles.ts index e2e1646c6..383a09c2b 100644 --- a/samples/react-organization-chart/src/components/OrgChart/useOrgChartStyles.ts +++ b/samples/react-organization-chart/src/components/OrgChart/useOrgChartStyles.ts @@ -2,7 +2,7 @@ import { mergeStyles, mergeStyleSets } from "@fluentui/react"; const currentTheme = window.__themeState__.theme; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type export const useOrgChartStyles = () => { const orgChartClasses = mergeStyleSets({ diff --git a/samples/react-organization-chart/src/components/PersonCard/usePersonaCardStyles.ts b/samples/react-organization-chart/src/components/PersonCard/usePersonaCardStyles.ts index 45de73706..e99a5fddf 100644 --- a/samples/react-organization-chart/src/components/PersonCard/usePersonaCardStyles.ts +++ b/samples/react-organization-chart/src/components/PersonCard/usePersonaCardStyles.ts @@ -2,7 +2,7 @@ import { IButtonStyles, IDocumentCardActionsStyles, IStackStyles, mergeStyles, m import type { Theme } from "spfx-uifabric-themes"; const currentTheme: Theme = window.__themeState__.theme; -// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/explicit-function-return-type export const usePersonaCardStyles = () => { const stackPersonaStyles: Partial = { diff --git a/samples/react-organization-chart/src/hooks/useGetUserProperties.ts b/samples/react-organization-chart/src/hooks/useGetUserProperties.ts index 43f635e63..9e61ce153 100644 --- a/samples/react-organization-chart/src/hooks/useGetUserProperties.ts +++ b/samples/react-organization-chart/src/hooks/useGetUserProperties.ts @@ -1,3 +1,4 @@ +/* eslint-disable @typescript-eslint/no-non-null-assertion */ import { sp, SPBatch } from "@pnp/sp/"; import { IUserInfo } from "../models/IUserInfo"; import * as React from "react"; @@ -57,10 +58,12 @@ export const useGetUserProperties = (): { // Get Direct Reports if exists if (wDirectReports && wDirectReports.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define reportsLists = await getDirectReports(wDirectReports, showGuestUsers); } // Get Managers if exists if (startUser && wExtendedManagers && wExtendedManagers.length > 0) { + // eslint-disable-next-line @typescript-eslint/no-use-before-define managersList = await getExtendedManagers( wExtendedManagers, loginNameStartUser!, @@ -88,10 +91,12 @@ const getDirectReports = async ( `${userReport}__orgchart__` ); if (!cacheDirectReport) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises sp.profiles .inBatch(batch) .getPropertiesFor(userReport) .then(async (directReport: IPersonProperties) => { + // eslint-disable-next-line @typescript-eslint/no-use-before-define const userInfo = await manpingUserProperties(directReport); if (!showGuestUsers && userInfo.userType === "Guest") return; @@ -99,6 +104,7 @@ const getDirectReports = async ( await set(`${userReport}__orgchart__`, directReport); }); } else { + // eslint-disable-next-line @typescript-eslint/no-use-before-define const userInfo = await manpingUserProperties(cacheDirectReport); if (!showGuestUsers && userInfo.userType === "Guest") continue; @@ -126,10 +132,12 @@ const getExtendedManagers = async ( `${manager}__orgchart__` ); if (!cacheManager) { + // eslint-disable-next-line @typescript-eslint/no-floating-promises sp.profiles .inBatch(batch) .getPropertiesFor(manager) .then(async (_profile: IPersonProperties) => { + // eslint-disable-next-line @typescript-eslint/no-use-before-define const userInfo = await manpingUserProperties(_profile); if (!showGuestUsers && userInfo.userType === "Guest") return; @@ -137,6 +145,7 @@ const getExtendedManagers = async ( await set(`${manager}__orgchart__`, _profile); }); } else { + // eslint-disable-next-line @typescript-eslint/no-use-before-define const userInfo = await manpingUserProperties(cacheManager); if (!showGuestUsers && userInfo.userType === "Guest") continue; @@ -147,6 +156,7 @@ const getExtendedManagers = async ( return wManagers; }; +// eslint-disable-next-line @typescript-eslint/explicit-function-return-type function userTypeMapper(userType: string) { switch (userType) { case "0":