Added New sample on react-graph-telephonedirectory

This commit is contained in:
Dipen Shah 2020-07-14 18:57:41 +05:30
parent ee89a8182f
commit 4652b399df
41 changed files with 19504 additions and 0 deletions

7
samples/.yo-rc.json Normal file
View File

@ -0,0 +1,7 @@
{
"@microsoft/generator-sharepoint": {
"isCreatingSolution": true,
"environment": "spo",
"whichFolder": "subdir"
}
}

View File

@ -0,0 +1,25 @@
# EditorConfig helps developers define and maintain consistent
# coding styles between different editors and IDEs
# editorconfig.org
root = true
[*]
# change these settings to your own preference
indent_style = space
indent_size = 2
# we recommend you to keep these unchanged
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[{package,bower}.json]
indent_style = space
indent_size = 2

View File

@ -0,0 +1,32 @@
# Logs
logs
*.log
npm-debug.log*
# Dependency directories
node_modules
# Build generated files
dist
lib
solution
temp
*.sppkg
# Coverage directory used by tools like istanbul
coverage
# OSX
.DS_Store
# Visual Studio files
.ntvs_analysis.dat
.vs
bin
obj
# Resx Generated Code
*.resx.ts
# Styles Generated Code
*.scss.ts

View File

@ -0,0 +1,12 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.9.1",
"libraryName": "react-graph-telephonedirectory",
"libraryId": "e1a26cc8-a086-454f-9f16-9aeb0a276bcc",
"environment": "spo",
"packageManager": "npm",
"isCreatingSolution": true,
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,61 @@
# React Graph Telephone Directory
## Summary
This is sample webpart using SPFx and MSGraph to fetch the users information based on First Name, Last Name and Email Address.
It will fetch the data and display in details list.
[picture of the web part in action]
## Used SharePoint Framework Version
![1.9.1](https://img.shields.io/badge/version-1.9.1-green.svg)
## Applies to
* [SharePoint Framework](https://docs.microsoft.com/sharepoint/dev/spfx/sharepoint-framework-overview)
* [Office 365 tenant](https://docs.microsoft.com/sharepoint/dev/spfx/set-up-your-development-environment)
> Update accordingly as needed.
## Prerequisites
> Any special pre-requisites?
## Solution
Solution|Author(s)
--------|---------
folder name | Author details
## Version history
Version|Date|Comments
-------|----|--------
1.1|September 2, 2025|Update comment
1.0|August 29, 2025|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.**
---
## Minimal Path to Awesome
* Clone this repository
* in the command line run:
* `npm install`
* `gulp serve`
> Include any additional steps as needed.
## Features
Description of the web part with possible additional details than in short summary.
This Web Part illustrates the following concepts on top of the SharePoint Framework:
* topic 1
* topic 2
* topic 3
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/readme-template" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 444 KiB

View File

@ -0,0 +1,19 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"telephonedirectory-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/telephonedirectory/TelephonedirectoryWebPart.js",
"manifest": "./src/webparts/telephonedirectory/TelephonedirectoryWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"TelephonedirectoryWebPartStrings": "lib/webparts/telephonedirectory/loc/{locale}.js",
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
"deployCdnPath": "temp/deploy"
}

View File

@ -0,0 +1,7 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
"workingDir": "./temp/deploy/",
"account": "<!-- STORAGE ACCOUNT NAME -->",
"container": "react-graph-telephonedirectory",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,23 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-graph-telephonedirectory-client-side-solution",
"id": "e1a26cc8-a086-454f-9f16-9aeb0a276bcc",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"isDomainIsolated": true,
"webApiPermissionRequests": [
{
"resource": "Microsoft Graph",
"scope": "Directory.Read.All"
},
{
"resource": "Microsoft Graph",
"scope": "User.Read"
}
]
},
"paths": {
"zippedPackage": "solution/react-graph-telephonedirectory.sppkg"
}
}

View File

@ -0,0 +1,10 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/core-build/serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://localhost:5432/workbench",
"api": {
"port": 5432,
"entryPath": "node_modules/@microsoft/sp-webpart-workbench/lib/api/"
}
}

View File

@ -0,0 +1,4 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
"cdnBasePath": "<!-- PATH TO CDN -->"
}

View File

@ -0,0 +1,7 @@
'use strict';
const gulp = require('gulp');
const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
build.initialize(gulp);

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,42 @@
{
"name": "react-graph-telephonedirectory",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.9.1",
"@microsoft/sp-lodash-subset": "1.9.1",
"@microsoft/sp-office-ui-fabric-core": "1.9.1",
"@microsoft/sp-webpart-base": "1.9.1",
"@pnp/spfx-controls-react": "^1.19.0",
"@types/es6-promise": "0.0.33",
"@types/react": "16.8.8",
"@types/react-dom": "16.8.3",
"@types/webpack-env": "1.13.1",
"office-ui-fabric-react": "6.189.2",
"react": "16.8.5",
"react-dom": "16.8.5"
},
"resolutions": {
"@types/react": "16.8.8"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.9.1",
"@microsoft/sp-tslint-rules": "1.9.1",
"@microsoft/sp-module-interfaces": "1.9.1",
"@microsoft/sp-webpart-workbench": "1.9.1",
"@microsoft/rush-stack-compiler-2.9": "0.7.16",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

View File

@ -0,0 +1,11 @@
import { MSGraphClient } from "@microsoft/sp-http";
import { IUserProperties } from "./IUserProperties";
/**
* Service to declare the methods
*/
export interface IMSGraphService{
getUserProperties(email:string,context:MSGraphClient):Promise<IUserProperties[]>;
getUserPropertiesBySearch(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>;
getUserPropertiesByFirstName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>;
getUserPropertiesByLastName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>;
}

View File

@ -0,0 +1,9 @@
export interface IUserProperties{
displayName:string;
email:string;
mobilePhone:string;
preferredLanguage:string;
JobTitle:string;
OfficeLocation:string;
businessPhone:string;
}

View File

@ -0,0 +1,116 @@
import { IMSGraphService } from "./IMSGraphService";
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { IUserProperties } from "./IUserProperties";
import { MSGraphClient,MSGraphClientFactory } from "@microsoft/sp-http";
import { Log } from "@microsoft/sp-core-library";
const LOG_SOURCE = "MSGraphService";
export class MSGraphService implements IMSGraphService{
public async getUserProperties(email:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = [];
try {
//let client:MSGraphClient = await context.msGraphClientFactory.getClient().then();
let endPoint:string = `/Users/${email}`;
let response = await client.api(`${endPoint}`).version("v1.0").get();
if(response){
userProperties.push({
businessPhone:response.businessPhones[0],
displayName:response.displayName,
email:response.mail,
JobTitle:response.jobTitle,
OfficeLocation:response.officeLocation,
mobilePhone:response.mobilePhone,
preferredLanguage:response.preferredLanguage
});
}
} catch (error) {
console.log(error);
Log.error(LOG_SOURCE+"getUserProperties():",error);
}
return userProperties;
}
public async getUserPropertiesByLastName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = [];
try {
let res = await client.api("users")
.version("v1.0")
.filter(`(startswith(surname,'${escape(searchFor)}'))`).get();
if(res.value.length !== 0){
res.value.map((_userProperty,_index)=>{
if(_userProperty.mail !== null){
userProperties.push({
businessPhone:_userProperty.businessPhones[0],
displayName:_userProperty.displayName,
email:_userProperty.mail,
JobTitle:_userProperty.jobTitle,
OfficeLocation:_userProperty.officeLocation,
mobilePhone:_userProperty.mobilePhone,
preferredLanguage:_userProperty.preferredLanguage
});
}
});
}
} catch (error) {
console.log(error);
Log.error(LOG_SOURCE+"getUserPropertiesByLastName():",error);
}
return userProperties;
}
public async getUserPropertiesByFirstName(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = [];
try {
let res = await client.api("users")
.version("v1.0")
.filter(`(startswith(givenName,'${escape(searchFor)}'))`).get();
if(res.value.length !== 0){
res.value.map((_userProperty,_index)=>{
if(_userProperty.mail !== null){
userProperties.push({
businessPhone:_userProperty.businessPhones[0],
displayName:_userProperty.displayName,
email:_userProperty.mail,
JobTitle:_userProperty.jobTitle,
OfficeLocation:_userProperty.officeLocation,
mobilePhone:_userProperty.mobilePhone,
preferredLanguage:_userProperty.preferredLanguage
});
}
});
}
} catch (error) {
console.log(error);
Log.error(LOG_SOURCE+"getUserPropertiesBySearch():",error);
}
return userProperties;
}
public async getUserPropertiesBySearch(searchFor:string,client:MSGraphClient):Promise<IUserProperties[]>{
let userProperties:IUserProperties[] = [];
try {
let res = await client.api("users")
.version("v1.0")
.filter(`(startswith(displayName,'${escape(searchFor)}'))`).get();
if(res.value.length !== 0){
res.value.map((_userProperty,_index)=>{
userProperties.push({
businessPhone:_userProperty.businessPhones[0],
displayName:_userProperty.displayName,
email:_userProperty.mail,
JobTitle:_userProperty.jobTitle,
OfficeLocation:_userProperty.officeLocation,
mobilePhone:_userProperty.mobilePhone,
preferredLanguage:_userProperty.preferredLanguage
});
});
}
} catch (error) {
console.log(error);
Log.error(LOG_SOURCE+"getUserPropertiesBySearch():",error);
}
return userProperties;
}
}

View File

@ -0,0 +1 @@
// A file is required to be in the root of the /src directory by the TypeScript compiler

View File

@ -0,0 +1,27 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "586d82f7-79c3-4fc6-be7b-290fa1e4f1f6",
"alias": "TelephonedirectoryWebPart",
"componentType": "WebPart",
// The "*" signifies that the version should be taken from the package.json
"version": "*",
"manifestVersion": 2,
// If true, the component can only be installed on sites where Custom Script is allowed.
// 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,
"supportedHosts": ["SharePointWebPart"],
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "telephonedirectory" },
"description": { "default": "Search user from organization using first name, last name and email address" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "telephonedirectory"
}
}]
}

View File

@ -0,0 +1,80 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import {
BaseClientSideWebPart,
IPropertyPaneConfiguration,
PropertyPaneTextField
} from '@microsoft/sp-webpart-base';
import * as strings from 'TelephonedirectoryWebPartStrings';
import Telephonedirectory from './components/Telephonedirectory';
import { ITelephoneDirectoryProps } from './components/ITelephonedirectoryProps';
import { MSGraphService } from '../../Services/MSGraphService';
import { MSGraphClient } from "@microsoft/sp-http";
export interface ITelephonedirectoryWebPartProps {
description: string;
Title:string;
}
export default class TelephonedirectoryWebPart extends BaseClientSideWebPart<ITelephonedirectoryWebPartProps> {
private MSGraphServiceInstance:MSGraphService;
private MSGraphClient:MSGraphClient;
public render(): void {
const element: React.ReactElement<ITelephoneDirectoryProps> = React.createElement(
Telephonedirectory,
{
description: this.properties.description,
MSGraphServiceInstance:this.MSGraphServiceInstance,
context:this.context,
MsGraphClient:this.MSGraphClient,
DisplayMode:this.displayMode,
WebpartTitle:strings.WebpartTitle,
updateProperty: (value: string) => {
this.properties.Title = value;
}
}
);
ReactDom.render(element, this.domElement);
}
protected async onInit(){
await super.onInit();
this.MSGraphServiceInstance = new MSGraphService();
this.MSGraphClient = await this.context.msGraphClientFactory.getClient();
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse('1.0');
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField('description', {
label: strings.DescriptionFieldLabel
}),
PropertyPaneTextField('Title',{
label: strings.Title
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,102 @@
import * as React from "react";
import { ByEmailProps } from "./ByEmailProps";
import styles from '../Telephonedirectory.module.scss';
import { ByEmailState } from "./ByEmailState";
import { autobind } from "office-ui-fabric-react/lib/Utilities";
import { PeoplePicker, PrincipalType } from '@pnp/spfx-controls-react/lib/PeoplePicker';
import { } from "@pnp/spfx-controls-react/";
import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack';
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "ByFirstName";
const stackTokens = { childrenGap: 50 };
const iconProps = { iconName: 'Calendar' };
const stackStyles: Partial<IStackStyles> = { root: { width: 650 } };
const columnProps: Partial<IStackProps> = {
tokens: { childrenGap: 15 },
styles: { root: { width: 700 } },
};
export class ByEmail extends React.Component<ByEmailProps,ByEmailState>{
constructor(props:ByEmailProps){
super(props);
this.state={
loading:false,
searchFor: '',
userProperties:[],
isDataFound:true,
};
}
@autobind
private _getPeoplePickerItems(items: any[]) {
if(items.length == 1){
this.getUsers(items[0].secondaryText !== ""?items[0].secondaryText:items[0].id.split('|').pop());
}
else
{
this.setState({
userProperties:[]
});
}
}
@autobind
private async getUsers(email:string) : Promise<any>{
this.setState({loading:true},async()=>{
await this.props.MSGraphServiceInstance
.getUserProperties(email,this.props.MSGraphClient)
// tslint:disable-next-line: no-shadowed-variable
.then((users)=>{
if(users.length !== 0){
this.setState({
userProperties:users,
isDataFound:true
});
}
else
{
this.setState({
userProperties:[],
isDataFound:false
});
}
});
});
}
public render(): React.ReactElement<ByEmailProps> {
return (
<div className={styles.telephonedirectory}>
<div>
<Stack horizontal tokens={stackTokens} styles={stackStyles}>
<Stack {...columnProps}>
<PeoplePicker
context={this.props.context}
placeholder=""
titleText="Email"
personSelectionLimit={1}
showtooltip={false}
disabled={false}
selectedItems={this._getPeoplePickerItems}
principalTypes={[PrincipalType.User]}
resolveDelay={1000} />
</Stack>
</Stack>
<div>
</div>
<div id='detailedList'>
{this.state.userProperties.length !== 0 &&
<DetailsList
items={this.state.userProperties}
columns={this.props.columns}
isHeaderVisible={true}
layoutMode={DetailsListLayoutMode.justified}
/>
}
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,10 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphService } from "../../../../Services/MSGraphService";
import { MSGraphClient } from "@microsoft/sp-http";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByEmailProps{
context:WebPartContext;
MSGraphServiceInstance:MSGraphService;
MSGraphClient:MSGraphClient;
columns:IColumn[];
}

View File

@ -0,0 +1,10 @@
import { IUserProperties } from "../../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByEmailState{
loading:boolean;
userProperties:IUserProperties[];
searchFor: string;
isDataFound:boolean;
}

View File

@ -0,0 +1,105 @@
import * as React from "react";
import { ByFirstNameProps } from "./ByFirstNameProps";
import styles from '../Telephonedirectory.module.scss';
import { ByFirstNameState } from "./ByFirstNameState";
import { autobind } from "office-ui-fabric-react/lib/Utilities";
import * as strings from 'TelephonedirectoryWebPartStrings';
import { Log } from "@microsoft/sp-core-library";
import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack';
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "ByFirstName";
const stackTokens = { childrenGap: 50 };
const stackStyles: Partial<IStackStyles> = { root: { width: 650 } };
const columnProps: Partial<IStackProps> = {
tokens: { childrenGap: 15 },
styles: { root: { width: 700 } },
};
export class ByFirstName extends React.Component<ByFirstNameProps,ByFirstNameState>{
constructor(props:ByFirstNameProps){
super(props);
this.state={
loading:false,
searchFor: '',
userProperties:[],
isDataFound:true,
};
}
@autobind
private searchUsers(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void {
try {
this.setState({
searchFor: newValue,
});
this.getUsers(newValue);
} catch (error) {
Log.error(LOG_SOURCE,error);
}
}
@autobind
private async getUsers(email:string) : Promise<any>{
this.setState({loading:true},async()=>{
await this.props.MSGraphServiceInstance
.getUserPropertiesByFirstName(email,this.props.MSGraphClient)
// tslint:disable-next-line: no-shadowed-variable
.then((users)=>{
if(users.length !== 0){
this.setState({
userProperties:users,
isDataFound:true
});
}
else
{
this.setState({
userProperties:[],
isDataFound:false
});
}
});
});
}
@autobind
private searchUsersError(value: string): string {
// The search for text cannot contain spaces
return (value == null || value.length == 0 || value.indexOf(" ") < 0)
? ''
: 'Nothing matched';
}
public render(): React.ReactElement<ByFirstNameProps> {
return (
<div className={styles.telephonedirectory}>
<div>
<Stack horizontal tokens={stackTokens} styles={stackStyles}>
<Stack {...columnProps}>
<TextField
label={strings.SearchUserByFirstName}
required={false}
value={this.state.searchFor}
onChange={this.searchUsers}
onGetErrorMessage={this.searchUsersError}
/>
</Stack>
</Stack>
<div>
</div>
<div id='detailedList'>
{this.state.userProperties.length !== 0 &&
<DetailsList
items={this.state.userProperties}
columns={this.props.Columns}
isHeaderVisible={true}
layoutMode={DetailsListLayoutMode.justified}
/>
}
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,10 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphService } from "../../../../Services/MSGraphService";
import { MSGraphClient } from "@microsoft/sp-http";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByFirstNameProps{
context:WebPartContext;
MSGraphServiceInstance:MSGraphService;
MSGraphClient:MSGraphClient;
Columns:IColumn[];
}

View File

@ -0,0 +1,10 @@
import { IUserProperties } from "../../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByFirstNameState{
loading:boolean;
userProperties:IUserProperties[];
searchFor: string;
isDataFound:boolean;
}

View File

@ -0,0 +1,108 @@
import * as React from "react";
import { ByLastNameProps } from "./ByLastNameProps";
import styles from '../Telephonedirectory.module.scss';
import { ByLastNameState } from "./ByLastNameState";
import { autobind } from "office-ui-fabric-react/lib/Utilities";
import * as strings from 'TelephonedirectoryWebPartStrings';
import { Log } from "@microsoft/sp-core-library";
import { Stack, IStackProps, IStackStyles } from 'office-ui-fabric-react/lib/Stack';
import { TextField } from "office-ui-fabric-react/lib/TextField";
import { DetailsList, DetailsListLayoutMode, IColumn, SelectionMode } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "ByFirstName";
const stackTokens = { childrenGap: 50 };
const iconProps = { iconName: 'Calendar' };
const stackStyles: Partial<IStackStyles> = { root: { width: 650 } };
const columnProps: Partial<IStackProps> = {
tokens: { childrenGap: 15 },
styles: { root: { width: 700 } },
};
export class ByLastName extends React.Component<ByLastNameProps,ByLastNameState>{
constructor(props:ByLastNameProps)
{
super(props);
this.state={
loading:false,
searchFor: '',
userProperties:[],
isDataFound:true,
};
}
@autobind
private searchUsers(event: React.FormEvent<HTMLInputElement | HTMLTextAreaElement>, newValue?: string): void {
try {
this.setState({
searchFor: newValue,
});
this.getUsers(newValue);
} catch (error) {
Log.error(LOG_SOURCE,error);
}
}
@autobind
private async getUsers(email:string) : Promise<any>{
this.setState({loading:true},async()=>{
await this.props.MSGraphServiceInstance
.getUserPropertiesByLastName(email,this.props.MSGraphClient)
// tslint:disable-next-line: no-shadowed-variable
.then((users)=>{
if(users.length !== 0){
this.setState({
userProperties:users,
isDataFound:true
});
}
else
{
this.setState({
userProperties:[],
isDataFound:false
});
}
});
});
}
@autobind
private searchUsersError(value: string): string {
// The search for text cannot contain spaces
return (value == null || value.length == 0 || value.indexOf(" ") < 0)
? ''
: 'Nothing matched';
}
public render(): React.ReactElement<ByLastNameProps> {
return (
<div className={styles.telephonedirectory}>
<div>
<Stack horizontal tokens={stackTokens} styles={stackStyles}>
<Stack {...columnProps}>
<TextField
label={strings.SearchUserByLastName}
required={false}
value={this.state.searchFor}
onChange={this.searchUsers}
onGetErrorMessage={this.searchUsersError}
/>
</Stack>
</Stack>
<div>
</div>
<div id='detailedList'>
{this.state.userProperties.length !== 0 &&
<DetailsList
items={this.state.userProperties}
columns={this.props.columns}
isHeaderVisible={true}
layoutMode={DetailsListLayoutMode.justified}
/>
}
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,11 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphService } from "../../../../Services/MSGraphService";
import { MSGraphClient } from "@microsoft/sp-http";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByLastNameProps{
context:WebPartContext;
MSGraphServiceInstance:MSGraphService;
MSGraphClient:MSGraphClient;
columns:IColumn[];
}

View File

@ -0,0 +1,8 @@
import { IUserProperties } from "../../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ByLastNameState{
loading:boolean;
userProperties:IUserProperties[];
searchFor: string;
isDataFound:boolean;
}

View File

@ -0,0 +1,8 @@
import { IUserProperties } from "../../../Services/IUserProperties";
import { IColumn } from "office-ui-fabric-react/lib/DetailsList";
export interface ITelephoneDirectoryState{
loading:boolean;
columns:IColumn[];
selectedKey:string;
}

View File

@ -0,0 +1,13 @@
import { MSGraphService } from "../../../Services/MSGraphService";
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { MSGraphClient } from "@microsoft/sp-http";
import { DisplayMode } from '@microsoft/sp-core-library';
export interface ITelephoneDirectoryProps {
description: string;
MSGraphServiceInstance:MSGraphService;
context:WebPartContext;
MsGraphClient:MSGraphClient;
DisplayMode:DisplayMode;
WebpartTitle:string;
updateProperty: (value: string) => void;
}

View File

@ -0,0 +1,78 @@
@import '~office-ui-fabric-react/dist/sass/References.scss';
.telephonedirectory {
.container {
max-width: 700px;
margin: 0px auto;
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.errorMessage{
color: red($color: #000000);
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
@include ms-fontColor-white;
}
.subTitle {
@include ms-font-l;
@include ms-fontColor-white;
}
.description {
@include ms-font-l;
@include ms-fontColor-white;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $ms-color-themePrimary;
border-color: $ms-color-themePrimary;
color: $ms-color-white;
// Basic Button
outline: transparent;
position: relative;
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
-webkit-font-smoothing: antialiased;
font-size: $ms-font-size-m;
font-weight: $ms-font-weight-regular;
border-width: 0;
text-align: center;
cursor: pointer;
display: inline-block;
padding: 0 16px;
.label {
font-weight: $ms-font-weight-semibold;
font-size: $ms-font-size-m;
height: 32px;
line-height: 32px;
margin: 0 4px;
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -0,0 +1,161 @@
import * as React from 'react';
import styles from './Telephonedirectory.module.scss';
import { ITelephoneDirectoryProps } from './ITelephonedirectoryProps';
import { ITelephoneDirectoryState } from "./ITelephoneDirectoryState";
import * as strings from 'TelephonedirectoryWebPartStrings';
import { WebPartTitle } from "@pnp/spfx-controls-react/lib/WebPartTitle";
import { ByFirstName } from "./ByFirstName/ByFirstName";
import { ByLastName } from "./ByLastName/ByLastName";
import { ByEmail } from "./ByEmail/ByEmail";
import { Pivot, PivotItem,PivotLinkFormat, PivotLinkSize } from 'office-ui-fabric-react/lib/Pivot';
import { IColumn } from 'office-ui-fabric-react/lib/DetailsList';
const LOG_SOURCE = "TelephoneDirectory";
export default class Telephonedirectory extends React.Component<ITelephoneDirectoryProps, ITelephoneDirectoryState> {
private headers = [
{ label: 'Name', key: 'displayName' },
{ label: 'Email', key: 'email' },
{ label:'Mobile Phone',key:'mobilePhone'},
{ label:'JobTitle',key:'JobTitle'},
{ label:'OfficeLocation',key:'OfficeLocation'},
{ label:'Business Phone',key:'businessPhone'
}];
constructor(props:ITelephoneDirectoryProps){
super(props);
const columns: IColumn[] = [
{
key: 'column1',
name: strings.DisplayName,
isRowHeader: true,
isSorted: true,
isSortedDescending: false,
sortAscendingAriaLabel: 'Sorted A to Z',
sortDescendingAriaLabel: 'Sorted Z to A',
fieldName: 'displayName',
minWidth: 100,
maxWidth: 300,
isResizable: false
},
{
key: 'column2',
name: strings.Email,
fieldName: 'email',
isSorted: true,
isSortedDescending: false,
minWidth: 200,
maxWidth: 300,
isResizable: false
},
{
key: 'column3',
name: strings.MobilePhone,
fieldName: 'mobilePhone',
isSorted: true,
isSortedDescending: false,
minWidth: 200,
maxWidth: 300,
isResizable: false
},
{
key: 'column5',
name: strings.JobTitle,
fieldName: 'JobTitle',
isSorted: true,
isSortedDescending: false,
minWidth: 200,
maxWidth: 300,
isResizable: false
},
{
key: 'column6',
name: strings.OfficeLocation,
fieldName: 'OfficeLocation',
isSorted: true,
isSortedDescending: false,
minWidth: 100,
maxWidth: 300,
isResizable: true
},
{
key: 'column7',
name: strings.businessPhone,
fieldName: 'businessPhone',
isSorted: true,
isSortedDescending: false,
minWidth: 100,
maxWidth: 300,
isResizable: true
}
];
this.state={
loading:false,
selectedKey:"byFirstName",
columns:columns
};
}
private _handleLinkClick = (item: PivotItem): void => {
this.setState({
selectedKey: item.props.itemKey
});
}
public render(): React.ReactElement<ITelephoneDirectoryProps> {
return(
<div className={ styles.telephonedirectory }>
<div>
<div>
<div>
<WebPartTitle displayMode={this.props.DisplayMode}
title={this.props.WebpartTitle}
updateProperty={this.props.updateProperty} />
<Pivot headersOnly={true}
selectedKey ={this.state.selectedKey}
onLinkClick = {this._handleLinkClick}
linkSize={PivotLinkSize.normal}
linkFormat={PivotLinkFormat.tabs}>
<PivotItem
headerText='Search User By First Name'
itemKey='byFirstName'
itemIcon="Group" ></PivotItem>
<PivotItem
headerText='Search User By Last Name'
itemKey='byLastName'
itemIcon="Group"></PivotItem>
<PivotItem
headerText='Search User By Email'
itemKey="byEmail"
itemIcon="Group"></PivotItem>
</Pivot><br/>
{this.state.selectedKey === "byFirstName" &&
<ByFirstName
MSGraphClient={this.props.MsGraphClient}
MSGraphServiceInstance={this.props.MSGraphServiceInstance}
context={this.props.context}
Columns={this.state.columns}></ByFirstName>
}
{this.state.selectedKey === "byLastName" &&
<ByLastName
MSGraphClient = {this.props.MsGraphClient}
MSGraphServiceInstance= {this.props.MSGraphServiceInstance}
context={this.props.context}
columns={this.state.columns}></ByLastName>
}
{this.state.selectedKey === "byEmail" &&
<ByEmail
MSGraphClient = {this.props.MsGraphClient}
MSGraphServiceInstance= {this.props.MSGraphServiceInstance}
context={this.props.context}
columns={this.state.columns}></ByEmail>
}
</div>
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,18 @@
define([], function() {
return {
"PropertyPaneDescription": "Beschreibung",
"BasicGroupName": "Gruppenname",
"DescriptionFieldLabel": "Beschreibung Feld",
"SearchUser":"Benutzer nach Vorname suchen",
"SearchUserByFirstName":"Benutzer nach Vorname suchen",
"SearchUserByLastName":"Suche Benutzer nach Nachname",
"DisplayName":"Anzeigename",
"Email":"Email",
"MobilePhone":"Mobiltelefon",
"JobTitle":"Berufsbezeichnung",
"OfficeLocation":"Bürostandort",
"businessPhone":"Geschäftstelefon",
"WebpartTitle":"Telefonbuch",
"Title":"Titel"
}
});

View File

@ -0,0 +1,18 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field",
"SearchUser":"Search User by First Name",
"SearchUserByFirstName":"Search User by First Name",
"SearchUserByLastName":"Search User by Last Name",
"DisplayName":"Display Name",
"Email":"Email",
"MobilePhone":"Mobile Phone",
"JobTitle":"Job Title",
"OfficeLocation":"Office Location",
"businessPhone":"Business Phone",
"WebpartTitle":"Telephone Directory",
"Title":"Title"
}
});

View File

@ -0,0 +1,22 @@
declare interface ITelephonedirectoryWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
SearchUser:string;
SearchUserByFirstName:string;
SearchUserByLastName:string;
DisplayName:string;
Email:string;
MobilePhone:string;
JobTitle:string;
OfficeLocation:string;
businessPhone:string;
WebpartTitle:string;
Title:string;
placeholder:string;
}
declare module 'TelephonedirectoryWebPartStrings' {
const strings: ITelephonedirectoryWebPartStrings;
export = strings;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -0,0 +1,38 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-2.9/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
"typeRoots": [
"./node_modules/@types",
"./node_modules/@microsoft"
],
"types": [
"es6-promise",
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection"
]
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules",
"lib"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "@microsoft/sp-tslint-rules/base-tslint.json",
"rules": {
"class-name": false,
"export-name": false,
"forin": false,
"label-position": false,
"member-access": true,
"no-arg": false,
"no-console": false,
"no-construct": false,
"no-duplicate-variable": true,
"no-eval": false,
"no-function-expression": true,
"no-internal-module": true,
"no-shadowed-variable": true,
"no-switch-case-fall-through": true,
"no-unnecessary-semicolons": true,
"no-unused-expression": true,
"no-use-before-declare": true,
"no-with-statement": true,
"semicolon": true,
"trailing-comma": false,
"typedef": false,
"typedef-whitespace": false,
"use-named-parameter": true,
"variable-name": false,
"whitespace": false
}
}