Example of MS Graph Teams beta API usage (#652)

This commit is contained in:
Alex Terentiev 2018-11-09 03:47:22 -04:00 committed by Vesa Juvonen
parent f071f531bc
commit d3c7739383
24 changed files with 19301 additions and 0 deletions

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

32
samples/react-team-creator/.gitignore vendored Normal file
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,10 @@
{
"@microsoft/generator-sharepoint": {
"version": "1.6.0",
"libraryName": "teams-creator",
"libraryId": "3284bbaa-0441-4153-9738-69a27a4b5956",
"environment": "spo",
"packageManager": "npm",
"isCreatingSolution": true
}
}

View File

@ -0,0 +1,65 @@
# React Teams Creator Web Part
## Summary
The web part illustrates usage of MS Graph beta APIs to work with Teams:
* O365 group creation
* Team creation
* Channel creation
* Installation of an app
* Adding tab
* Getting apps from App Catalog
![React Side Panel Client-Side Web Part](./assets/teams-creator.png)
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/drop-1.6.0-green.svg)
## Applies to
* [SharePoint Framework](https:/dev.office.com/sharepoint)
* [MS Graph](https://developer.microsoft.com/en-us/graph)
* [MS Teams](https://docs.microsoft.com/en-us/microsoftteams/microsoft-teams)
## Solution
Solution|Author(s)
--------|---------
teams-creator-client-side-solution | Alex Terentiev ([Sharepointalist Inc.](http://www.sharepointalist.com), [AJIXuMuK](https://github.com/AJIXuMuK))
## Version history
Version|Date|Comments
-------|----|--------
1.0|October 17, 2018|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.**
## Features
Sample features:
- O365 Group creation
- MS Team creation
- Channel creation
- Teams App installation
- Adding Teams tab
- Usage of PnP React Controls
## Caveats
- There is no way to filter Teams Apps requested from App Catalog by App Type. Because of that dropdown displays not only apps that are available as Tabs but all of them.
- Although the app can be added as a Tab there is no API to configure the app completely. At least, there is no way to figure out what settings are there for this or that specific app. So, after tab creation in the Teams app a user will see "Set up tab" button in the fresh tab.
## Building the code
```bash
git clone the repo
npm i
npm i -g gulp
gulp
```
This package produces the following:
* lib/* - intermediate-stage commonjs build artifacts
* dist/* - the bundled script, along with other resources
* deploy/* - all resources which should be uploaded to a CDN.

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 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": {
"teams-creator-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/teamsCreator/TeamsCreatorWebPart.js",
"manifest": "./src/webparts/teamsCreator/TeamsCreatorWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"TeamsCreatorWebPartStrings": "lib/webparts/teamsCreator/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": "teams-creator",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,21 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "teams-creator-client-side-solution",
"id": "3284bbaa-0441-4153-9738-69a27a4b5956",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"webApiPermissionRequests": [{
"resource": "Microsoft Graph",
"scope": "Group.ReadWrite.All"
}, {
"resource": "Microsoft Graph",
"scope": "AppCatalog.ReadWrite.All"
}
]
},
"paths": {
"zippedPackage": "solution/teams-creator.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);

18201
samples/react-team-creator/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,36 @@
{
"name": "teams-creator",
"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.6.0",
"@microsoft/sp-lodash-subset": "1.6.0",
"@microsoft/sp-office-ui-fabric-core": "1.6.0",
"@microsoft/sp-webpart-base": "1.6.0",
"@pnp/spfx-controls-react": "1.9.0",
"@types/es6-promise": "0.0.33",
"@types/react": "15.6.6",
"@types/react-dom": "15.5.6",
"@types/webpack-env": "1.13.1",
"react": "15.6.2",
"react-dom": "15.6.2"
},
"devDependencies": {
"@microsoft/sp-build-web": "1.6.0",
"@microsoft/sp-module-interfaces": "1.6.0",
"@microsoft/sp-webpart-workbench": "1.6.0",
"tslint-microsoft-contrib": "~5.0.0",
"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,26 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "ebead8b1-1dcf-4426-a06a-a11137cc9b6e",
"alias": "TeamsCreatorWebPart",
"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": "Teams Creator" },
"description": { "default": "Teams Creator description" },
"officeFabricIconFontName": "Page",
"properties": {
"description": "Teams Creator"
}
}]
}

View File

@ -0,0 +1,60 @@
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 'TeamsCreatorWebPartStrings';
import TeamsCreator from './components/TeamsCreator';
import { ITeamsCreatorProps } from './components/ITeamsCreatorProps';
export interface ITeamsCreatorWebPartProps {
description: string;
}
export default class TeamsCreatorWebPart extends BaseClientSideWebPart<ITeamsCreatorWebPartProps> {
public render(): void {
const element: React.ReactElement<ITeamsCreatorProps > = React.createElement(
TeamsCreator,
{
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('description', {
label: strings.DescriptionFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,8 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface ITeamsCreatorProps {
/**
* Web Part context
*/
context: WebPartContext;
}

View File

@ -0,0 +1,55 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
.teamsCreator {
.container {
max-width: 700px;
margin: 0px 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;
}
.teamSection, .channelSection {
margin-bottom: 10px;
}
.buttons {
text-align: right;
margin-top: 20px;
.button {
margin: 0 4px;
}
}
.error {
color: $ms-color-errorText;
}
}

View File

@ -0,0 +1,583 @@
import * as React from 'react';
import styles from './TeamsCreator.module.scss';
import { ITeamsCreatorProps } from './ITeamsCreatorProps';
import { TextField, ITextField } from 'office-ui-fabric-react/lib/TextField';
import { Checkbox } from 'office-ui-fabric-react/lib/Checkbox';
import { Dropdown, IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
import { PrimaryButton, DefaultButton, ActionButton } from 'office-ui-fabric-react/lib/Button';
import { Spinner } from 'office-ui-fabric-react/lib/Spinner';
import { PeoplePicker, PrincipalType, IPeoplePickerUserItem } from "@pnp/spfx-controls-react/lib/PeoplePicker";
import * as strings from 'TeamsCreatorWebPartStrings';
import { MSGraphClient } from '@microsoft/sp-http';
/**
* State of the component
*/
export enum CreationState {
/**
* Initial state - user input
*/
notStarted,
/**
* creating all selected elements (group, team, channel, tab)
*/
creating,
/**
* everything has been created
*/
created,
/**
* error during creation
*/
error
}
/**
* App definition returned from App Catalog
*/
export interface ITeamsApp {
id: string;
externalId?: string;
name: string;
version: string;
distributionMethod: string;
}
/**
* State
*/
export interface ITeamsCreatorState {
/**
* Selected team name. Also used as group name
*/
teamName?: string;
/**
* team description
*/
teamDescription?: string;
/**
* Group owners
*/
owners?: string[];
/**
* group members
*/
members?: string[];
/**
* Flag if channel should be created
*/
createChannel?: boolean;
/**
* channel name
*/
channelName?: string;
/**
* channel description
*/
channelDescription?: string;
/**
* flag if we need to add a tab
*/
addTab?: boolean;
/**
* tab name
*/
tabName?: string;
/**
* teams apps from app catalog
*/
apps?: ITeamsApp[];
/**
* current state of the component
*/
creationState?: CreationState;
/**
* creation spinner text
*/
spinnerText?: string;
/**
* id of the selected app to be added as tab
*/
selectedAppId?: string;
}
export default class TeamsCreator extends React.Component<ITeamsCreatorProps, ITeamsCreatorState> {
constructor(props: ITeamsCreatorProps) {
super(props);
this.state = {
creationState: CreationState.notStarted
};
this._onClearClick = this._onClearClick.bind(this);
}
public render(): React.ReactElement<ITeamsCreatorProps> {
const {
teamName,
teamDescription,
createChannel,
channelName,
channelDescription,
addTab,
tabName,
apps,
creationState,
spinnerText,
selectedAppId
} = this.state;
const appsDropdownOptions: IDropdownOption[] = apps ? apps.map(app => { return { key: app.id, text: app.name }; }) : [];
return (
<div className={styles.teamsCreator}>
<h2>{strings.Welcome}</h2>
<div className={styles.container}>
{{
0: <div>
<div className={styles.teamSection}>
<TextField required={true} label={strings.TeamNameLabel} value={teamName} onChanged={this._onTeamNameChange.bind(this)}></TextField>
<TextField label={strings.TeamDescriptionLabel} value={teamDescription} onChanged={this._onTeamDescriptionChange.bind(this)}></TextField>
<PeoplePicker
context={this.props.context}
titleText={strings.Owners}
personSelectionLimit={3}
showHiddenInUI={false}
principleTypes={[PrincipalType.User]}
selectedItems={this._onOwnersSelected.bind(this)}
isRequired={true} />
<PeoplePicker
context={this.props.context}
titleText={strings.Members}
personSelectionLimit={3}
showHiddenInUI={false}
principleTypes={[PrincipalType.User]}
selectedItems={this._onMembersSelected.bind(this)} />
</div>
<Checkbox label={strings.CreateChannel} checked={createChannel} onChange={this._onCreateChannelChange.bind(this)} />
{createChannel && <div>
<div className={styles.channelSection}>
<TextField required={createChannel} label={strings.ChannelName} value={channelName} onChanged={this._onChannelNameChange.bind(this)}></TextField>
<TextField label={strings.ChannelDescription} value={channelDescription} onChanged={this._onChannelDescriptionChange.bind(this)}></TextField>
</div>
<Checkbox label={strings.AddTab} checked={addTab} onChange={this._onAddTabChange.bind(this)} />
{addTab && <div>
<TextField required={addTab} label={strings.TabName} value={tabName} onChanged={this._onTabNameChange.bind(this)}></TextField>
<Dropdown
required={addTab}
label={strings.App}
disabled={!this.state.apps}
options={appsDropdownOptions}
selectedKey={selectedAppId}
onChanged={this._onAppSelected.bind(this)}></Dropdown>
</div>}
</div>}
<div className={styles.buttons}>
<PrimaryButton text={strings.Create} className={styles.button} onClick={this._onCreateClick.bind(this)} />
<DefaultButton text={strings.Clear} className={styles.button} onClick={this._onClearClick} />
</div>
</div>,
1: <div>
<Spinner label={spinnerText} />
</div>,
2: <div>
<div>{strings.Success}</div>
<PrimaryButton iconProps={{ iconName: 'TeamsLogo' }} href='https://aka.ms/mstfw' target='_blank'>{strings.OpenTeams}</PrimaryButton>
<DefaultButton onClick={this._onClearClick}>{strings.StartOver}</DefaultButton>
</div>,
4: <div>
<div className={styles.error}>{strings.Error}</div>
<DefaultButton onClick={this._onClearClick}>{strings.StartOver}</DefaultButton>
</div>
}[creationState]}
</div>
</div>
);
}
private _onTeamNameChange(value: string) {
this.setState({
teamName: value
});
}
private _onTeamDescriptionChange(value: string) {
this.setState({
teamDescription: value
});
}
private _onChannelNameChange(value: string) {
this.setState({
channelName: value
});
}
private _onChannelDescriptionChange(value: string) {
this.setState({
channelDescription: value
});
}
private _onTabNameChange(value: string) {
this.setState({
tabName: value
});
}
private _onAppSelected(item: IDropdownOption) {
this.setState({
selectedAppId: item.key as string
});
}
private _onCreateChannelChange(e: React.FormEvent<HTMLElement | HTMLInputElement>, checked: boolean) {
this.setState({
createChannel: checked
});
}
private _onAddTabChange(e: React.FormEvent<HTMLElement | HTMLInputElement>, checked: boolean) {
this.setState({
addTab: checked
});
this._getAvailableApps();
}
private _onMembersSelected(members: IPeoplePickerUserItem[]) {
this.setState({
members: members.map(m => m.id)
});
}
private _onOwnersSelected(owners: IPeoplePickerUserItem[]) {
this.setState({
owners: owners.map(o => o.id)
});
}
private async _onCreateClick() {
this._processCreationRequest();
}
private _onClearClick() {
this._clearState();
}
private _clearState() {
this.setState({
teamName: '',
teamDescription: '',
members: [],
owners: [],
createChannel: false,
channelName: '',
channelDescription: '',
addTab: false,
tabName: '',
selectedAppId: '',
creationState: CreationState.notStarted,
spinnerText: ''
});
}
private async _getAvailableApps(): Promise<void> {
if (this.state.apps) {
return;
}
const context = this.props.context;
const graphClient = await context.msGraphClientFactory.getClient();
const appsResponse = await graphClient.api('appCatalogs/teamsApps').version('beta').get();
const apps = appsResponse.value as ITeamsApp[];
apps.sort((a, b) => {
if (a.name < b.name) {
return -1;
}
else if (a.name > b.name) {
return 1;
}
return 0;
});
this.setState({
apps: apps
});
}
/**
* Main flow
*/
private async _processCreationRequest() {
const context = this.props.context;
// initializing graph client to be used in all requests
const graphClient = await context.msGraphClientFactory.getClient();
this.setState({
creationState: CreationState.creating,
spinnerText: strings.CreatingGroup
});
//
// Create a group first
//
const groupId = await this._createGroup(graphClient);
if (!groupId) {
this._onError();
return;
}
this.setState({
spinnerText: strings.CreatingTeam
});
//
// Create team
//
const teamId = await this._createTeamWithAttempts(groupId, graphClient);
if (!teamId) {
this._onError();
return;
}
if (!this.state.createChannel) {
this.setState({
creationState: CreationState.created
});
return;
}
this.setState({
spinnerText: strings.CreatingChannel
});
//
// Create channel
//
const channelId = await this._createChannel(teamId, graphClient);
if (!channelId) {
this._onError();
return;
}
if (!this.state.addTab) {
this.setState({
creationState: CreationState.created
});
return;
}
this.setState({
spinnerText: strings.InstallingApp
});
//
// install app
//
const isInstalled = await this._installApp(teamId, graphClient);
if (!isInstalled) {
this._onError();
return;
}
this.setState({
spinnerText: strings.CreatingTab
});
//
// add tab
//
const isTabCreated = await this._addTab(teamId, channelId, graphClient);
if (!isTabCreated) {
this._onError();
}
else {
this.setState({
creationState: CreationState.created
});
}
}
private _onError(message?: string): void {
this.setState({
creationState: CreationState.error
});
}
/**
* Installs the app to the team
* @param teamId team Id
* @param graphClient graph client
*/
private async _installApp(teamId: string, graphClient: MSGraphClient): Promise<boolean> {
try {
await graphClient.api(`teams/${teamId}/apps`).version('beta').post({
id: this.state.selectedAppId
});
}
catch (error) {
console.error(error);
return false;
}
return true;
}
/**
* Adds tab to the specified channel of the team
* @param teamId team id
* @param channelId channel id
* @param graphClient graph client
*/
private async _addTab(teamId: string, channelId: string, graphClient: MSGraphClient): Promise<boolean> {
try {
await graphClient.api(`teams/${teamId}/channels/${channelId}/tabs`).version('beta').post({
name: this.state.tabName,
teamsAppId: this.state.selectedAppId
});
}
catch (error) {
console.error(error);
return false;
}
return true;
}
/**
* Creates channel in the team
* @param teamId team id
* @param graphClient graph client
*/
private async _createChannel(teamId: string, graphClient: MSGraphClient): Promise<string> {
const {
channelName,
channelDescription
} = this.state;
try {
const response = await graphClient.api(`teams/${teamId}/channels`).version('beta').post({
displayName: channelName,
description: channelDescription
});
return response.id;
}
catch (error) {
console.error(error);
return '';
}
}
/**
* Creates O365 group
* @param graphClient graph client
*/
private async _createGroup(graphClient: MSGraphClient): Promise<string> {
const displayName = this.state.teamName;
const mailNickname = this._generateMailNickname(displayName);
let {
owners,
members
} = this.state;
const groupRequest = {
displayName: displayName,
description: this.state.teamDescription,
groupTypes: [
'Unified'
],
mailEnabled: true,
mailNickname: mailNickname,
securityEnabled: false
};
if (owners && owners.length) {
groupRequest['owners@data.bind'] = owners.map(owner => {
return `https://graph.microsoft.com/v1.0/users/${owner}`;
});
}
if (members && members.length) {
groupRequest['members@data.bind'] = members.map(member => {
return `https://graph.microsoft.com/v1.0/users/${member}`;
});
}
try {
const response = await graphClient.api('groups').version('beta').post(groupRequest);
return response.id;
}
catch (error) {
console.error(error);
return '';
}
}
/**
* Creates team. as mentioned in the documentation - we need to make multiple attempts if team creation request errored
* @param groupId group id
* @param graphClient graph client
*/
private async _createTeamWithAttempts(groupId: string, graphClient: MSGraphClient): Promise<string> {
let attemptsCount = 0;
let teamId: string = '';
//
// From the documentation: If the group was created less than 15 minutes ago, it's possible for the Create team call to fail with a 404 error code due to replication delays.
// The recommended pattern is to retry the Create team call three times, with a 10 second delay between calls.
//
do {
teamId = await this._createTeam(groupId, graphClient);
if (teamId) {
attemptsCount = 3;
}
else {
attemptsCount++;
}
} while (attemptsCount < 3);
return teamId;
}
/**
* Waits 10 seconds and tries to create a team
* @param groupId group id
* @param graphClient graph client
*/
private async _createTeam(groupId: string, graphClient: MSGraphClient): Promise<string> {
return new Promise<string>(resolve => {
setTimeout(() => {
graphClient.api(`groups/${groupId}/team`).version('beta').put({
memberSettings: {
allowCreateUpdateChannels: true
},
messagingSettings: {
allowUserEditMessages: true,
allowUserDeleteMessages: true
},
funSettings: {
allowGiphy: true,
giphyContentRating: "strict"
}
}).then(response => {
resolve(response.id);
}, () => {
resolve('');
});
}, 10000);
});
}
/**
* Generates mail nick name by display name of the group
* @param displayName group display name
*/
private _generateMailNickname(displayName: string): string {
return displayName.toLowerCase().replace(/\s/gmi, '-');
}
}

View File

@ -0,0 +1,29 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"DescriptionFieldLabel": "Description Field",
"TeamNameLabel": "Team name",
"TeamDescriptionLabel": "Team description",
"Owners": "Owners",
"Members": "Members",
"CreateChannel": "Create new channel in the Team",
"ChannelName": "Channel name",
"ChannelDescription": "Channel description",
"AddTab": "Add a new tab to the channel",
"TabName": "Tab name",
"App": "Select an app to be added as a tab",
"Welcome": "Welcom to Teams Creator Web Part!",
"Create": "Create",
"Clear": "Clear",
"CreatingGroup": "Creating O365 Group...",
"CreatingTeam": "Creating team...",
"CreatingChannel": "Creating channel...",
"InstallingApp": "Installing app...",
"CreatingTab": "Creating tab to host the app...",
"Error": "Something went wrong during the process. Please, refer browser console for more details.",
"Success": "All selected components have been successfully created.",
"StartOver": "Start over",
"OpenTeams": "Open Teams"
}
});

View File

@ -0,0 +1,32 @@
declare interface ITeamsCreatorWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
DescriptionFieldLabel: string;
TeamNameLabel: string;
TeamDescriptionLabel: string;
Owners: string;
Members: string;
CreateChannel: string;
ChannelName: string;
ChannelDescription: string;
AddTab: string;
TabName: string;
App: string;
Welcome: string;
Create: string;
Clear: string;
CreatingGroup: string;
CreatingTeam: string;
CreatingChannel: string;
InstallingApp: string;
CreatingTab: string;
Error: string;
Success: string;
StartOver: string;
OpenTeams: string;
}
declare module 'TeamsCreatorWebPartStrings' {
const strings: ITeamsCreatorWebPartStrings;
export = strings;
}

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,32 @@
{
"rulesDirectory": [
"tslint-microsoft-contrib"
],
"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
}
}