Merge pull request #2085 from a1mery/app-secret-expiration-pagination
[react-graph-secret-expiration] - Add pagination
This commit is contained in:
commit
d16f505759
|
@ -10,7 +10,7 @@ I got the idea from this great article [Use Power Automate to Notify of Upcoming
|
|||
## Compatibility
|
||||
|
||||
![SPFx 1.12.1](https://img.shields.io/badge/SPFx-1.12.1-green.svg)
|
||||
![Node.js LTS v14 | LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v14%20%7C%20LTS%20v12%20%7C%20LTS%20v10-green.svg)
|
||||
![Node.js LTS v12 | LTS v10](https://img.shields.io/badge/Node.js-LTS%20v12%20%7C%20LTS%20v10-green.svg)
|
||||
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
|
||||
![Teams N/A: Untested with Microsoft Teams](https://img.shields.io/badge/Teams-N%2FA-lightgrey.svg "Untested with 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")
|
||||
|
@ -32,6 +32,7 @@ react-graph-app-secret-expiration | [Aimery Thomas](https://github.com/a1mery) (
|
|||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0|September 17, 2021|Initial release
|
||||
1.1|October 10, 2021|Add pagination
|
||||
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
@ -39,14 +40,14 @@ Version|Date|Comments
|
|||
- Clone this repository
|
||||
- Ensure that you are at the solution folder
|
||||
- In the command-line run:
|
||||
- **npm install**
|
||||
- **gulp bundle**
|
||||
- **gulp package-solution**
|
||||
- `npm install`
|
||||
- `gulp bundle`
|
||||
- `gulp package-solution`
|
||||
- Deploy the package to your app catalog
|
||||
- Approve the API permission request from the SharePoint admin
|
||||
- Add the web part to a page
|
||||
- In the command-line run:
|
||||
- **gulp serve --nobrowser**
|
||||
- `gulp serve --nobrowser`
|
||||
|
||||
## Features
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
"This sample web part shows the list of your applications registered in Azure AD along with their associated client secret/certificate expiration date."
|
||||
],
|
||||
"creationDateTime": "2021-09-17",
|
||||
"updateDateTime": "2021-09-17",
|
||||
"updateDateTime": "2021-10-10",
|
||||
"products": [
|
||||
"SharePoint",
|
||||
"Office"
|
||||
|
@ -22,6 +22,10 @@
|
|||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.12.1"
|
||||
},
|
||||
{
|
||||
"key": "PNPCONTROLS",
|
||||
"value": "Pagination"
|
||||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"solution": {
|
||||
"name": "react-graph-app-secret-expiration-client-side-solution",
|
||||
"id": "b25d85a4-7310-408a-a263-25959b0a5b1b",
|
||||
"version": "1.0.0.0",
|
||||
"version": "1.1.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"isDomainIsolated": false,
|
||||
"skipFeatureDeployment": true,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-graph-app-secret-expiration",
|
||||
"version": "0.0.1",
|
||||
"version": "1.1.0",
|
||||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "react-graph-app-secret-expiration",
|
||||
"version": "0.0.1",
|
||||
"version": "1.1.0",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
|
|
|
@ -5,7 +5,9 @@ import { ListView, IViewField, SelectionMode, GroupOrder, IGrouping } from "@pnp
|
|||
import { IApplications, IApplication, IFormattedApplication } from '../../../models/IApplication';
|
||||
import { IGraphAppSecretExpirationState } from './GraphAppSecretExpirationState';
|
||||
import * as moment from 'moment';
|
||||
import { DefaultButton, Spinner, mergeStyles } from '@fluentui/react';
|
||||
import { DefaultButton, Spinner, mergeStyles, SearchBox } from '@fluentui/react';
|
||||
import { Pagination } from "@pnp/spfx-controls-react/lib/pagination";
|
||||
import * as strings from 'GraphAppSecretExpirationWebPartStrings';
|
||||
|
||||
const stackItemHidden = mergeStyles({
|
||||
display: 'none',
|
||||
|
@ -57,9 +59,13 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
|
|||
super(props);
|
||||
this.state = {
|
||||
applications: [],
|
||||
filteredApplications: [],
|
||||
filterValue: "",
|
||||
searchFilter: "",
|
||||
error: undefined,
|
||||
loading: true,
|
||||
groupByFields: []
|
||||
groupByFields: [],
|
||||
page: 1
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -113,42 +119,80 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
|
|||
var today = (moment(Date.now())).format('DD-MMM-YYYY');
|
||||
try {
|
||||
applications.forEach(app => {
|
||||
|
||||
if (app.passwordCredentials.length > 0) {
|
||||
app.passwordCredentials.forEach(pswd => {
|
||||
let daysBeforeExpiration = moment.duration((moment(pswd.endDateTime)).diff(today, 'days'), 'days').asDays();
|
||||
let formatedApp: IFormattedApplication = {
|
||||
applicationId: app.appId,
|
||||
displayName: app.displayName,
|
||||
type: "Secret",
|
||||
expirationDate: (moment(pswd.endDateTime)).format('DD-MMM-YYYY'),
|
||||
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
|
||||
};
|
||||
displayedApplication.push(formatedApp);
|
||||
});
|
||||
}
|
||||
if (app.keyCredentials.length > 0) {
|
||||
app.keyCredentials.forEach(keyCred => {
|
||||
let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays();
|
||||
let formatedApp: IFormattedApplication = {
|
||||
applicationId: app.appId,
|
||||
displayName: app.displayName,
|
||||
type: "Certificate",
|
||||
expirationDate: (moment(keyCred.endDateTime)).format('DD-MMM-YYYY'),
|
||||
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
|
||||
};
|
||||
displayedApplication.push(formatedApp);
|
||||
});
|
||||
}
|
||||
app.passwordCredentials.forEach(pswd => {
|
||||
let daysBeforeExpiration = moment.duration((moment(pswd.endDateTime)).diff(today, 'days'), 'days').asDays();
|
||||
let formatedApp: IFormattedApplication = {
|
||||
applicationId: app.appId,
|
||||
displayName: app.displayName,
|
||||
type: "Secret",
|
||||
expirationDate: (moment(pswd.endDateTime)).format('DD-MMM-YYYY'),
|
||||
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
|
||||
};
|
||||
displayedApplication.push(formatedApp);
|
||||
});
|
||||
app.keyCredentials.forEach(keyCred => {
|
||||
let daysBeforeExpiration = moment.duration((moment(keyCred.endDateTime)).diff(today, 'days'), 'days').asDays();
|
||||
let formatedApp: IFormattedApplication = {
|
||||
applicationId: app.appId,
|
||||
displayName: app.displayName,
|
||||
type: "Certificate",
|
||||
expirationDate: (moment(keyCred.endDateTime)).format('DD-MMM-YYYY'),
|
||||
daysLeft: daysBeforeExpiration > 0 ? daysBeforeExpiration : 0
|
||||
};
|
||||
displayedApplication.push(formatedApp);
|
||||
});
|
||||
});
|
||||
this.setState({
|
||||
applications: displayedApplication
|
||||
applications: displayedApplication,
|
||||
filteredApplications: displayedApplication
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
private _getPage(selectedPage: number) {
|
||||
this.setState({
|
||||
page: selectedPage
|
||||
});
|
||||
}
|
||||
|
||||
private _filterApplication = (value, clear: boolean) => {
|
||||
let searchResult: IFormattedApplication[] = [];
|
||||
if (clear) {
|
||||
this.state.applications.forEach(app => {
|
||||
if (this._filterByProperties(app, value)) {
|
||||
searchResult.push(app);
|
||||
}
|
||||
});
|
||||
this.setState({
|
||||
filteredApplications: searchResult,
|
||||
filterValue: value
|
||||
});
|
||||
} else {
|
||||
this.setState({
|
||||
filteredApplications: this.state.applications,
|
||||
filterValue: value,
|
||||
page: 1
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private _filterByProperties(application: IFormattedApplication, filterValue) {
|
||||
if (application.applicationId.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
|
||||
return true;
|
||||
} else if (application.displayName.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
|
||||
return true;
|
||||
} else if (application.expirationDate.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
|
||||
return true;
|
||||
} else if (application.type.toLowerCase().indexOf(filterValue.toLowerCase()) >= 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private _groupView = () => {
|
||||
if (this.state.groupByFields.length === 0) {
|
||||
let groupByFields: IGrouping[] = [
|
||||
|
@ -177,21 +221,31 @@ export default class GraphAppSecretExpiration extends React.Component<IGraphAppS
|
|||
<p className={styles.title}>Application list :</p>
|
||||
<div className={this.state.loading ? "" : stackItemHidden}>
|
||||
<Spinner label="Loading..." ariaLive="assertive" labelPosition="left" />
|
||||
<br/>
|
||||
<br />
|
||||
</div>
|
||||
<div className={this.state.loading ? stackItemHidden : ""}>
|
||||
<DefaultButton text={this.state.groupByFields.length === 0 ? "Group by App ID" : "Ungroup"} onClick={this._groupView} />
|
||||
<SearchBox placeholder="Search" onChange={(e, text) => this._filterApplication(text, false)} onClear={() => this._filterApplication("", true)} value={this.state.filterValue} />
|
||||
<ListView
|
||||
items={this.state.applications}
|
||||
items={this.state.filteredApplications.slice(this.state.page === 1 || this.state.filterValue !== "" ? 0 : this.state.page * 10 - 10, this.state.page * 10)}
|
||||
viewFields={_viewFields}
|
||||
iconFieldName="ServerRelativeUrl"
|
||||
compact={true}
|
||||
selectionMode={SelectionMode.none}
|
||||
selection={this._getSelection}
|
||||
groupByFields={this.state.groupByFields}
|
||||
showFilter={true}
|
||||
showFilter={false}
|
||||
filterPlaceHolder="Search..." />
|
||||
</div>
|
||||
<Pagination
|
||||
currentPage={1}
|
||||
totalPages={Math.floor(this.state.filteredApplications.length / 10) + 1}
|
||||
onChange={(page) => this._getPage(page)}
|
||||
limiter={3} // Optional - default value 3
|
||||
hideFirstPageJump // Optional
|
||||
hideLastPageJump // Optional
|
||||
limiterIcon={"Emoji12"} // Optional
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -3,7 +3,11 @@ import { IApplications, IApplication,IPasswordCredential,IKeyCredential,IFormatt
|
|||
|
||||
export interface IGraphAppSecretExpirationState {
|
||||
applications: IFormattedApplication[];
|
||||
filteredApplications: IFormattedApplication[];
|
||||
filterValue: string;
|
||||
searchFilter: string;
|
||||
groupByFields: IGrouping[];
|
||||
page: number;
|
||||
error: string;
|
||||
loading: boolean;
|
||||
}
|
Loading…
Reference in New Issue