Merge pull request #1778 from nanddeepn/spfx-react-teams-meeting-app-questionnaire

This commit is contained in:
Hugo Bernier 2021-03-25 01:25:46 -04:00 committed by GitHub
commit 7d628c29b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 23176 additions and 0 deletions

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": {
"isCreatingSolution": true,
"environment": "spo",
"version": "1.12.0",
"libraryName": "spfx-ms-teams-questionnaire-meeting-app",
"libraryId": "78093b48-2c62-4536-8f93-95ec16b01f8b",
"packageManager": "npm",
"isDomainIsolated": false,
"componentType": "webpart"
}
}

View File

@ -0,0 +1,155 @@
# Questionnaire Teams Meeting App
## Summary
SPFx v1.12 support for Microsoft Teams meeting apps development. Questionnaire meeting app provides Pre-meeting app experience for MS Teams meeting attendees to ask the questions related to meeting before the meeting starts.
![WebPart Preview](./assets/web-part-preview.gif)
The Questionnaire meeting app displays the questions from attendees as pre-meeting app experience.
![Questionnaire Preview](./assets/questionnaire-preview.png)
### NPM Packages Used
Below NPM package(s) are used to develop this sample:
1. @pnp/sp (https://pnp.github.io/pnpjs/sp/)
2. moment (https://www.npmjs.com/package/moment)
### Project setup and important files
```txt
spfx-react-teams-meeting-app-questionnaire
├── teams <-- MS Teams manifest
│ └── manifest.json
└── src
└── models
├── IQuestionnaireItem.ts
└── services
├── SPOService.ts <-- Extensible Service
└── webparts
└── questionnaireMeetingApp
├── QuestionnaireMeetingAppWebPart.manifest.json <-- Configurable web part properties
├── QuestionnaireMeetingAppWebPart.ts
├── components
│ └── QuestionnaireMeetingApp
│ │ ├── QuestionnaireMeetingApp.tsx <-- Questionnaire Component
│ │ ├── QuestionnaireMeetingApp.module.scss
│ │ ├── IQuestionnaireMeetingAppProps.ts
│ │ ├── IQuestionnaireMeetingAppState.ts
│ └── Popup <-- New Question Creation Component
| │ ├── AskQuestion.tsx
| │ ├── IAskQuestionProps.ts
| │ ├── IAskQuestionState.ts
└── loc
├── en-us.js
└── mystrings.d.ts
```
## Compatibility
![SPFx 1.12](https://img.shields.io/badge/SPFx-1.12.0-green.svg)
![Node.js LTS 10.x | LTS 12.x](https://img.shields.io/badge/Node.js-LTS%2010%20%7C%20LTS%20.12.x-green.svg)
![SharePoint Online](https://img.shields.io/badge/SharePoint-Online-yellow.svg)
![Teams Yes: Designed for Microsoft Teams](https://img.shields.io/badge/Teams-Yes-green.svg "Designed for 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")
## Applies to
- [Microsoft Teams](https://aka.ms/microsoftteams)
- [SharePoint Framework](https://aka.ms/spfx)
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
## Prerequisites
- Administrative access to MS Teams to deploy the package
### Web Part Properties
The properties should be pre-configured inside `QuestionnaireMeetingAppWebPart.manifest.json` as when the web part is added as MS Teams meeting experience, we do not get any settings to configure.
Property|Type|Required|Default value|Comments
--------|----|--------|-------------|--------
siteUrl|Text|Yes|/|Provide the relative URL of the site where below list exists.
listName|Text|Yes|Teams Meeting Questionnaire|Title of the list storing meeting questionnaires.
### SharePoint Asset
A SharePoint list (named `Teams Meeting Questionnaire`) should be manually created to store the meeting questionnaires. The schema of the list is as below.
Display Name|Internal Name|Type|Required|Comments
------------|-------------|----|--------|--------
Title|Title|Single line of text|Y|OOB Title column
Description|Description|Multiple lines of text|N|
MeetingID|MeetingID|Single line of text|N|
## Minimal Path to Awesome
### SharePoint deployment
- Clone this repo
- Navigate to the folder with current sample
- Restore dependencies: `$ npm i`
- Bundle the solution: `$ gulp bundle --ship`
- Package the solution: `$ gulp package-solution --ship`
- Locate the solution at `./sharepoint/solution/spfx-ms-teams-questionnaire-meeting-app.sppkg` and upload it to SharePoint tenant app catalog
![Deploy SPFx solution](./assets/deploy-spfx-solution.png)
- Select `Make this solution available to all sites in the organization`.
- Click `Deploy`.
### MS Teams deployment
- Navigate to `teams` folder and zip the content (2 png files and manifest.json).
- Open MS Teams.
- Click `Apps`.
- Click `Upload a custom app` > `Upload for <tenant>`.
![Deploy to MS Teams](./assets/deploy-to-ms-teams.png)
## Solution
Solution|Author(s)
--------|---------
spfx-react-teams-meeting-app-questionnaire|[Nanddeep Nachan](https://www.linkedin.com/in/nanddeepnachan/) (SharePoint Consultant, [@NanddeepNachan](https://twitter.com/NanddeepNachan))
spfx-react-teams-meeting-app-questionnaire|[Ravi Kulkarni](https://www.linkedin.com/in/ravi-kulkarni-a5381723/) (SharePoint Consultant, [@RaviKul16a87](https://twitter.com/RaviKul16a87))
spfx-react-teams-meeting-app-questionnaire|[Smita Nachan](https://www.linkedin.com/in/smitanachan/) (SharePoint Consultant, [@SmitaNachan](https://twitter.com/SmitaNachan))
## Version history
Version|Date|Comments
-------|----|--------
1.0.0|March 22, 2021|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
This project contains sample client-side web part built on the SharePoint Framework illustrating possibilities to surface SPFx web part as Microsoft Teams meeting app.
This sample illustrates the following concepts on top of the SharePoint Framework:
- Surface SPFx web part as Microsoft Teams meeting app
- Using PnP/PnPjs
- Creating extensible services
- Using Office UI Fabric controls for building SharePoint Framework client-side web parts
## References
- [Getting started with SharePoint Framework](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
- [Building for Microsoft teams](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview)
- [Apps in Teams meetings](https://docs.microsoft.com/en-us/microsoftteams/platform/apps-in-teams-meetings/teams-apps-in-meetings)
- [Use Microsoft Graph in your solution](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis)
- [Publish SharePoint Framework applications to the Marketplace](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview)
- [Microsoft 365 Patterns and Practices](https://aka.ms/m365pnp) - Guidance, tooling, samples and open-source controls for your Microsoft 365 development
- [PnPjs Configuration](https://pnp.github.io/pnpjs/concepts/configuration/)
- [Support Microsoft Teams Themes in SharePoint Framework Solutions](https://blog.aterentiev.com/support-microsoft-teams-themes-in) by Alex Terentiev, [@alexaterentiev](https://twitter.com/alexaterentiev)
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-teams-meeting-app-questionnaire" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 132 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

View File

@ -0,0 +1,83 @@
[
{
"name": "pnp-sp-dev-spfx-web-parts-react-teams-meeting-app-questionnaire",
"source": "pnp",
"title": "Questionnaire Teams Meeting App",
"shortDescription": "Questionnaire meeting app provides Pre-meeting app experience for MS Teams meeting attendees to ask the questions related to meeting before the meeting starts.",
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/master/samples/react-teams-meeting-app-questionnaire",
"longDescription": [
"SPFx v1.12 support for Microsoft Teams meeting apps development. Questionnaire meeting app provides Pre-meeting app experience for MS Teams meeting attendees to ask the questions related to meeting before the meeting starts.",
"The Questionnaire meeting app displays the questions from attendees as pre-meeting app experience."
],
"created": "2021-03-22",
"modified": "2021-03-22",
"products": [
"SharePoint",
"Office",
"Teams"
],
"metadata": [
{
"key": "CLIENT-SIDE-DEV",
"value": "React"
},
{
"key": "SPFX-VERSION",
"value": "1.12.0"
},
{
"key": "SPFX-TEAMSMEETINGAPP",
"value": "true"
}
],
"thumbnails": [
{
"type": "image",
"order": 100,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/master/samples/react-teams-meeting-app-questionnaire/assets/web-part-preview.gif",
"alt": "Web Part Preview"
},
{
"type": "image",
"order": 101,
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/master/samples/react-teams-meeting-app-questionnaire/assets/questionnaire-preview.png",
"alt": "Questionnaire Preview"
}
],
"authors": [
{
"gitHubAccount": "nanddeepn",
"company": "",
"pictureUrl": "https://github.com/nanddeepn.png",
"name": "Nanddeep Nachan",
"twitter": "NanddeepNachan"
},
{
"gitHubAccount": "ravi16a87",
"company": "Cognizant",
"pictureUrl": "https://github.com/ravi16a87.png",
"name": "Ravi Kulkarni",
"twitter":"RaviKul16a87"
},
{
"gitHubAccount": "SmitaNachan",
"company": "",
"pictureUrl": "https://github.com/SmitaNachan.png",
"name": "Smita Nachan",
"twitter": "SmitaNachan"
}
],
"references": [
{
"name": "Build your first SharePoint client-side web part",
"description": "Client-side web parts are client-side components that run in the context of a SharePoint page. Client-side web parts can be deployed to SharePoint environments that support the SharePoint Framework. You can also use modern JavaScript web frameworks, tools, and libraries to build them.",
"url": "https://docs.microsoft.com/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
},
{
"name": "Apps in Teams meetings",
"description": "Meetings are key to productivity in Teams. They enable collaboration, partnership, informed communication, and shared feedback in an inclusive and active forum. As a developer, you can create configurable tab, bot, and message extension applications to enhance and enrich a Teams meeting experience. Meeting users can access apps, via the tab gallery, to enable relevant scenarios such as pre-staging a Kanban board, launching an in-meeting actionable dialog, or creating a post-meeting poll. Your meeting app can deliver a user experience for each stage of the meeting lifecycle based upon attendee status.",
"url": "https://docs.microsoft.com/microsoftteams/platform/apps-in-teams-meetings/teams-apps-in-meetings?WT.mc_id=M365-MVP-5003693"
}
]
}
]

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 MiB

View File

@ -0,0 +1,18 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
"version": "2.0",
"bundles": {
"questionnaire-meeting-app-web-part": {
"components": [
{
"entrypoint": "./lib/webparts/questionnaireMeetingApp/QuestionnaireMeetingAppWebPart.js",
"manifest": "./src/webparts/questionnaireMeetingApp/QuestionnaireMeetingAppWebPart.manifest.json"
}
]
}
},
"externals": {},
"localizedResources": {
"QuestionnaireMeetingAppWebPartStrings": "lib/webparts/questionnaireMeetingApp/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": "spfx-ms-teams-questionnaire-meeting-app",
"accessKey": "<!-- ACCESS KEY -->"
}

View File

@ -0,0 +1,21 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
"solution": {
"name": "spfx-ms-teams-questionnaire-meeting-app-client-side-solution",
"id": "78093b48-2c62-4536-8f93-95ec16b01f8b",
"version": "1.0.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"isDomainIsolated": false,
"developer": {
"name": "",
"websiteUrl": "",
"privacyUrl": "",
"termsOfUseUrl": "",
"mpnId": ""
}
},
"paths": {
"zippedPackage": "solution/spfx-ms-teams-questionnaire-meeting-app.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,16 @@
'use strict';
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.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(require('gulp'));

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,35 @@
{
"name": "spfx-ms-teams-questionnaire-meeting-app",
"version": "0.0.1",
"private": true,
"main": "lib/index.js",
"scripts": {
"build": "gulp bundle",
"clean": "gulp clean",
"test": "gulp test"
},
"dependencies": {
"@microsoft/sp-core-library": "1.12.0",
"@microsoft/sp-lodash-subset": "1.12.0",
"@microsoft/sp-office-ui-fabric-core": "1.12.0",
"@microsoft/sp-property-pane": "1.12.0",
"@microsoft/sp-webpart-base": "1.12.0",
"@pnp/sp": "2.3.1-beta0",
"moment": "^2.29.1",
"office-ui-fabric-react": "7.156.0",
"react": "16.9.0",
"react-dom": "16.9.0"
},
"devDependencies": {
"@types/react": "16.9.36",
"@types/react-dom": "16.9.8",
"@microsoft/sp-build-web": "1.12.0",
"@microsoft/sp-tslint-rules": "1.12.0",
"@microsoft/sp-module-interfaces": "1.12.0",
"@microsoft/sp-webpart-workbench": "1.12.0",
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
"gulp": "~4.0.2",
"ajv": "~5.2.2",
"@types/webpack-env": "1.13.1"
}
}

View File

@ -0,0 +1,79 @@
{
"id": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985",
"alias": "QuestionnaireMeetingAppWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart"
],
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": {
"default": "Other"
},
"title": {
"default": "QuestionnaireMeetingApp"
},
"description": {
"default": "Provides pre-meeting experience with questionnaire"
},
"officeFabricIconFontName": "Page",
"properties": {
"siteUrl": "/",
"listName": "Teams Meeting Questionnaire"
}
}
],
"loaderConfig": {
"internalModuleBaseUrls": [
"https://localhost:4321/dist/"
],
"entryModuleId": "questionnaire-meeting-app-web-part",
"scriptResources": {
"questionnaire-meeting-app-web-part": {
"type": "path",
"path": "questionnaire-meeting-app-web-part_84192ae6459ea857d480.js"
},
"tslib": {
"type": "component",
"id": "01c4df03-e775-48cb-aa14-171ee5199a15",
"version": "1.10.0"
},
"@microsoft/sp-property-pane": {
"type": "component",
"id": "f9e737b7-f0df-4597-ba8c-3060f82380db",
"version": "1.12.0"
},
"@microsoft/sp-core-library": {
"type": "component",
"id": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"version": "1.12.0"
},
"@microsoft/sp-webpart-base": {
"type": "component",
"id": "974a7777-0990-4136-8fa6-95d80114c2e0",
"version": "1.12.0"
},
"react": {
"type": "component",
"id": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"version": "16.9.0"
},
"react-dom": {
"type": "component",
"id": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"version": "16.9.0"
},
"QuestionnaireMeetingAppWebPartStrings": {
"type": "localizedPath",
"paths": {
"en-US": "QuestionnaireMeetingAppWebPartStrings_en-us_733fec4d177b77e953cdd1256b08bf0a.js"
},
"defaultPath": "QuestionnaireMeetingAppWebPartStrings_en-us_733fec4d177b77e953cdd1256b08bf0a.js"
}
}
}
}

View File

@ -0,0 +1,12 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"SiteURLFieldLabel": "Relative Site URL",
"ListNameFieldLabel": "Relative Site URL",
"AddQuestion": "New Question",
"Posted": "posted",
"Title": "Title",
"Description": "Description"
}
});

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,104 @@
{
"bundles": {
"questionnaire-meeting-app-web-part": {
"dependencies": [
{
"componentId": "01c4df03-e775-48cb-aa14-171ee5199a15",
"componentName": "tslib",
"componentVersion": "1.10.0",
"isDirectDependency": true
},
{
"componentId": "f9e737b7-f0df-4597-ba8c-3060f82380db",
"componentName": "@microsoft/sp-property-pane",
"componentVersion": "1.12.0",
"isDirectDependency": true
},
{
"componentId": "1c6c9123-7aac-41f3-a376-3caea41ed83f",
"componentName": "@microsoft/sp-loader",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "8217e442-8ed3-41fd-957d-b112e841286a",
"componentName": "@ms/sp-telemetry",
"componentVersion": "0.19.2",
"isDirectDependency": false
},
{
"componentId": "e40f8203-b39d-425a-a957-714852e33b79",
"componentName": "@microsoft/sp-dynamic-data",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "73e1dc6c-8441-42cc-ad47-4bd3659f8a3a",
"componentName": "@microsoft/sp-lodash-subset",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"componentName": "@microsoft/sp-core-library",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "2e09fb9b-13bb-48f2-859f-97d6fff71176",
"componentName": "@ms/odsp-core-bundle",
"componentVersion": "1.1.13",
"isDirectDependency": false
},
{
"componentId": "78359e4b-07c2-43c6-8d0b-d060b4d577e8",
"componentName": "@microsoft/sp-diagnostics",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "1c4541f7-5c31-41aa-9fa8-fbc9dc14c0a8",
"componentName": "@microsoft/sp-page-context",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "229b8d08-79f3-438b-8c21-4613fc877abd",
"componentName": "@microsoft/load-themed-styles",
"componentVersion": "0.1.2",
"isDirectDependency": false
},
{
"componentId": "c07208f0-ea3b-4c1a-9965-ac1b825211a6",
"componentName": "@microsoft/sp-http",
"componentVersion": "1.12.0",
"isDirectDependency": false
},
{
"componentId": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"componentName": "react",
"componentVersion": "16.9.0",
"isDirectDependency": false
},
{
"componentId": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"componentName": "react-dom",
"componentVersion": "16.9.0",
"isDirectDependency": false
},
{
"componentId": "974a7777-0990-4136-8fa6-95d80114c2e0",
"componentName": "@microsoft/sp-webpart-base",
"componentVersion": "1.12.0",
"isDirectDependency": true
},
{
"componentId": "467dc675-7cc5-4709-8aac-78e3b71bd2f6",
"componentName": "@microsoft/sp-component-base",
"componentVersion": "1.12.0",
"isDirectDependency": false
}
]
}
}
}

View File

@ -0,0 +1,79 @@
{
"id": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985",
"alias": "QuestionnaireMeetingAppWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart"
],
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": {
"default": "Other"
},
"title": {
"default": "QuestionnaireMeetingApp"
},
"description": {
"default": "Provides pre-meeting experience with questionnaire"
},
"officeFabricIconFontName": "Page",
"properties": {
"siteUrl": "/",
"listName": "Teams Meeting Questionnaire"
}
}
],
"loaderConfig": {
"internalModuleBaseUrls": [
"<!-- PATH TO CDN -->"
],
"entryModuleId": "questionnaire-meeting-app-web-part",
"scriptResources": {
"questionnaire-meeting-app-web-part": {
"type": "path",
"path": "questionnaire-meeting-app-web-part_84192ae6459ea857d480.js"
},
"tslib": {
"type": "component",
"id": "01c4df03-e775-48cb-aa14-171ee5199a15",
"version": "1.10.0"
},
"@microsoft/sp-property-pane": {
"type": "component",
"id": "f9e737b7-f0df-4597-ba8c-3060f82380db",
"version": "1.12.0"
},
"@microsoft/sp-core-library": {
"type": "component",
"id": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"version": "1.12.0"
},
"@microsoft/sp-webpart-base": {
"type": "component",
"id": "974a7777-0990-4136-8fa6-95d80114c2e0",
"version": "1.12.0"
},
"react": {
"type": "component",
"id": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"version": "16.9.0"
},
"react-dom": {
"type": "component",
"id": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"version": "16.9.0"
},
"QuestionnaireMeetingAppWebPartStrings": {
"type": "localizedPath",
"paths": {
"en-US": "QuestionnaireMeetingAppWebPartStrings_en-us_733fec4d177b77e953cdd1256b08bf0a.js"
},
"defaultPath": "QuestionnaireMeetingAppWebPartStrings_en-us_733fec4d177b77e953cdd1256b08bf0a.js"
}
}
}
}

View File

@ -0,0 +1,79 @@
{
"id": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985",
"alias": "QuestionnaireMeetingAppWebPart",
"componentType": "WebPart",
"version": "0.0.1",
"manifestVersion": 2,
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart"
],
"preconfiguredEntries": [
{
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
"group": {
"default": "Other"
},
"title": {
"default": "QuestionnaireMeetingApp"
},
"description": {
"default": "Provides pre-meeting experience with questionnaire"
},
"officeFabricIconFontName": "Page",
"properties": {
"siteUrl": "/",
"listName": "Teams Meeting Questionnaire"
}
}
],
"loaderConfig": {
"internalModuleBaseUrls": [
"<!-- PATH TO CDN -->"
],
"entryModuleId": "questionnaire-meeting-app-web-part",
"scriptResources": {
"questionnaire-meeting-app-web-part": {
"type": "path",
"path": "questionnaire-meeting-app-web-part_84192ae6459ea857d480.js"
},
"tslib": {
"type": "component",
"id": "01c4df03-e775-48cb-aa14-171ee5199a15",
"version": "1.10.0"
},
"@microsoft/sp-property-pane": {
"type": "component",
"id": "f9e737b7-f0df-4597-ba8c-3060f82380db",
"version": "1.12.0"
},
"@microsoft/sp-core-library": {
"type": "component",
"id": "7263c7d0-1d6a-45ec-8d85-d4d1d234171b",
"version": "1.12.0"
},
"@microsoft/sp-webpart-base": {
"type": "component",
"id": "974a7777-0990-4136-8fa6-95d80114c2e0",
"version": "1.12.0"
},
"react": {
"type": "component",
"id": "0d910c1c-13b9-4e1c-9aa4-b008c5e42d7d",
"version": "16.9.0"
},
"react-dom": {
"type": "component",
"id": "aa0a46ec-1505-43cd-a44a-93f3a5aa460a",
"version": "16.9.0"
},
"QuestionnaireMeetingAppWebPartStrings": {
"type": "localizedPath",
"paths": {
"en-US": "QuestionnaireMeetingAppWebPartStrings_en-us_733fec4d177b77e953cdd1256b08bf0a.js"
},
"defaultPath": "QuestionnaireMeetingAppWebPartStrings_en-us_733fec4d177b77e953cdd1256b08bf0a.js"
}
}
}
}

View File

@ -0,0 +1,59 @@
@import "./colors.module";
[data-theme="contrast"] {
:global {
.ms-Fabric {
color: $contrast-primaryText;
}
.ms-Button-icon {
color: $contrast-primaryText;
}
.ms-Overlay {
background-color: $contrast-overlay;
}
.ms-Panel-main {
background-color: $contrast-surfaceBackground;
border-left-color: $contrast-panelBorder;
border-right-color: $contrast-panelBorder;
.ms-Panel-headerText {
color: $contrast-primaryText;
}
}
.spPropertyPaneContainer {
background-color: $contrast-white;
[class^="propertyPane_"] {
background-color: $contrast-white;
border-left-color: $contrast-panelBorder;
[class^="propertyPanePageTitle_"],
[class^="propertyPanePageDescription_"],
[class^="propertyPaneGroupHeaderNoAccordion_"] {
color: $contrast-primaryText;
}
.ms-Button--icon {
&:hover {
background-color: transparent;
}
}
}
}
.ms-Label {
color: $contrast-primaryText;
}
.ms-TextField {
.ms-TextField-fieldGroup {
background-color: $contrast-inputBackground;
color: $contrast-primaryText;
border-color: $contrast-inputBorder;
.ms-TextField-field {
color: $contrast-primaryText;
}
&:hover {
border-color: $contrast-inputBorderHovered;
}
}
}
}
}

View File

@ -0,0 +1,59 @@
@import "./colors.module";
[data-theme="dark"] {
:global {
.ms-Fabric {
color: $dark-primaryText;
}
.ms-Button-icon {
color: $dark-primaryText;
}
.ms-Overlay {
background-color: $dark-overlay;
}
.ms-Panel-main {
background-color: $dark-surfaceBackground;
border-left-color: $dark-panelBorder;
border-right-color: $dark-panelBorder;
.ms-Panel-headerText {
color: $dark-primaryText;
}
}
.spPropertyPaneContainer {
background-color: $dark-white;
[class^="propertyPane_"] {
background-color: $dark-white;
border-left-color: $dark-panelBorder;
[class^="propertyPanePageTitle_"],
[class^="propertyPanePageDescription_"],
[class^="propertyPaneGroupHeaderNoAccordion_"] {
color: $dark-primaryText;
}
.ms-Button--icon {
&:hover {
background-color: transparent;
}
}
}
}
.ms-Label {
color: $dark-primaryText;
}
.ms-TextField {
.ms-TextField-fieldGroup {
background-color: $dark-inputBackground;
color: $dark-primaryText;
border-color: $dark-inputBorder;
.ms-TextField-field {
color: $dark-primaryText;
}
&:hover {
border-color: $dark-inputBorderHovered;
}
}
}
}
}

View File

@ -0,0 +1,61 @@
@import "./colors.module";
[data-theme="default"] {
:global {
.ms-Fabric {
color: $default-primaryText;
}
.ms-Button-icon {
color: $default-primaryText;
}
.ms-Overlay {
background-color: $default-overlay;
}
.ms-Panel-main {
background-color: $default-surfaceBackground;
border-left-color: $default-panelBorder;
border-right-color: $default-panelBorder;
.ms-Panel-headerText {
color: $default-primaryText;
}
}
// Property Pane
.spPropertyPaneContainer {
background-color: $default-white;
[class^="propertyPane_"] {
background-color: $default-white;
border-left-color: $default-panelBorder;
[class^="propertyPanePageTitle_"],
[class^="propertyPanePageDescription_"],
[class^="propertyPaneGroupHeaderNoAccordion_"] {
color: $default-primaryText;
}
.ms-Button--icon {
&:hover {
background-color: transparent;
}
}
}
}
// Text Field
.ms-Label {
color: $default-primaryText;
}
.ms-TextField {
.ms-TextField-fieldGroup {
background-color: $default-inputBackground;
color: $default-primaryText;
border-color: $default-inputBorder;
.ms-TextField-field {
color: $default-primaryText;
}
&:hover {
border-color: $default-inputBorderHovered;
}
}
}
}
}

View File

@ -0,0 +1,61 @@
//SharePoint
$questionnaireMeetingApp-background: "[theme:white, default:#fff]";
$questionnaireMeetingApp-color: "[theme:primaryText, default:#333]";
$questionnaireMeetingAppButton-background: "[theme:themePrimary, default:#0078d4]";
$questionnaireMeetingAppButton-color: "[theme:white, default:#fff]";
$overlay: "[theme:whiteTranslucent40, default:rgba(255, 255,255, 0.4)]";
$surfaceBackground: "[theme:white, default:#fff]";
$primaryText: "[theme:primaryText, default:#333]";
$panelBorder: "[theme: neutralLight, default: #eaeaea]";
$white: "[theme:white, default: #fff]"; // property pane background
$inputBackground: "[theme:inputBackground, default:#fff]"; //input background
$inputBorder: "[theme:inputBorder, default:#a6a6a6]"; // input border
$inputBorderHovered: "[theme:inputBorderHovered, default:#333333]"; // input border hovered
// default theme
$default-questionnaireMeetingApp-background: #f3f2f1;
$default-questionnaireMeetingApp-color: #252423;
$default-questionnaireMeetingAppButton-background: #6264a7;
$default-questionnaireMeetingAppButton-color: #f3f2f1;
$default-overlay: rgba(255, 255, 255, 0.4);
$default-surfaceBackground: #f3f2f1;
$default-primaryText: #252423;
$default-panelBorder: #dedddc;
$default-white: #f3f2f1;
$default-inputBackground: #fff;
$default-inputBorder: #b5b4b2;
$default-inputBorderHovered: #252423;
// dark theme
$dark-questionnaireMeetingApp-background: #2d2c2c;
$dark-questionnaireMeetingApp-color: #ffffff;
$dark-questionnaireMeetingAppButton-background: #6264a7;
$dark-questionnaireMeetingAppButton-color: #2d2c2c;
$dark-overlay: rgba(37, 36, 35, 0.75);
$dark-surfaceBackground: #2d2c2c;
$dark-primaryText: #ffffff;
$dark-panelBorder: #4c4b4b;
$dark-white: #2d2c2c;
$dark-inputBackground: #000;
$dark-inputBorder: #c8c8c8;
$dark-inputBorderHovered: #ffffff;
// contrast theme
$contrast-questionnaireMeetingApp-background: #000000;
$contrast-questionnaireMeetingApp-color: #ffffff;
$contrast-questionnaireMeetingAppButton-background: #6264a7;
$contrast-questionnaireMeetingAppButton-color: #000000;
$contrast-overlay: rgba(37, 36, 35, 0.75);
$contrast-surfaceBackground: #000000;
$contrast-primaryText: #ffffff;
$contrast-panelBorder: #4c4b4b;
$contrast-white: #000000;
$contrast-inputBackground: #000;
$contrast-inputBorder: #c8c8c8;
$contrast-inputBorderHovered: #ffffff;

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,9 @@
// Represents attributes of Questionnaire
export interface IQuestionnaireItem {
ID?: number;
MeetingID: string;
Title: string;
Description: string;
Author?: any;
Modified?: Date;
}

View File

@ -0,0 +1,43 @@
import { IQuestionnaireItem } from "../models/IQuestionnaireItem";
import { sp } from '@pnp/sp/presets/all';
export default class SPOService {
public async getQuestionnaire(listTitle: string, meetingId: string): Promise<IQuestionnaireItem[]> {
let meetingQuestionnaire: IQuestionnaireItem[] = [];
try {
// Get Client POC Master
meetingQuestionnaire = await sp.web.lists.getByTitle(listTitle)
.items
.select("ID,MeetingID,Title,Description,Author/Title,Author/EMail,Modified")
.expand("Author")
.filter(`MeetingID eq '${meetingId}'`)
.orderBy("Modified", false)
.get<IQuestionnaireItem[]>();
}
catch (error) {
console.log(error);
return Promise.reject(error);
}
return meetingQuestionnaire;
}
public async addQuestion(listTitle: string, item: IQuestionnaireItem): Promise<boolean> {
try {
// Get Client POC Master
return sp.web.lists.getByTitle(listTitle)
.items
.add({
Title: item.Title,
Description: item.Description,
MeetingID: item.MeetingID
})
.then((value) => {
return Promise.resolve(true);
});
}
catch (error) {
return Promise.reject(error);
}
}
}

View File

@ -0,0 +1,28 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
"id": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985",
"alias": "QuestionnaireMeetingAppWebPart",
"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": "QuestionnaireMeetingApp" },
"description": { "default": "Provides pre-meeting experience with questionnaire" },
"officeFabricIconFontName": "Page",
"properties": {
"siteUrl": "/",
"listName": "Teams Meeting Questionnaire"
}
}]
}

View File

@ -0,0 +1,87 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { IPropertyPaneConfiguration, PropertyPaneTextField } from '@microsoft/sp-property-pane';
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
import * as strings from 'QuestionnaireMeetingAppWebPartStrings';
import QuestionnaireMeetingApp from './components/QuestionnaireMeetingApp';
import { IQuestionnaireMeetingAppProps } from './components/IQuestionnaireMeetingAppProps';
import { sp } from '@pnp/sp/presets/all';
export interface IQuestionnaireMeetingAppWebPartProps {
siteUrl: string;
listName: string;
}
export default class QuestionnaireMeetingAppWebPart extends BaseClientSideWebPart<IQuestionnaireMeetingAppWebPartProps> {
public async onInit(): Promise<void> {
return super.onInit().then(_ => {
if (this.context.sdks.microsoftTeams) {
// checking that we're in Teams
const context = this.context.sdks.microsoftTeams!.context;
this._applyTheme(context.theme || 'default');
this.context.sdks.microsoftTeams.teamsJs.registerOnThemeChangeHandler(this._applyTheme);
// Setup context to PnPjs
sp.setup({
spfxContext: this.context,
sp: {
baseUrl: `https://${this.context.sdks.microsoftTeams.context.teamSiteDomain}${this.properties.siteUrl}`
}
});
}
});
}
private _applyTheme = (theme: string): void => {
this.context.domElement.setAttribute('data-theme', theme);
document.body.setAttribute('data-theme', theme);
}
public render(): void {
const element: React.ReactElement<IQuestionnaireMeetingAppProps> = React.createElement(
QuestionnaireMeetingApp,
{
siteUrl: this.properties.siteUrl,
listName: this.properties.listName,
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('siteUrl', {
label: strings.SiteURLFieldLabel
}),
PropertyPaneTextField('listName', {
label: strings.ListNameFieldLabel
})
]
}
]
}
]
};
}
}

View File

@ -0,0 +1,7 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IQuestionnaireMeetingAppProps {
siteUrl: string;
listName: string;
context: WebPartContext;
}

View File

@ -0,0 +1,7 @@
import { IQuestionnaireItem } from "../../../models/IQuestionnaireItem";
export interface IQuestionnaireMeetingAppState {
infoLoaded: boolean;
meetingQuestionnaire: IQuestionnaireItem[];
showPopup:boolean;
}

View File

@ -0,0 +1,116 @@
@import "~office-ui-fabric-react/dist/sass/References.scss";
@import "../../../../common/Global.dark.module.scss";
@import "../../../../common/Global.default.module.scss";
@import "../../../../common/Global.contrast.module.scss";
@import "../../../../common/colors.module";
.askQuestion {
.container {
margin: 0px auto;
}
.labelRequired {
color: red !important;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $default-questionnaireMeetingAppButton-background;
border-color: $default-questionnaireMeetingAppButton-background;
color: $default-questionnaireMeetingAppButton-color;
// 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;
}
}
:global(.ms-Dialog-main) {
// override built-in max-width so dialog can grow wider
max-width: 760px !important;
// 100% width on medium and below
@media (max-width: $ms-screen-max-md) {
& {
min-width: 100%;
}
}
// 80% width on medium to xx-large
@media (min-width: $ms-screen-min-md) and (max-width: $ms-screen-max-xl) {
& {
min-width: 80%;
}
}
// 800px width on x-large and above
@media (min-width: $ms-screen-min-xl) {
& {
min-width: 760px;
}
}
}
}
[data-theme="default"] {
.askQuestion {
background: $default-questionnaireMeetingApp-background;
color: $default-questionnaireMeetingApp-color;
.button {
background: $default-questionnaireMeetingAppButton-background;
background-color: $default-questionnaireMeetingAppButton-background;
color: $default-questionnaireMeetingAppButton-color;
}
}
}
[data-theme="dark"] {
.askQuestion {
background: $dark-questionnaireMeetingApp-background;
color: $dark-questionnaireMeetingApp-color;
.button {
background: $dark-questionnaireMeetingAppButton-background;
background-color: $dark-questionnaireMeetingAppButton-background;
color: $dark-questionnaireMeetingAppButton-color;
}
}
}
[data-theme="contrast"] {
.askQuestion {
background: $contrast-questionnaireMeetingApp-background;
color: $contrast-questionnaireMeetingApp-color;
.button {
background: $contrast-questionnaireMeetingAppButton-background;
background-color: $contrast-questionnaireMeetingAppButton-background;
color: $contrast-questionnaireMeetingAppButton-color;
}
}
}

View File

@ -0,0 +1,105 @@
import * as React from 'react';
import * as strings from 'QuestionnaireMeetingAppWebPartStrings';
import styles from './AskQuestion.module.scss';
import { IAskQuestionProps } from './IAskQuestionProps';
import { IAskQuestionState } from './IAskQuestionState';
import { TextField, Dialog, DialogType, DefaultButton, PrimaryButton, DialogFooter } from 'office-ui-fabric-react';
import SPOService from '../../../../services/SPOService';
import { IQuestionnaireItem } from '../../../../models/IQuestionnaireItem';
export class AskQuestion extends React.Component<IAskQuestionProps, IAskQuestionState> {
private SPOService: SPOService = null;
public constructor(props) {
super(props);
this.state = {
questionTitle: "",
questionDescription: "",
isloading: false,
isSaveClicked: false,
isQuestionTitleEmpty: true
};
this.onSave = this.onSave.bind(this);
this.SPOService = new SPOService();
this.hidePanel = this.hidePanel.bind(this);
}
private hidePanel() {
this.props.onDissmissPanel(true);
}
private async onSave() {
this.setState({ isSaveClicked: true });
if (!this.state.isQuestionTitleEmpty) {
let item: IQuestionnaireItem = {
Title: this.state.questionTitle,
Description: this.state.questionDescription,
MeetingID: this.props.context.sdks.microsoftTeams.context.meetingId
};
this.SPOService.addQuestion(this.props.listName, item)
.then((response: any) => {
this.props.onDissmissPanel(true);
});
}
}
public render(): React.ReactElement<IAskQuestionProps> {
return (
<div className={styles.askQuestion}>
<Dialog
isOpen={this.props.showPopup}
dialogContentProps={{
type: DialogType.normal,
title: strings.AddQuestion,
showCloseButton: true
}}
modalProps={{ containerClassName: styles.askQuestion }}
onDismiss={this.hidePanel}
hidden={false}>
<div>
{
!this.state.isloading &&
<div>
<div>
<TextField
label={strings.Title}
required
deferredValidationTime={500}
onChange={(ev, title) => {
this.setState({ questionTitle: title, isQuestionTitleEmpty: false, isSaveClicked: false });
}}
/>
{this.state.isSaveClicked &&
this.state.questionTitle.trim().length == 0 && (
<div className={styles.labelRequired}>
Client Note Title is required
</div>
)}
</div>
<div>
<TextField
label={strings.Description}
required
multiline
deferredValidationTime={500}
onChange={(ev, addedBy) => {
this.setState({ questionDescription: addedBy, isSaveClicked: false });
}}
/>
</div>
</div>
}
</div>
<DialogFooter>
<PrimaryButton className={styles.button} onClick={this.onSave} text="Save" />
<DefaultButton onClick={this.hidePanel} text="Cancel" />
</DialogFooter>
</Dialog>
</div>
);
}
}

View File

@ -0,0 +1,8 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IAskQuestionProps {
context: WebPartContext;
showPopup: boolean;
onDissmissPanel: (refresh: boolean) => void;
listName: string;
}

View File

@ -0,0 +1,8 @@
import { IDropdownOption } from 'office-ui-fabric-react/';
export interface IAskQuestionState {
questionTitle:string;
questionDescription:string;
isloading:boolean;
isSaveClicked:boolean;
isQuestionTitleEmpty:boolean;
}

View File

@ -0,0 +1,107 @@
@import "~office-ui-fabric-react/dist/sass/References.scss";
@import '../../../common/Global.dark.module.scss';
@import '../../../common/Global.default.module.scss';
@import '../../../common/Global.contrast.module.scss';
@import "../../../common/colors.module";
.questionnaireMeetingApp {
.container {
margin: 0px auto;
}
.row {
@include ms-Grid-row;
padding: 20px;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@include ms-xl8;
@include ms-xlPush2;
@include ms-lgPush1;
}
.title {
@include ms-font-xl;
}
.subTitle {
@include ms-font-l;
}
.description {
@include ms-font-l;
}
.button {
// Our button
text-decoration: none;
height: 32px;
// Primary Button
min-width: 80px;
background-color: $default-questionnaireMeetingApp-background;
border-color: $default-questionnaireMeetingApp-background;
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;
}
}
}
[data-theme='default'] {
.questionnaireMeetingApp {
background: $default-questionnaireMeetingApp-background;
color: $default-questionnaireMeetingApp-color;
.button {
background: $default-questionnaireMeetingAppButton-background;
color: $default-questionnaireMeetingAppButton-color;
}
}
}
[data-theme='dark'] {
.questionnaireMeetingApp {
background: $dark-questionnaireMeetingApp-background;
color: $dark-questionnaireMeetingApp-color;
.button {
background: $dark-questionnaireMeetingAppButton-background;
color: $dark-questionnaireMeetingAppButton-color;
}
}
}
[data-theme='contrast'] {
.questionnaireMeetingApp {
background: $contrast-questionnaireMeetingApp-background;
color: $contrast-questionnaireMeetingApp-color;
.button {
background: $contrast-questionnaireMeetingAppButton-background;
color: $contrast-questionnaireMeetingAppButton-color;
}
}
}

View File

@ -0,0 +1,115 @@
import * as React from 'react';
import * as strings from 'QuestionnaireMeetingAppWebPartStrings';
import styles from './QuestionnaireMeetingApp.module.scss';
import { IQuestionnaireMeetingAppProps } from './IQuestionnaireMeetingAppProps';
import { IQuestionnaireMeetingAppState } from './IQuestionnaireMeetingAppState';
import { IQuestionnaireItem } from "../../../models/IQuestionnaireItem";
import SPOService from '../../../services/SPOService';
import { PrimaryButton } from 'office-ui-fabric-react';
import { AskQuestion } from './Popup/AskQuestion';
import { ActivityItem, IActivityItemProps, Link, mergeStyleSets } from 'office-ui-fabric-react';
import * as moment from 'moment';
const classNames = mergeStyleSets({
exampleRoot: {
marginTop: '20px',
},
nameText: {
fontWeight: 'bold',
},
});
export default class QuestionnaireMeetingApp extends React.Component<IQuestionnaireMeetingAppProps, IQuestionnaireMeetingAppState> {
private SPOService: SPOService = null;
public constructor(props) {
super(props);
this.state = {
infoLoaded: false,
meetingQuestionnaire: [],
showPopup: false
};
this.SPOService = new SPOService();
this.onDismissPanel = this.onDismissPanel.bind(this);
}
public async componentDidMount() {
const meetingQuestionnaireInfo: IQuestionnaireItem[] = await this.SPOService.getQuestionnaire(this.props.listName, this.props.context.sdks.microsoftTeams.context.meetingId);
this.setState({
infoLoaded: true,
meetingQuestionnaire: meetingQuestionnaireInfo
});
}
private async onDismissPanel(refresh: boolean) {
this.setState({ showPopup: false, infoLoaded: false });
if (refresh === true) {
const meetingQuestionnaireInfo: IQuestionnaireItem[] = await this.SPOService.getQuestionnaire(this.props.listName, this.props.context.sdks.microsoftTeams.context.meetingId);
this.setState({
infoLoaded: true,
meetingQuestionnaire: meetingQuestionnaireInfo
});
}
}
public render(): React.ReactElement<IQuestionnaireMeetingAppProps> {
return (
<div className={styles.questionnaireMeetingApp}>
<div className={styles.container}>
<div className={styles.row}>
<div className={styles.column}>
<PrimaryButton className={styles.button} onClick={() => { this.setState({ showPopup: true }); }} text={strings.AddQuestion} />
</div>
</div>
<div className={styles.row}>
<div className={styles.column}>
<div>
{
this.state.meetingQuestionnaire.map(item => {
const activityItem: (IActivityItemProps & { key: string | number }) = {
key: item.ID,
activityDescription: [
<Link
key={item.ID}
className={classNames.nameText}
>
{item.Author.Title}
</Link>,
<span key={2}> {strings.Posted} </span>,
<span key={3} className={classNames.nameText}>
{item.Title}
</span>,
],
activityPersonas: [{ imageUrl: `/_layouts/15/userphoto.aspx?size=S&username=${item.Author.EMail}` }],
comments: item.Description,
timeStamp: moment(item.Modified).format("LLL"),
};
return (
<ActivityItem {...activityItem} key={activityItem.key} className={classNames.exampleRoot} />
);
})
}
</div>
<div>
{
this.state.showPopup &&
<AskQuestion
onDissmissPanel={this.onDismissPanel}
showPopup={this.state.showPopup}
context={this.props.context}
listName={this.props.listName}
/>
}
</div>
</div>
</div>
</div>
</div>
);
}
}

View File

@ -0,0 +1,12 @@
define([], function() {
return {
"PropertyPaneDescription": "Description",
"BasicGroupName": "Group Name",
"SiteURLFieldLabel": "Relative Site URL",
"ListNameFieldLabel": "Relative Site URL",
"AddQuestion": "New Question",
"Posted": "posted",
"Title": "Title",
"Description": "Description"
}
});

View File

@ -0,0 +1,15 @@
declare interface IQuestionnaireMeetingAppWebPartStrings {
PropertyPaneDescription: string;
BasicGroupName: string;
SiteURLFieldLabel: string;
ListNameFieldLabel: string;
AddQuestion: string;
Posted: string;
Title: string;
Description: string;
}
declare module 'QuestionnaireMeetingAppWebPartStrings' {
const strings: IQuestionnaireMeetingAppWebPartStrings;
export = strings;
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 157 B

View File

@ -0,0 +1,56 @@
{
"$schema": "https://developer.microsoft.com/json-schemas/teams/v1.5/MicrosoftTeams.schema.json",
"manifestVersion": "1.8",
"version": "1.0.0",
"id": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985",
"packageName": "Meeting Questionnaire",
"developer": {
"name": "Nanddeep Nachan",
"websiteUrl": "https://aka.ms/sppnp",
"privacyUrl": "https://privacy.microsoft.com/privacystatement",
"termsOfUseUrl": "https://www.microsoft.com/servicesagreement"
},
"name": {
"short": "Meeting Questionnaire",
"full": "SPFx based MS Teams Questionnaire Meeting App"
},
"description": {
"short": "MS Teams pre-meeting questionnaire experience",
"full": "Provides MS Teams pre-meeting experience with questionnaire"
},
"icons": {
"outline": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985_outline.png",
"color": "31f78b4e-8520-4ef2-884c-fb3bdd5d9985_color.png"
},
"accentColor": "#004578",
"configurableTabs": [
{
"configurationUrl": "https://{teamSiteDomain}{teamSitePath}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest={teamSitePath}/_layouts/15/teamshostedapp.aspx%3FopenPropertyPane=true%26teams%26componentId=31f78b4e-8520-4ef2-884c-fb3bdd5d9985%26forceLocale={locale}",
"canUpdateConfiguration": true,
"scopes": [
"team",
"groupchat"
],
"context": [
"channelTab",
"privateChatTab",
"meetingChatTab",
"meetingDetailsTab",
"meetingSidePanel"
]
}
],
"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"
}
}

View File

@ -0,0 +1,35 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-3.7/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": [
"webpack-env"
],
"lib": [
"es5",
"dom",
"es2015.collection",
"es2015.promise"
]
},
"include": [
"src/**/*.ts",
"src/**/*.tsx"
]
}

View File

@ -0,0 +1,30 @@
{
"extends": "./node_modules/@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
}
}