diff --git a/samples/react-graph-webpart-report/README.md b/samples/react-graph-webpart-report/README.md index 890ef5b81..155427623 100644 --- a/samples/react-graph-webpart-report/README.md +++ b/samples/react-graph-webpart-report/README.md @@ -50,8 +50,7 @@ Microsoft Graph permission: | Version | Date | Comments | | ------- | ---------------- | --------------- | | 1.0 | March 23, 2023 | Initial release | - - +| 2.0 | July 11, 2023 | Add minor features| ## Minimal Path to Awesome diff --git a/samples/react-graph-webpart-report/assets/sample.json b/samples/react-graph-webpart-report/assets/sample.json index edd83901b..b10af1ab2 100644 --- a/samples/react-graph-webpart-report/assets/sample.json +++ b/samples/react-graph-webpart-report/assets/sample.json @@ -10,7 +10,7 @@ "This sample web part shows a report of the web parts used on the current site." ], "creationDateTime": "2023-05-13", - "updateDateTime": "2023-05-13", + "updateDateTime": "2023-07-11", "products": [ "SharePoint" ], diff --git a/samples/react-graph-webpart-report/config/package-solution.json b/samples/react-graph-webpart-report/config/package-solution.json index 6e06ea380..a9706aa53 100644 --- a/samples/react-graph-webpart-report/config/package-solution.json +++ b/samples/react-graph-webpart-report/config/package-solution.json @@ -3,7 +3,7 @@ "solution": { "name": "react-graph-webpart-report-client-side-solution", "id": "d5339db5-8abe-451a-8afe-57a16de5d286", - "version": "1.0.0.0", + "version": "2.0.0.0", "includeClientSideAssets": true, "skipFeatureDeployment": true, "isDomainIsolated": false, diff --git a/samples/react-graph-webpart-report/package.json b/samples/react-graph-webpart-report/package.json index 436550d2f..3d1fc0105 100644 --- a/samples/react-graph-webpart-report/package.json +++ b/samples/react-graph-webpart-report/package.json @@ -1,6 +1,6 @@ { "name": "react-graph-webpart-report", - "version": "0.0.1", + "version": "2.0.0", "private": true, "engines": { "node": ">=16.13.0 <17.0.0" diff --git a/samples/react-graph-webpart-report/src/webparts/GraphService.ts b/samples/react-graph-webpart-report/src/webparts/GraphService.ts index 8980769a4..45716112c 100644 --- a/samples/react-graph-webpart-report/src/webparts/GraphService.ts +++ b/samples/react-graph-webpart-report/src/webparts/GraphService.ts @@ -1,62 +1,49 @@ import { MSGraphClientV3 } from "@microsoft/sp-http"; -import { SitePage } from "./types"; - +import { GraphSitePage, GraphSitePageCollection, GraphWebPartCollection } from "./types"; +import { BaseComponentContext } from "@microsoft/sp-component-base"; export interface IGraphService { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - GetWebParts(client: MSGraphClientV3, siteId: string, pageId: string): Promise; - // eslint-disable-next-line @typescript-eslint/no-explicit-any - GetSitePages(client: MSGraphClientV3, siteId: string): Promise; + GetWebParts(siteId: string, pageId: string): Promise; + GetSitePages(siteId: string): Promise; } +export class GraphService implements IGraphService { + private MSGraphClient: MSGraphClientV3; + private Context: BaseComponentContext; + constructor(Context: BaseComponentContext) { + this.Context = Context; + } -class GraphService implements IGraphService { + private async Get_Client(): Promise { + if (this.MSGraphClient === undefined) + this.MSGraphClient = await this.Context.msGraphClientFactory.getClient("3"); + return this.MSGraphClient; + } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - public async GetWebParts(client: MSGraphClientV3, siteId: string, pageId: string): Promise { - try{ - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const rawWebParts: any = await this.GET(client, "sites/" + siteId + "/pages/" + pageId + "/webparts","",""); - return rawWebParts; - } catch (error){ + public async GetWebParts(siteId: string, pageId: string): Promise { + try { + const client = await this.Get_Client(); + const retrievedWebParts: GraphWebPartCollection = await client.api("sites/" + siteId + "/pages/microsoft.graph.sitePage/" + pageId + "/webparts").version('beta').get(); + return retrievedWebParts; + } catch (error) { return null; } - - } - public async GetSitePages(client: MSGraphClientV3, siteId: string): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const rawPages: any = await this.GET(client, "sites/" + siteId + "/pages", "", "id,title"); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return rawPages.value.flatMap((rawPage: any) => ( - [ + public async GetSitePages(siteId: string): Promise { + const pages: GraphSitePage[] = []; + const client = await this.Get_Client(); + const retrievedPages: GraphSitePageCollection = await client.api("sites/" + siteId + "/pages/microsoft.graph.sitePage").select("id,title").version('beta').get(); + retrievedPages.value.forEach(page => { + pages.push( { - id: rawPage.id, - title: rawPage.title + id: page.id, + title: page.title } - ] - )); - } - - - // eslint-disable-next-line @typescript-eslint/no-explicit-any - private GET(client: MSGraphClientV3, api: string, filter?: string, select?: string, top?: number, responseType?: any): Promise { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return new Promise((resolve, reject) => { - // eslint-disable-next-line @typescript-eslint/no-floating-promises - client.api(api).version("beta").select(select).filter(filter).responseType(responseType) - // eslint-disable-next-line @typescript-eslint/no-explicit-any - .get((error: any, response: any) => { - if (error) { - reject(error); - return; - } - resolve(response); - }); + ) }); + return pages; } } -export const GraphServiceInstance = new GraphService(); diff --git a/samples/react-graph-webpart-report/src/webparts/WebPartData.ts b/samples/react-graph-webpart-report/src/webparts/WebPartData.ts index 9f5a9e40a..054e81891 100644 --- a/samples/react-graph-webpart-report/src/webparts/WebPartData.ts +++ b/samples/react-graph-webpart-report/src/webparts/WebPartData.ts @@ -1,35 +1,28 @@ -import { SitePage, WebPart } from "./types"; -import { GraphServiceInstance } from "./GraphService"; -import { MSGraphClientV3 } from "@microsoft/sp-http"; +import { GraphWebPartCollection, WebPart } from "./types"; +import { IGraphService } from "./GraphService"; +import {SitePage} from "@microsoft/microsoft-graph-types-beta" -export async function _getSiteWebParts(graphClient: MSGraphClientV3, siteId: string): Promise { +export async function _getSiteWebParts(service: IGraphService, siteId: string): Promise { try { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const siteWebParts: any = []; - const sitePages: SitePage[] = await GraphServiceInstance.GetSitePages(graphClient, siteId); - for (let i: number = 0; i ([ + const siteWebParts: WebPart[] = []; + const sitePages: SitePage[] = await service.GetSitePages(siteId); + for (let i: number = 0; i <= sitePages.length - 1; i++) { + const graphWebParts: GraphWebPartCollection | null = await service.GetWebParts(siteId, sitePages[i].id); + if (graphWebParts !== null) { + graphWebParts.value.forEach(siteWebPart => { + siteWebParts.push( { siteId: siteId, pageTitle: sitePages[i].title, id: siteWebPart.id, - title: siteWebPart.data.title, + title: siteWebPart.innerHtml !== undefined ? "Text" : siteWebPart.data.title, } - ])) - ); + ) + }); } } - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return siteWebParts.flatMap((t: any)=>t); - - + return siteWebParts; } catch (error) { console.error(error); return null; diff --git a/samples/react-graph-webpart-report/src/webparts/types.ts b/samples/react-graph-webpart-report/src/webparts/types.ts index 698be934c..b385c6a85 100644 --- a/samples/react-graph-webpart-report/src/webparts/types.ts +++ b/samples/react-graph-webpart-report/src/webparts/types.ts @@ -5,12 +5,38 @@ export type WebPart = { title: string; } -export type AggredatedWebParts = { - titles: string[]; - count: number[]; +export type GraphWebPart = { + data?: GraphWebPartData; + id: string; + webPartType: string; + innerHtml?: string; } -export type SitePage = { +export type GraphWebPartCollection = { + value: GraphWebPart[]; +} + +export type GraphWebPartData = { + audiences: string[]; + dataVersion: string[]; + description: string; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + properties: any; + // eslint-disable-next-line @typescript-eslint/no-explicit-any + serverProcessedContent: any; + title: string; +} + +export type AggredatedWebParts = { + WPTitles: string[]; + WPCount: number[]; +} + +export type GraphSitePageCollection = { + value: GraphSitePage[]; +} + +export type GraphSitePage = { id: string; title: string; } @@ -22,6 +48,6 @@ export type ChartDataCustom = { export type DataSet = { label: string; - data: number []; + data: number[]; //backgroundColor: string[]; } \ No newline at end of file diff --git a/samples/react-graph-webpart-report/src/webparts/webPartReport/WebPartReportWebPart.ts b/samples/react-graph-webpart-report/src/webparts/webPartReport/WebPartReportWebPart.ts index 8d77bc4f4..2ae8b4f99 100644 --- a/samples/react-graph-webpart-report/src/webparts/webPartReport/WebPartReportWebPart.ts +++ b/samples/react-graph-webpart-report/src/webparts/webPartReport/WebPartReportWebPart.ts @@ -11,7 +11,7 @@ import * as strings from 'WebPartReportWebPartStrings'; import WebPartReport from './components/WebPartReport'; import { IWebPartReportProps } from './components/IWebPartReportProps'; import { ITopActions, TopActionsFieldType } from '@microsoft/sp-top-actions'; -import { MSGraphClientV3 } from "@microsoft/sp-http"; +import {GraphService} from "./../GraphService" export interface IWebPartReportWebPartProps { description: string; @@ -20,7 +20,6 @@ export interface IWebPartReportWebPartProps { } export default class WebPartReportWebPart extends BaseClientSideWebPart { - private graphClient: MSGraphClientV3; private _isDarkTheme: boolean = false; public render(): void { @@ -33,24 +32,15 @@ export default class WebPartReportWebPart extends BaseClientSideWebPart { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return new Promise((resolve: () => void, reject: (error: any) => void): void => { - this.context.msGraphClientFactory - .getClient("3") - .then((client: MSGraphClientV3): void => { - this.graphClient = client; - resolve(); - }, err => reject(err)); - }); - } + // eslint-disable-next-line @typescript-eslint/no-empty-function + protected async onInit(): Promise {} protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void { if (!currentTheme) { @@ -111,19 +101,24 @@ export default class WebPartReportWebPart extends BaseClientSideWebPart{ this.properties.displayOption = newValue; - console.log("test",displayOption.toString() === "1"); this.render(); } }; diff --git a/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportProps.ts b/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportProps.ts index 1de9a7d4f..9ee3d1663 100644 --- a/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportProps.ts +++ b/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportProps.ts @@ -1,4 +1,4 @@ -import { MSGraphClientV3 } from "@microsoft/sp-http"; +import { IGraphService } from "../../GraphService"; export interface IWebPartReportProps { description: string; @@ -7,5 +7,6 @@ export interface IWebPartReportProps { hasTeamsContext: boolean; userDisplayName: string; siteId: string; - graphClient: MSGraphClientV3; + GraphService: IGraphService; + } diff --git a/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportWebPartState.ts b/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportWebPartState.ts index 1e6438b87..e735a8310 100644 --- a/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportWebPartState.ts +++ b/samples/react-graph-webpart-report/src/webparts/webPartReport/components/IWebPartReportWebPartState.ts @@ -2,6 +2,7 @@ import { AggredatedWebParts, WebPart } from "../../types"; export interface IWebPartReportWebPartState { webPartList: WebPart[]; - aggregatedWebPartList: AggredatedWebParts; + chartWebPartList: AggredatedWebParts; loading: boolean; + page: number; } \ No newline at end of file diff --git a/samples/react-graph-webpart-report/src/webparts/webPartReport/components/WebPartReport.tsx b/samples/react-graph-webpart-report/src/webparts/webPartReport/components/WebPartReport.tsx index 976beb41f..593a2c921 100644 --- a/samples/react-graph-webpart-report/src/webparts/webPartReport/components/WebPartReport.tsx +++ b/samples/react-graph-webpart-report/src/webparts/webPartReport/components/WebPartReport.tsx @@ -2,12 +2,12 @@ import * as React from 'react'; import styles from './WebPartReport.module.scss'; import { IWebPartReportProps } from './IWebPartReportProps'; import { _getSiteWebParts } from '../../WebPartData'; -import { WebPart } from '../../types'; import { ListView, IViewField } from "@pnp/spfx-controls-react/lib/ListView"; import { IWebPartReportWebPartState } from './IWebPartReportWebPartState'; import { ChartControl, ChartType } from '@pnp/spfx-controls-react/lib/ChartControl'; import { ChartData } from 'chart.js'; import { Spinner } from '@fluentui/react'; +import { Pagination } from "@pnp/spfx-controls-react/lib/Pagination"; const _viewFields: IViewField[] = [ { @@ -48,10 +48,7 @@ const options: any = { }; -let siteWebParts: WebPart[]; -let webPartsCounts: number[] = []; -let webPartsTitles: string[] = []; -const aggregatedWebPartData = new Map(); + export default class WebPartReport extends React.Component { @@ -60,62 +57,66 @@ export default class WebPartReport extends React.Component { - await this._setChartData(); + public async componentDidMount(): Promise { + await this._getWebParts(); } private loadingData(): Promise { - return new Promise((resolve, _reject) => { - - let countWP:number[] = []; - countWP = this.state.aggregatedWebPartList.count const data: ChartData = { - labels: this.state.aggregatedWebPartList.titles.length > 0 ? this.state.aggregatedWebPartList.titles : [], - datasets: [{ label: "WebParts", data: countWP.length > 0 ? countWP : [] }] + labels: this.state.chartWebPartList.WPTitles.length > 0 ? this.state.chartWebPartList.WPTitles : [], + datasets: [{ + label: "WebParts", + data: this.state.chartWebPartList.WPCount.length > 0 ? this.state.chartWebPartList.WPCount : [] + }] }; resolve(data); - }); } - public async _setChartData(): Promise { + public async _getWebParts(): Promise { - webPartsCounts = []; - webPartsTitles = []; - aggregatedWebPartData.clear(); + const webPartsCounts: number[] = []; + const webPartsTitles: string[] = []; + const webPartMap = new Map(); - siteWebParts = await _getSiteWebParts(this.props.graphClient, this.props.siteId.toString()); + webPartMap.clear(); + + const siteWebParts = await _getSiteWebParts(this.props.GraphService, this.props.siteId.toString()); siteWebParts.forEach(e => { - if (!aggregatedWebPartData.has(e.title)) { - aggregatedWebPartData.set(e.title, 1); + if (!webPartMap.has(e.title)) { + webPartMap.set(e.title, 1); } else { - aggregatedWebPartData.set(e.title, aggregatedWebPartData.get(e.title) + 1) + webPartMap.set(e.title, webPartMap.get(e.title) + 1) } }); - aggregatedWebPartData.forEach(a => { - webPartsCounts.push(a); - }); - aggregatedWebPartData.forEach((value, key) => { + webPartMap.forEach((value, key) => { + webPartsCounts.push(value); webPartsTitles.push(key); }); this.setState({ webPartList: siteWebParts, - aggregatedWebPartList: { - titles: webPartsTitles, - count: webPartsCounts + chartWebPartList: { + WPTitles: webPartsTitles, + WPCount: webPartsCounts }, loading: false }); } + private _getPage(selectedPage: number): void { + this.setState({ + page: selectedPage + }); + } public render(): React.ReactElement { const { @@ -126,22 +127,33 @@ export default class WebPartReport extends React.Component
-
-

Web parts list:

+
+

List of web parts:

+ this._getPage(page)} + limiter={3} // Optional - default value 3 + hideFirstPageJump // Optional + hideLastPageJump // Optional + limiterIcon={"Emoji12"} // Optional />
- +
);