React-Tree-Organization-Chart (#798)

* commit

* Tree Organization Chart
This commit is contained in:
joaojmendes 2019-03-09 10:51:16 +00:00 committed by Vesa Juvonen
parent 2194dd957b
commit b6bea9922e
30 changed files with 18955 additions and 0 deletions

View File

@ -0,0 +1,62 @@
# React Tree Organization Web Part
## 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).
![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-GA-green.svg)
## Applies to
* [SharePoint Framework](https:/dev.office.com/sharepoint)
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
> Update accordingly as needed.
## WebPart Properties
Property |Type|Required| comments
--------------------|----|--------|----------
WebPart Title| Text| no|
Show Only My Team| Boolean | true
MaxDepth | Number| yes | Maximum number of levels to show
## Solution
Solution|Author(s)
--------|---------
Tree Organization WebPart|João Mendes
## Version history
Version|Date|Comments
-------|----|--------
1.0.0|Fev 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.**
---
## Minimal Path to Awesome
- Clone this repository
- in the command line run:
- `npm install`
- `gulp build`
- `gulp bundle --ship`
- `gulp package-solution --ship`
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/readme-template" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -0,0 +1,20 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"tree-org-chart-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/treeOrgChart/TreeOrgChartWebPart.js",
"manifest": "./src/webparts/treeOrgChart/TreeOrgChartWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"TreeOrgChartWebPartStrings": "lib/webparts/treeOrgChart/loc/{locale}.js",
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/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-tree-orgchart",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,14 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "react-tree-orgchart-client-side-solution",
"id": "d76a0c4f-d669-42eb-9533-68d5cec5e9d3",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false
},
"paths": {
"zippedPackage": "solution/react-tree-orgchart.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);

18143
samples/react-tree-orgchart/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,46 @@
{
"name": "react-tree-orgchart",
"version": "0.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
},
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.7.1-plusbeta",
"@microsoft/sp-lodash-subset": "1.7.1-plusbeta",
"@microsoft/sp-office-ui-fabric-core": "1.7.1-plusbeta",
"@microsoft/sp-webpart-base": "1.7.1-plusbeta",
"@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",
"@types/es6-promise": "0.0.33",
"@types/react": "16.4.2",
"@types/react-dom": "16.0.5",
"@types/webpack-env": "1.13.1",
"react": "16.3.2",
"react-dom": "16.3.2",
"react-sortable-tree": "^2.6.0"
},
"resolutions": {
"@types/react": "16.4.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.7.1-plusbeta",
"@microsoft/sp-tslint-rules": "1.7.1-plusbeta",
"@microsoft/sp-module-interfaces": "1.7.1-plusbeta",
"@microsoft/sp-webpart-workbench": "1.7.1-plusbeta",
"gulp": "~3.9.1",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"ajv": "~5.2.2"
}
}

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,39 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
import { graph } from "@pnp/graph";
import { sp } from '@pnp/sp';
import { ITreeChildren } from "../webparts/treeOrgChart/components/ITreeChildren";
export default class spservices {
constructor(private context:WebPartContext) {
sp.setup({
spfxContext: this.context
});
}
public async getUserProperties(user:string){
let currentUserProperties:any = await sp.profiles.getPropertiesFor(user);
console.log(currentUserProperties);
return currentUserProperties;
}
/**
* async GetUserProfileProperty
user:string */
public async getUserProfileProperty(user:string,property:string) {
let UserProperty:any = await sp.profiles.getUserProfilePropertyFor(user, property);
console.log(UserProperty);
return UserProperty;
}
}

View File

@ -0,0 +1,28 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "4f45c562-805c-42da-81cc-8e84bf7834b0",
"alias": "TreeOrgChartWebPart",
"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,
"preconfiguredEntries": [{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
"group": { "default": "Other" },
"title": { "default": "Tree Organization Chart" },
"description": { "default": "Tree Organization Chart" },
"officeFabricIconFontName": "Org",
"properties": {
"title": "My Team Organization Chart",
"currentUserTeam": true,
"maxLevels": 5
}
}]
}

View File

@ -0,0 +1,92 @@
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 { ITreeOrgChartProps } from './components/ITreeOrgChartProps';
import { PropertyFieldNumber } from '@pnp/spfx-property-controls/lib/PropertyFieldNumber';
import { setup as pnpSetup } from '@pnp/common';
export interface ITreeOrgChartWebPartProps {
title: string;
currentUserTeam: boolean;
maxLevels: number;
}
export default class TreeOrgChartWebPart extends BaseClientSideWebPart<ITreeOrgChartWebPartProps> {
public onInit(): Promise<void> {
pnpSetup({
spfxContext: this.context
});
return Promise.resolve();
}
public render(): void {
const element: React.ReactElement<ITreeOrgChartProps> = React.createElement(
TreeOrgChart,
{
title: this.properties.title,
displayMode: this.displayMode,
updateProperty: (value: string) => {
this.properties.title = value;
},
currentUserTeam: this.properties.currentUserTeam,
maxLevels: this.properties.maxLevels,
context: this.context
}
);
ReactDom.render(element, this.domElement);
}
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('title', {
label: strings.TitleFieldLabel
}),
PropertyPaneToggle('currentUserTeam', {
label: strings.CurrentUserTeamFieldLabel
}),
PropertyFieldNumber("maxLevels", {
key: "numberValue",
label: strings.MaxLevels,
description: strings.MaxLevels,
value: this.properties.maxLevels,
maxValue: 10,
minValue: 1,
disabled: false
})
]
}
]
}
]
};
}
}

View File

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

View File

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

View File

@ -0,0 +1,10 @@
import { DisplayMode } from '@microsoft/sp-core-library';
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface ITreeOrgChartProps {
title: string;
currentUserTeam:boolean;
maxLevels:number;
displayMode: DisplayMode;
updateProperty: (value: string) => void;
context: WebPartContext;
}

View File

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

View File

@ -0,0 +1,77 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.treeOrgChart {
.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);
}
.treeContainer{
height: 450px;
overflow: auto;
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
background-color: $ms-color-themeDark;
padding: 20px;
}
.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,244 @@
// SPFx React-tree-Organization-Chart
// Author: João Mendes
// Fev 2019
//
import * as React from 'react';
import styles from './TreeOrgChart.module.scss';
import { ITreeOrgChartProps } from './ITreeOrgChartProps';
import { ITreeOrgChartState } from './ITreeOrgChartState';
import { escape } from '@microsoft/sp-lodash-subset';
import SortableTree from 'react-sortable-tree';
import 'react-sortable-tree/style.css';
import { IPersonaSharedProps, Persona, PersonaSize, PersonaPresence } from 'office-ui-fabric-react/lib/Persona';
import { IconButton, IButtonProps } 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';
export default class TreeOrgChart extends React.Component<ITreeOrgChartProps, ITreeOrgChartState> {
private treeData: ITreeData[];
private treeChildren: ITreeChildren[];
private SPService: spservice;
constructor(props) {
super(props);
this.SPService = new spservice(this.props.context);
this.state = {
treeData: [],
isLoading: true
};
}
//
private handleTreeOnChange(treeData) {
this.setState({ treeData });
}
public async componentDidUpdate(prevProps: ITreeOrgChartProps, prevState: ITreeOrgChartState) {
if (this.props.currentUserTeam !== prevProps.currentUserTeam || this.props.maxLevels !== prevProps.maxLevels) {
await this.loadOrgchart();
}
}
public async componentDidMount() {
await this.loadOrgchart();
}
/*
// 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);
this.treeData = [];
// Test if show only my Team or All Organization Chart
if (!this.props.currentUserTeam) {
const treeManagers = await this.buildOrganizationChart(currentUserProperties);
treeManagers ?
this.treeData.push(treeManagers)
: null;
} else {
const treeManagers = await this.buildMyTeamOrganizationChart(currentUserProperties);
treeManagers ?
this.treeData.push({
title: (treeManagers.person),
expanded: true,
children: treeManagers.treeChildren
})
: null;
}
console.log(JSON.stringify(this.treeData));
this.setState({ treeData: this.treeData, isLoading: false });
}
/*
Build Organization Chart from currentUser
@parm : currentUserProperties
*/
public async buildOrganizationChart(currentUserProperties: any) {
// Get Managers
let treeManagers: ITreeData = null;
if (currentUserProperties.ExtendedManagers && currentUserProperties.ExtendedManagers.length > 0) {
treeManagers = await this.getUsers(currentUserProperties.ExtendedManagers[0]);
}
return treeManagers;
}
/*
// Get user from Top Manager
*/
private async getUsers(manager: string) {
let person: any;
let spUser: IPersonaSharedProps = {};
// Get User Properties
const managerProperties = await this.SPService.getUserProperties(manager);
const imageInitials: string[] = managerProperties.DisplayName.split(' ');
// Persona Card Properties
spUser.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
spUser.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${imageInitials[1].substring(0, 1).toUpperCase()}`;
spUser.text = managerProperties.DisplayName;
spUser.tertiaryText = managerProperties.Email;
spUser.secondaryText = managerProperties.Title;
// PersonaCard component
person = <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
} 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(' ');
spUser.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
spUser.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${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);
usersDirectReports ? treeChildren.push({ title: (person), children: usersDirectReports }) :
treeChildren.push({ title: (person) });
}
return treeChildren;
}
/*
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(' ');
// PersonaCard Props
manager.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${managerProperties.Email}`;
manager.imageInitials = `${imageInitials[0].substring(0, 1).toUpperCase()}${imageInitials[1].substring(0, 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;
}
// Get my Properties
const meImageInitials: string[] = currentUserProperties.DisplayName.split(' ');
me.imageUrl = `/_layouts/15/userphoto.aspx?size=L&username=${currentUserProperties.Email}`;
me.imageInitials = `${meImageInitials[0].substring(0, 1).toUpperCase()}${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;
}
// 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 = `${imageInitials[0].substring(0, 1).toUpperCase()}${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 };
}
// Render
public render(): React.ReactElement<ITreeOrgChartProps> {
return (
<div className={styles.treeOrgChart}>
<WebPartTitle displayMode={this.props.displayMode}
title={this.props.title}
updateProperty={this.props.updateProperty} />
{
this.state.isLoading ? <Spinner size={SpinnerSize.large} label="Loading Organization Chart ..."></Spinner> : null
}
<div className={styles.treeContainer}>
<SortableTree
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={(ev) => {
window.open(`https://eur.delve.office.com/?p=${rowInfo.node.title.props.tertiaryText}&v=work`);
}}
/>
],
})}
/>
</div>
</div>
);
}
}

View File

@ -0,0 +1,9 @@
define([], function () {
return {
"PropertyPaneDescription": "Tree Organization Chart",
"BasicGroupName": "Properties",
"TitleFieldLabel": "WebPart Title",
"CurrentUserTeamFieldLabel": "Show only my team ?",
"MaxLevels": "Max Depth"
}
});

View File

@ -0,0 +1,12 @@
declare interface ITreeOrgChartWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
TitleFieldLabel: string;
CurrentUserTeamFieldLabel:string;
MaxLevels: string;
}
declare module 'TreeOrgChartWebPartStrings' {
const strings: ITreeOrgChartWebPartStrings;
export = strings;
}

View File

@ -0,0 +1,47 @@
{
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.2/MicrosoftTeams.schema.json",
"manifestVersion": "1.2",
"packageName": "TreeOrgChart",
"id": "4f45c562-805c-42da-81cc-8e84bf7834b0",
"version": "0.1",
"developer": {
"name": "SPFx + Teams Dev",
"websiteUrl": "https://products.office.com/en-us/sharepoint/collaboration",
"privacyUrl": "https://privacy.microsoft.com/en-us/privacystatement",
"termsOfUseUrl": "https://www.microsoft.com/en-us/servicesagreement"
},
"name": {
"short": "TreeOrgChart"
},
"description": {
"short": "Tree Organization Chart",
"full": "Tree Organization Chart"
},
"icons": {
"outline": "tab20x20.png",
"color": "tab96x96.png"
},
"accentColor": "#004578",
"configurableTabs": [
{
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=4f45c562-805c-42da-81cc-8e84bf7834b0",
"canUpdateConfiguration": false,
"scopes": [
"team"
]
}
],
"validDomains": [
"*.login.microsoftonline.com",
"*.sharepoint.com",
"*.sharepoint-df.com",
"spoppe-a.akamaihd.net",
"spoprod-a.akamaihd.net",
"resourceseng.blob.core.windows.net",
"msft.spoppe.com"
],
"webApplicationInfo": {
"resource": "https://{teamSiteDomain}",
"id": "00000003-0000-0ff1-ce00-000000000000"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 933 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@ -0,0 +1,34 @@
{
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,
"module": "esnext",
"moduleResolution": "node",
"jsx": "react",
"declaration": true,
"sourceMap": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"outDir": "lib",
"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
}
}