Added react-teams-lead-dashboard sample
|
@ -0,0 +1,33 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
release
|
||||
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
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.12.1",
|
||||
"libraryName": "lead-assist",
|
||||
"libraryId": "c311a0fc-3dcb-4316-a798-fd7d8a6d5344",
|
||||
"environment": "spo",
|
||||
"packageManager": "npm",
|
||||
"isCreatingSolution": false,
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
# Lead Assist Dashboard
|
||||
|
||||
## Summary
|
||||
|
||||
This sample shows how to initegrate SharePoint Framework, PnP React Controls, and Microsoft Graph Toolkit in a solution available for SharePoint web parts or Microsoft Teams personal application.
|
||||
|
||||
![Lead Assist Dashboard](/assets/LeadAssistDashboard_overview.png)
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
|
||||
![version](https://img.shields.io/npm/v/@microsoft/sp-component-base?color=green)
|
||||
|
||||
## Applies to
|
||||
|
||||
- [SharePoint Framework](https://aka.ms/spfx)
|
||||
- [Microsoft 365 tenant](https://docs.microsoft.com/en-us/sharepoint/dev/spfx/set-up-your-developer-tenant)
|
||||
- [Microsoft Teams](https://www.microsoft.com/en-ww/microsoft-teams)
|
||||
|
||||
> Get your own free development tenant by subscribing to [Microsoft 365 developer program](http://aka.ms/o365devprogram)
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
leadAssistDashboard | [PnP](https://pnp.github.io/)
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
1.0.0|October 5, 2021|Initial release
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- Ensure that you are at the solution folder
|
||||
- In the command-line run:
|
||||
- **npm install**
|
||||
- **npm run package**
|
||||
- Upload the generated SPPKG file into the SharePoint App Catalog of your tenant
|
||||
- Select the SPPKG in the App Catalog and click on "Sync to Teams" button
|
||||
- Add the web part to a SharePoint page
|
||||
- In the first run the web part will ask for the target SharePoint site URL
|
||||
|
||||
If needed:
|
||||
- Using the control panel of the web part
|
||||
- Create the SharePoint demo lists
|
||||
- Populate the SharePoint demo lists
|
||||
- Generate the Microsoft Graph demo data
|
||||
|
||||
## Features
|
||||
|
||||
This solution provides an example of how to implement a SharePoint Framework web part, that is also usable as a Microsoft Teams personal app, using the [SharePoint Framework React Controls](https://github.com/pnp/sp-dev-fx-controls-react/) and the [Microsoft Graph Toolkit](https://github.com/microsoftgraph/microsoft-graph-toolkit).
|
||||
|
||||
This web part illustrates the following concepts:
|
||||
|
||||
- How to use the [PnP React Controls](https://github.com/pnp/sp-dev-fx-controls-react/) such as the chart control
|
||||
![Activity chart detail](/assets/ActivityChart.png)
|
||||
|
||||
- How to integrate the [Microsoft Graph Toolkit](https://github.com/microsoftgraph/microsoft-graph-toolkit) in a SharePoint Framework web part such as the Agenda control
|
||||
|
||||
![MGT Agenda control in action](/assets/AgendaControl.png)
|
||||
|
||||
and the Todo control
|
||||
|
||||
![MGT Todo control in action](/assets/TodoControl.png)
|
||||
|
||||
- How to execute operations on SharePoint using [PnP JS](https://github.com/pnp/pnpjs/)
|
||||
|
||||
## 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)
|
||||
- [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
|
||||
|
||||
## 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.**
|
||||
|
||||
> "Sharing is Caring"
|
After Width: | Height: | Size: 838 KiB |
After Width: | Height: | Size: 317 KiB |
After Width: | Height: | Size: 3.0 MiB |
After Width: | Height: | Size: 395 KiB |
|
@ -0,0 +1,73 @@
|
|||
[
|
||||
{
|
||||
"name": "pnp-sp-dev-spfx-web-parts-react-teams-lead-dashboard",
|
||||
"source": "pnp",
|
||||
"title": "Lead Dashboard",
|
||||
"shortDescription": "This sample shows how to use SPFx to create a Microsoft Teams dashboard personal app.",
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/tree/main/samples/react-teams-lead-dashboard",
|
||||
"longDescription": [
|
||||
"This sample shows how to use SPFx to create a Microsoft Teams dashboard personal app."
|
||||
],
|
||||
"creationDateTime": "2021-10-05",
|
||||
"updateDateTime": "2021-10-05",
|
||||
"products": [
|
||||
"SharePoint",
|
||||
"Office"
|
||||
],
|
||||
"metadata": [
|
||||
{
|
||||
"key": "CLIENT-SIDE-DEV",
|
||||
"value": "React"
|
||||
},
|
||||
{
|
||||
"key": "SPFX-VERSION",
|
||||
"value": "1.12.0"
|
||||
}
|
||||
],
|
||||
"thumbnails": [
|
||||
{
|
||||
"type": "image",
|
||||
"order": 100,
|
||||
"url": "https://github.com/pnp/sp-dev-fx-webparts/raw/main/samples/react-teams-lead-dashboard/assets/LeadAssistDashboard_overview.png",
|
||||
"alt": "Lead Dashboard"
|
||||
}
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"gitHubAccount": "PaoloPia",
|
||||
"company": "PiaSys.com",
|
||||
"pictureUrl": "https://github.com/PaoloPia.png",
|
||||
"name": "Paolo Pialorsi",
|
||||
"twitter": "PaoloPia"
|
||||
},
|
||||
{
|
||||
"gitHubAccount": "GuidoZam",
|
||||
"company": "PiaSys.com",
|
||||
"pictureUrl": "https://github.com/GuidoZam.png",
|
||||
"name": "Guido Zambarda"
|
||||
}
|
||||
],
|
||||
"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/en-us/sharepoint/dev/spfx/web-parts/get-started/build-a-hello-world-web-part"
|
||||
},
|
||||
{
|
||||
"name": "Building for Microsoft Teams",
|
||||
"description": "Learn how to create SPFx web parts that integrate with the Microsoft Teams context.",
|
||||
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/build-for-teams-overview"
|
||||
},
|
||||
{
|
||||
"name": "Use Microsoft Graph in your solution",
|
||||
"description": "Learn how you can leverage the Microsoft Graph API from your custom developed SPFx web parts.",
|
||||
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/web-parts/get-started/using-microsoft-graph-apis"
|
||||
},
|
||||
{
|
||||
"name": "Publish SharePoint Framework applications to the Marketplace",
|
||||
"description": "Publishing your SharePoint Framework solutions to marketplace (also known as AppSource) and to SharePoint store, which allows you to reach other organizations and let them easily install your application in their Microsoft 365 tenant.",
|
||||
"url": "https://docs.microsoft.com/en-us/sharepoint/dev/spfx/publish-to-marketplace-overview"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"lead-assist-dashboard-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/leadAssistDashboard/LeadAssistDashboardWebPart.js",
|
||||
"manifest": "./src/webparts/leadAssistDashboard/LeadAssistDashboardWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
},
|
||||
"lead-assist-dashboard-settings-web-part": {
|
||||
"components": [
|
||||
{
|
||||
"entrypoint": "./lib/webparts/leadAssistDashboardSettings/LeadAssistDashboardSettingsWebPart.js",
|
||||
"manifest": "./src/webparts/leadAssistDashboardSettings/LeadAssistDashboardSettingsWebPart.manifest.json"
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"externals": {},
|
||||
"localizedResources": {
|
||||
"LeadAssistDashboardWebPartStrings": "lib/webparts/leadAssistDashboard/loc/{locale}.js",
|
||||
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
|
||||
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js",
|
||||
"LeadAssistDashboardSettingsWebPartStrings": "lib/webparts/leadAssistDashboardSettings/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "./release/assets/"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./release/assets/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "lead-assist",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "lead-assist-client-side-solution",
|
||||
"id": "c311a0fc-3dcb-4316-a798-fd7d8a6d5344",
|
||||
"version": "1.0.11.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true,
|
||||
"isDomainIsolated": false,
|
||||
"webApiPermissionRequests": [
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Tasks.ReadWrite"
|
||||
},
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Calendars.ReadWrite"
|
||||
},
|
||||
{
|
||||
"resource": "Microsoft Graph",
|
||||
"scope": "Files.ReadWrite.AppFolder"
|
||||
}
|
||||
],
|
||||
"developer": {
|
||||
"name": "PnP",
|
||||
"websiteUrl": "https://pnp.github.io/",
|
||||
"privacyUrl": "https://pnp.github.io/",
|
||||
"termsOfUseUrl": "https://pnp.github.io/",
|
||||
"mpnId": ""
|
||||
}
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/lead-assist.sppkg"
|
||||
}
|
||||
}
|
|
@ -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/"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "<!-- PATH TO CDN -->"
|
||||
}
|
|
@ -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'));
|
|
@ -0,0 +1,40 @@
|
|||
{
|
||||
"name": "lead-assist",
|
||||
"version": "1.0.11",
|
||||
"private": true,
|
||||
"main": "lib/index.js",
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"serve": "gulp serve --nobrowser",
|
||||
"package": "gulp bundle --ship & gulp package-solution --ship"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/mgt-react": "^2.2.0",
|
||||
"@microsoft/mgt-spfx": "^2.2.0",
|
||||
"@microsoft/sp-core-library": "1.12.1",
|
||||
"@microsoft/sp-lodash-subset": "1.12.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "1.12.1",
|
||||
"@microsoft/sp-property-pane": "1.12.1",
|
||||
"@microsoft/sp-webpart-base": "1.12.1",
|
||||
"@pnp/sp": "^2.7.0",
|
||||
"@pnp/spfx-controls-react": "3.1.0",
|
||||
"office-ui-fabric-react": "7.156.0",
|
||||
"react": "16.9.0",
|
||||
"react-dom": "16.9.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/rush-stack-compiler-3.7": "0.2.3",
|
||||
"@microsoft/sp-build-web": "1.12.1",
|
||||
"@microsoft/sp-module-interfaces": "1.12.1",
|
||||
"@microsoft/sp-tslint-rules": "1.12.1",
|
||||
"@microsoft/sp-webpart-workbench": "1.12.1",
|
||||
"@pnp/spfx-property-controls": "^3.2.0",
|
||||
"@types/react": "16.9.36",
|
||||
"@types/react-dom": "16.9.8",
|
||||
"@types/webpack-env": "1.13.1",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~4.0.2",
|
||||
"sp-pnp-js": "^3.0.10"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
@import './colors.module';
|
||||
[data-theme='contrast'] {
|
||||
:global {
|
||||
.ms-Fabric {
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-Button-icon {
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
}
|
||||
|
||||
.ms-Overlay {
|
||||
background-color: $contrast-leadAssistDashboard-overlay;
|
||||
}
|
||||
|
||||
.ms-Panel-main {
|
||||
background-color: $contrast-leadAssistDashboard-surfaceBackground;
|
||||
border-left-color: $contrast-leadAssistDashboard-panelBorder;
|
||||
border-right-color: $contrast-leadAssistDashboard-panelBorder;
|
||||
.ms-Panel-headerText {
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
}
|
||||
}
|
||||
|
||||
.spPropertyPaneContainer {
|
||||
background-color: $contrast-leadAssistDashboard-white;
|
||||
[class^="propertyPane_"] {
|
||||
background-color: $contrast-leadAssistDashboard-white;
|
||||
border-left-color: $contrast-leadAssistDashboard-panelBorder;
|
||||
[class^="propertyPanePageTitle_"],
|
||||
[class^="propertyPanePageDescription_"],
|
||||
[class^="propertyPaneGroupHeaderNoAccordion_"] {
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-Button--icon {
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ms-Label {
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-TextField {
|
||||
.ms-TextField-fieldGroup {
|
||||
background-color: $contrast-leadAssistDashboard-inputBackground;
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
border-color: $contrast-leadAssistDashboard-inputBorder;
|
||||
.ms-TextField-field {
|
||||
color: $contrast-leadAssistDashboard-primaryText;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $contrast-leadAssistDashboard-inputBorderHovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
@import './colors.module';
|
||||
[data-theme='dark'] {
|
||||
:global {
|
||||
.ms-Fabric {
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-Button-icon {
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
}
|
||||
|
||||
.ms-Overlay {
|
||||
background-color: $dark-leadAssistDashboard-overlay;
|
||||
}
|
||||
|
||||
.ms-Panel-main {
|
||||
background-color: $dark-leadAssistDashboard-surfaceBackground;
|
||||
border-left-color: $dark-leadAssistDashboard-panelBorder;
|
||||
border-right-color: $dark-leadAssistDashboard-panelBorder;
|
||||
.ms-Panel-headerText {
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
}
|
||||
}
|
||||
|
||||
.spPropertyPaneContainer {
|
||||
background-color: $dark-leadAssistDashboard-white;
|
||||
[class^="propertyPane_"] {
|
||||
background-color: $dark-leadAssistDashboard-white;
|
||||
border-left-color: $dark-leadAssistDashboard-panelBorder;
|
||||
[class^="propertyPanePageTitle_"],
|
||||
[class^="propertyPanePageDescription_"],
|
||||
[class^="propertyPaneGroupHeaderNoAccordion_"] {
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-Button--icon {
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.ms-Label {
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-TextField {
|
||||
.ms-TextField-fieldGroup {
|
||||
background-color: $dark-leadAssistDashboard-inputBackground;
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
border-color: $dark-leadAssistDashboard-inputBorder;
|
||||
.ms-TextField-field {
|
||||
color: $dark-leadAssistDashboard-primaryText;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $dark-leadAssistDashboard-inputBorderHovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
@import './colors.module';
|
||||
[data-theme='default'] {
|
||||
:global {
|
||||
.ms-Fabric {
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-Button-icon {
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
}
|
||||
|
||||
.ms-Overlay {
|
||||
background-color: $default-leadAssistDashboard-overlay;
|
||||
}
|
||||
|
||||
.ms-Panel-main {
|
||||
background-color: $default-leadAssistDashboard-surfaceBackground;
|
||||
border-left-color: $default-leadAssistDashboard-panelBorder;
|
||||
border-right-color: $default-leadAssistDashboard-panelBorder;
|
||||
.ms-Panel-headerText {
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
}
|
||||
}
|
||||
|
||||
// Property Pane
|
||||
.spPropertyPaneContainer {
|
||||
background-color: $default-leadAssistDashboard-white;
|
||||
[class^="propertyPane_"] {
|
||||
background-color: $default-leadAssistDashboard-white;
|
||||
border-left-color: $default-leadAssistDashboard-panelBorder;
|
||||
[class^="propertyPanePageTitle_"],
|
||||
[class^="propertyPanePageDescription_"],
|
||||
[class^="propertyPaneGroupHeaderNoAccordion_"] {
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-Button--icon {
|
||||
&:hover {
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Text Field
|
||||
.ms-Label {
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
}
|
||||
.ms-TextField {
|
||||
.ms-TextField-fieldGroup {
|
||||
background-color: $default-leadAssistDashboard-inputBackground;
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
border-color: $default-leadAssistDashboard-inputBorder;
|
||||
.ms-TextField-field {
|
||||
color: $default-leadAssistDashboard-primaryText;
|
||||
}
|
||||
&:hover {
|
||||
border-color: $default-leadAssistDashboard-inputBorderHovered;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
//SharePoint
|
||||
$leadAssistDashboard-background: "[theme:white, default:#fff]";
|
||||
$leadAssistDashboard-color: "[theme:primaryText, default:#333]";
|
||||
$leadAssistDashboard-buttonBackground: "[theme:themePrimary, default:#0078d4]";
|
||||
$leadAssistDashboard-buttonColor: "[theme:white, default:#fff]";
|
||||
$leadAssistDashboard-callRGBColor: rgb(101, 94, 134);
|
||||
$leadAssistDashboard-emailRGBColor: rgb(237, 208, 149);
|
||||
$leadAssistDashboard-textRGBColor: rgb(207, 157, 188);
|
||||
$leadAssistDashboard-eventColor: #0078d4;
|
||||
$leadAssistDashboard-eventTeamsColor: #6264a7;
|
||||
$leadAssistDashboard-firstComponent-background: "[theme:white, default:#fff]";
|
||||
$leadAssistDashboard-firstComponent-color: "[theme:primaryText, default:#333]";
|
||||
$leadAssistDashboard-firstComponentButton-background: "[theme:themePrimary, default:#0078d4]";
|
||||
$leadAssistDashboard-firstComponentButton-color: "[theme:white, default:#fff]";
|
||||
$leadAssistDashboard-white: "[theme:white, default: #fff]"; // property pane background
|
||||
$leadAssistDashboard-inputBackground: "[theme:inputBackground, default:#fff]"; //input background
|
||||
$leadAssistDashboard-inputBorder: "[theme:inputBorder, default:#a6a6a6]"; // input border
|
||||
$leadAssistDashboard-inputBorderHovered: "[theme:inputBorderHovered, default:#333333]"; // input border hovered
|
||||
$leadAssistDashboard-overlay: "[theme:whiteTranslucent40, default:rgba(255, 255,255, 0.4)]";
|
||||
$leadAssistDashboard-surfaceBackground: "[theme:white, default:#fff]";
|
||||
$leadAssistDashboard-primaryText: "[theme:primaryText, default:#333]";
|
||||
$leadAssistDashboard-panelBorder: "[theme: neutralLight, default: #eaeaea]";
|
||||
|
||||
// default theme
|
||||
$default-leadAssistDashboard-background: #f3f2f1;
|
||||
$default-leadAssistDashboard-color: #252423;
|
||||
$default-leadAssistDashboard-buttonBackground: #6264a7;
|
||||
$default-leadAssistDashboard-buttonColor: #f3f2f1;
|
||||
$default-leadAssistDashboard-callRGBColor: rgb(101, 94, 134);
|
||||
$default-leadAssistDashboard-emailRGBColor: rgb(237, 208, 149);
|
||||
$default-leadAssistDashboard-textRGBColor: rgb(207, 157, 188);
|
||||
$default-leadAssistDashboard-eventColor: #0078d4;
|
||||
$default-leadAssistDashboard-eventTeamsColor: #6264a7;
|
||||
$default-leadAssistDashboard-firstComponent-background: #f3f2f1;
|
||||
$default-leadAssistDashboard-firstComponent-color: #252423;
|
||||
$default-leadAssistDashboard-firstComponentButton-background: #6264a7;
|
||||
$default-leadAssistDashboard-firstComponentButton-color: #f3f2f1;
|
||||
$default-leadAssistDashboard-white: #f3f2f1; // property pane background
|
||||
$default-leadAssistDashboard-inputBackground: #fff;
|
||||
$default-leadAssistDashboard-inputBorder: #b5b4b2;
|
||||
$default-leadAssistDashboard-inputBorderHovered: #252423;
|
||||
$default-leadAssistDashboard-overlay: rgba(255, 255, 255, 0.4);
|
||||
$default-leadAssistDashboard-surfaceBackground: #f3f2f1;
|
||||
$default-leadAssistDashboard-primaryText: #252423;
|
||||
$default-leadAssistDashboard-panelBorder: #dedddc;
|
||||
|
||||
// dark theme
|
||||
$dark-leadAssistDashboard-background: #2d2c2c;
|
||||
$dark-leadAssistDashboard-color: #ffffff;
|
||||
$dark-leadAssistDashboard-buttonBackground: #6264a7;
|
||||
$dark-leadAssistDashboard-buttonColor: #2d2c2c;
|
||||
$dark-leadAssistDashboard-callRGBColor: rgb(101, 94, 134);
|
||||
$dark-leadAssistDashboard-emailRGBColor: rgb(237, 208, 149);
|
||||
$dark-leadAssistDashboard-textRGBColor: rgb(207, 157, 188);
|
||||
$dark-leadAssistDashboard-eventColor: #0078d4;
|
||||
$dark-leadAssistDashboard-eventTeamsColor: #6264a7;
|
||||
$dark-leadAssistDashboard-firstComponent-background: #2d2c2c;
|
||||
$dark-leadAssistDashboard-firstComponent-color: #ffffff;
|
||||
$dark-leadAssistDashboard-firstComponentButton-background: #6264a7;
|
||||
$dark-leadAssistDashboard-firstComponentButton-color: #2d2c2c;
|
||||
$dark-leadAssistDashboard-white: #2d2c2c; // property pane background
|
||||
$dark-leadAssistDashboard-inputBackground: #000;
|
||||
$dark-leadAssistDashboard-inputBorder: #c8c8c8;
|
||||
$dark-leadAssistDashboard-inputBorderHovered: #ffffff;
|
||||
$dark-leadAssistDashboard-overlay: rgba(37, 36, 35, 0.75);
|
||||
$dark-leadAssistDashboard-surfaceBackground: #2d2c2c;
|
||||
$dark-leadAssistDashboard-primaryText: #ffffff;
|
||||
$dark-leadAssistDashboard-panelBorder: #4c4b4b;
|
||||
|
||||
// contrast theme
|
||||
$contrast-leadAssistDashboard-background: #000000;
|
||||
$contrast-leadAssistDashboard-color: #ffffff;
|
||||
$contrast-leadAssistDashboard-buttonBackground: #6264a7;
|
||||
$contrast-leadAssistDashboard-buttonColor: #000000;
|
||||
$contrast-leadAssistDashboard-callRGBColor: rgb(101, 94, 134);
|
||||
$contrast-leadAssistDashboard-emailRGBColor: rgb(237, 208, 149);
|
||||
$contrast-leadAssistDashboard-textRGBColor: rgb(207, 157, 188);
|
||||
$contrast-leadAssistDashboard-eventColor: #0078d4;
|
||||
$contrast-leadAssistDashboard-eventTeamsColor: #6264a7;
|
||||
$contrast-leadAssistDashboard-firstComponent-background: #000000;
|
||||
$contrast-leadAssistDashboard-firstComponent-color: #ffffff;
|
||||
$contrast-leadAssistDashboard-firstComponentButton-background: #6264a7;
|
||||
$contrast-leadAssistDashboard-firstComponentButton-color: #000000;
|
||||
$contrast-leadAssistDashboard-white: #000000; // property pane background
|
||||
$contrast-leadAssistDashboard-inputBackground: #000;
|
||||
$contrast-leadAssistDashboard-inputBorder: #c8c8c8;
|
||||
$contrast-leadAssistDashboard-inputBorderHovered: #ffffff;
|
||||
$contrast-leadAssistDashboard-overlay: rgba(37, 36, 35, 0.75);
|
||||
$contrast-leadAssistDashboard-surfaceBackground: #000000;
|
||||
$contrast-leadAssistDashboard-primaryText: #ffffff;
|
||||
$contrast-leadAssistDashboard-panelBorder: #4c4b4b;
|
|
@ -0,0 +1 @@
|
|||
// A file is required to be in the root of the /src directory by the TypeScript compiler
|
|
@ -0,0 +1,370 @@
|
|||
import * as strings from "LeadAssistDashboardWebPartStrings";
|
||||
import { Providers } from "@microsoft/mgt-spfx";
|
||||
import { IList, sp } from "@pnp/sp/presets/all";
|
||||
import { IListField } from "./IListField";
|
||||
import { IValueListItem } from "./IValueListItem";
|
||||
import { ISaleListItem } from "./ISaleListItem";
|
||||
|
||||
export default class DataService {
|
||||
// Lists names
|
||||
public static ActivityCallsListName: string = strings.ActivityCallsListName;
|
||||
public static ActivityEmailsListName: string = strings.ActivityEmailsListName;
|
||||
public static ActivityTextsListName: string = strings.ActivityTextsListName;
|
||||
public static ProgressListName: string = strings.ProgressListName;
|
||||
public static RecentlyDoneSalesContractsListName: string = strings.RecentlyDoneSalesContractsListName;
|
||||
// Fields titles
|
||||
public static FieldTitleName: string = "Title";
|
||||
public static FieldValueName: string = "DemoValue";
|
||||
public static FieldDescriptionName: string = "DemoDescription";
|
||||
public static FieldMonthName: string = "DemoMonth";
|
||||
public static FieldGroupName: string = "Demo Group";
|
||||
// Fields types
|
||||
public static FieldTypeText: string = "SP.FieldText";
|
||||
public static FieldTypeTextKindId: number = 2;
|
||||
public static FieldTypeNumber: string = "SP.FieldNumber";
|
||||
public static FieldTypeNumberKindId: number = 9;
|
||||
|
||||
public static MonthNames: string[] = [ strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July ];
|
||||
|
||||
// Value field definition
|
||||
private static ValueField: IListField = {
|
||||
fieldName: DataService.FieldValueName,
|
||||
fieldType: DataService.FieldTypeNumber,
|
||||
fieldTypeKindId: DataService.FieldTypeNumberKindId
|
||||
};
|
||||
|
||||
// Description field definition
|
||||
private static DescriptionField: IListField = {
|
||||
fieldName: DataService.FieldDescriptionName,
|
||||
fieldType: DataService.FieldTypeText,
|
||||
fieldTypeKindId: DataService.FieldTypeTextKindId
|
||||
};
|
||||
|
||||
// Month field definition
|
||||
private static MonthField: IListField = {
|
||||
fieldName: DataService.FieldMonthName,
|
||||
fieldType: DataService.FieldTypeText,
|
||||
fieldTypeKindId: DataService.FieldTypeTextKindId
|
||||
};
|
||||
|
||||
/**
|
||||
* Format the specific date in a short locale string
|
||||
* @param date The date to be formatted
|
||||
* @returns The formatted date in string format
|
||||
*/
|
||||
public static getTime(date: Date): string {
|
||||
const timeOptions = {
|
||||
timeStyle: 'short',
|
||||
hour12: true
|
||||
};
|
||||
|
||||
return date.toLocaleTimeString([], timeOptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the specific date in a short locale string
|
||||
* @param date The date to be formatted
|
||||
* @returns The formatted date in string format
|
||||
*/
|
||||
public static getDate(date: Date): string {
|
||||
return date.toLocaleDateString([], { day: 'numeric', month: 'short' });
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate an array of number for demo data
|
||||
* @param length Length of the numeric array
|
||||
* @returns Numeric array of demo data
|
||||
*/
|
||||
public static generateNumericDemoData(length: number): number[] {
|
||||
let demoData = [...Array(length)].map(() => {
|
||||
let n = Math.floor(Math.random() * 9);
|
||||
|
||||
return n;
|
||||
});
|
||||
|
||||
return demoData;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the demo fields and lists
|
||||
*/
|
||||
public static async generateDemoLists(): Promise<void> {
|
||||
if (confirm(strings.ConfirmCreateDemoLists) == true) {
|
||||
// Ensure fields
|
||||
await this.ensureWebField(DataService.FieldValueName, DataService.FieldTypeNumber, DataService.FieldTypeNumberKindId);
|
||||
await this.ensureWebField(DataService.FieldDescriptionName, DataService.FieldTypeText, DataService.FieldTypeTextKindId);
|
||||
await this.ensureWebField(DataService.FieldMonthName, DataService.FieldTypeText, DataService.FieldTypeTextKindId);
|
||||
|
||||
// Ensure lists
|
||||
await this.ensureList(DataService.ActivityCallsListName, [this.ValueField]);
|
||||
await this.ensureList(DataService.ActivityEmailsListName, [this.ValueField]);
|
||||
await this.ensureList(DataService.ActivityTextsListName, [this.ValueField]);
|
||||
await this.ensureList(DataService.ProgressListName, [this.ValueField]);
|
||||
await this.ensureList(DataService.RecentlyDoneSalesContractsListName, [this.DescriptionField]);
|
||||
|
||||
alert(strings.DemoListsCreated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the demo data for the SharePoint lists
|
||||
*/
|
||||
public static async generateListsDemoData(): Promise<void> {
|
||||
if (confirm(strings.ConfirmAddDemoData) == true) {
|
||||
// Get all the lists references
|
||||
const activityCallsList = await sp.web.lists.getByTitle(DataService.ActivityCallsListName);
|
||||
const activityEmailsList = await sp.web.lists.getByTitle(DataService.ActivityEmailsListName);
|
||||
const activityTextsList = await sp.web.lists.getByTitle(DataService.ActivityTextsListName);
|
||||
const progressList = await sp.web.lists.getByTitle(DataService.ProgressListName);
|
||||
const recentContractsList = await sp.web.lists.getByTitle(DataService.RecentlyDoneSalesContractsListName);
|
||||
|
||||
// Generate sales products list demo data
|
||||
DataService.generateNumericDemoData(7).forEach((p, i) => {
|
||||
activityCallsList.items.add({
|
||||
Title: strings.DemoActivityCallTitle + " " + i,
|
||||
DemoValue: p * 10
|
||||
});
|
||||
});
|
||||
|
||||
// Generate sales services list demo data
|
||||
DataService.generateNumericDemoData(7).forEach((p, i) => {
|
||||
activityEmailsList.items.add({
|
||||
Title: strings.DemoActivityEmailTitle + " " + i,
|
||||
DemoValue: p * 10
|
||||
});
|
||||
});
|
||||
|
||||
// Generate activity text list demo data
|
||||
DataService.generateNumericDemoData(7).forEach((p, i) => {
|
||||
activityTextsList.items.add({
|
||||
Title: strings.DemoActivityTextTitle + " " + i,
|
||||
DemoValue: p * 10
|
||||
});
|
||||
});
|
||||
|
||||
// Generate progress list demo data
|
||||
DataService.generateNumericDemoData(9).forEach(p => {
|
||||
progressList.items.add({
|
||||
Title: p.toString(),
|
||||
DemoValue: p
|
||||
});
|
||||
});
|
||||
|
||||
// Generate recently list demo data
|
||||
[...Array(5)].map((v, i) => {
|
||||
return {
|
||||
title: strings.DemoDocumentTitle + " " + i,
|
||||
description: strings.DemoDocumentDescription + " " + i
|
||||
};
|
||||
}).forEach(r => {
|
||||
recentContractsList.items.add({
|
||||
Title: r.title,
|
||||
DemoDescription: r.description
|
||||
});
|
||||
});
|
||||
|
||||
alert(strings.DemoDataGenerated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate demo data using Graph
|
||||
*/
|
||||
public static async generateGraphDemoData(): Promise<void> {
|
||||
if (confirm(strings.ConfirmAddGraphDemoData) == true) {
|
||||
let provider = Providers.globalProvider;
|
||||
if (provider) {
|
||||
// Get the Graph client
|
||||
let graphClient = provider.graph.client;
|
||||
|
||||
// Add new task
|
||||
let taskLists = await graphClient.api("me/todo/lists").get();
|
||||
|
||||
// Simple task
|
||||
await graphClient.api(`me/todo/lists/${taskLists.value[0].id}/tasks`).create({
|
||||
"title": strings.DemoEventTitle,
|
||||
"linkedResources": [
|
||||
{
|
||||
"webUrl": "https://aka.ms/m365pnp",
|
||||
"applicationName": strings.DemoApplicationName,
|
||||
"displayName": strings.DemoDisplayName
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Task with due date
|
||||
let taskDate = new Date();
|
||||
taskDate.setDate(taskDate.getDate() + 1);
|
||||
await graphClient.api(`me/todo/lists/${taskLists.value[0].id}/tasks`).create({
|
||||
"title": strings.DemoTaskWithDateTitle,
|
||||
"dueDateTime": {
|
||||
"dateTime": taskDate.toISOString(),
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"linkedResources":[
|
||||
{
|
||||
"webUrl": "https://aka.ms/m365pnp",
|
||||
"applicationName": strings.DemoApplicationName,
|
||||
"displayName": strings.DemoDisplayName
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// Start and end date for events
|
||||
let startDate = new Date();
|
||||
let endDate = new Date();
|
||||
|
||||
// Set the start and end date to tomorrow
|
||||
startDate.setDate(startDate.getDate() + 1);
|
||||
endDate.setDate(endDate.getDate() + 1);
|
||||
// Add one hour to the end date
|
||||
endDate.setHours(endDate.getHours() + 1);
|
||||
|
||||
// Event with location
|
||||
await graphClient.api("me/events").create({
|
||||
"subject": strings.DemoEventTitle,
|
||||
"body": {
|
||||
"contentType": "HTML",
|
||||
"content": strings.DemoEventContent
|
||||
},
|
||||
"start": {
|
||||
"dateTime": startDate.toISOString(),
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"end": {
|
||||
"dateTime": endDate.toISOString(),
|
||||
"timeZone": "UTC"
|
||||
},
|
||||
"location":{
|
||||
"displayName": strings.DemoEventLocation
|
||||
}
|
||||
});
|
||||
|
||||
// Change date for the next event
|
||||
startDate.setDate(startDate.getDate() + 1);
|
||||
endDate.setDate(endDate.getDate() + 1);
|
||||
|
||||
// Online event
|
||||
await graphClient.api("me/events").create({
|
||||
"subject": strings.DemoEventOnlineTitle,
|
||||
"body": {
|
||||
"contentType": "HTML",
|
||||
"content": strings.DemoEventOnlineContent
|
||||
},
|
||||
"start": {
|
||||
"dateTime": startDate.toISOString(),
|
||||
"timeZone": "Pacific Standard Time"
|
||||
},
|
||||
"end": {
|
||||
"dateTime": endDate.toISOString(),
|
||||
"timeZone": "Pacific Standard Time"
|
||||
},
|
||||
"isOnlineMeeting": "true"
|
||||
});
|
||||
}
|
||||
|
||||
alert(strings.GraphDemoDataGenerated);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the SharePoint lists
|
||||
*/
|
||||
public static async deleteSharePointDemoLists(): Promise<void> {
|
||||
if (confirm(strings.ConfirmDeleteSharePointDemoLists) == true) {
|
||||
// Get all the lists references
|
||||
const activityCallsList = await sp.web.lists.getByTitle(DataService.ActivityCallsListName);
|
||||
const activityEmailsList = await sp.web.lists.getByTitle(DataService.ActivityEmailsListName);
|
||||
const activityTextsList = await sp.web.lists.getByTitle(DataService.ActivityTextsListName);
|
||||
const progressList = await sp.web.lists.getByTitle(DataService.ProgressListName);
|
||||
const recentContractsList = await sp.web.lists.getByTitle(DataService.RecentlyDoneSalesContractsListName);
|
||||
|
||||
// Delete the lists
|
||||
await activityCallsList.delete();
|
||||
await activityEmailsList.delete();
|
||||
await activityTextsList.delete();
|
||||
await progressList.delete();
|
||||
await recentContractsList.delete();
|
||||
|
||||
alert(strings.SharePointDemoListsDeleted);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items with values from a specific SharePoint list
|
||||
* @param listTitle Title of the list
|
||||
* @param mappingFunction Function to map every items of the list
|
||||
* @returns An array of the mapped items
|
||||
*/
|
||||
public static async getItemsWithValueFromList(listTitle: string, mappingFunction: (value: any, index: number) => IValueListItem): Promise<IValueListItem[]> {
|
||||
return await this.getItemsFromList(listTitle, mappingFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get sales items from a specific SharePoint list
|
||||
* @param listTitle Title of the list
|
||||
* @param mappingFunction Function to map every items of the list
|
||||
* @returns An array of the mapped items
|
||||
*/
|
||||
public static async getSalesItemsFromList(listTitle: string, mappingFunction: (value: any, index: number) => ISaleListItem): Promise<ISaleListItem[]> {
|
||||
return await this.getItemsFromList(listTitle, mappingFunction);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get items from a specific SharePoint list
|
||||
* @param listTitle Title of the list
|
||||
* @param mappingFunction Function to map every items of the list
|
||||
* @returns An array of the mapped items
|
||||
*/
|
||||
private static async getItemsFromList<T>(listTitle: string, mappingFunction: (value: T, index: number) => T): Promise<T[]> {
|
||||
// Get the items from the list
|
||||
const items = await sp.web.lists.getByTitle(listTitle).items.getAll();
|
||||
// Map the items with the specified function
|
||||
const mappedItems = items.map((v, i) => mappingFunction(v, i));
|
||||
return mappedItems;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a field on a SharePoint site exists, otherwise create it
|
||||
* @param fieldName Name of the field
|
||||
* @param fieldType Type of the field
|
||||
* @param typeKindId Type kind id of the field
|
||||
*/
|
||||
private static async ensureWebField(fieldName: string, fieldType: string, typeKindId: number) {
|
||||
var existingField = (await sp.web.fields.get()).filter(f => f.StaticName == fieldName);
|
||||
|
||||
// If the field has not been added yet create it
|
||||
if (existingField && existingField.length == 0) {
|
||||
await sp.web.fields.add(fieldName, fieldType, { FieldTypeKind: typeKindId, Group: DataService.FieldGroupName });
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a specific field to the target SharePoint list
|
||||
* @param targetList Name of the target SharePoint list
|
||||
* @param fieldName Name of the field
|
||||
* @param fieldType Type of the field
|
||||
* @param typeKindId Type kind id of the field
|
||||
*/
|
||||
private static async addFieldToList(targetList: IList, fieldName: string, fieldType: string, fieldTypeKindId: number) {
|
||||
await targetList.fields.add(fieldName, fieldType, { FieldTypeKind: fieldTypeKindId, Group: DataService.FieldGroupName });
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that a SharePoint list exists, otherwise create it
|
||||
* @param listName Name of the SharePoint list
|
||||
* @param fieldsToAdd Fields to add to the SharePoint list
|
||||
*/
|
||||
private static async ensureList(listName: string, fieldsToAdd: IListField[]) {
|
||||
// Ensure the existence of the demo list
|
||||
const listExists = await sp.web.lists.ensure(listName);
|
||||
|
||||
// If the list exists and there are fields specified
|
||||
if (listExists.created == true && fieldsToAdd != undefined) {
|
||||
// Cycle through fields to add
|
||||
for (let i = 0; i < fieldsToAdd.length; i++) {
|
||||
await this.addFieldToList(listExists.list, fieldsToAdd[i].fieldName, fieldsToAdd[i].fieldType, fieldsToAdd[i].fieldTypeKindId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface IListField {
|
||||
fieldName: string;
|
||||
fieldType: string;
|
||||
fieldTypeKindId: number;
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
export interface ISaleListItem {
|
||||
id: number;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
export interface IValueListItem {
|
||||
value: number;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
import { HttpClient, MSGraphClient } from "@microsoft/sp-http";
|
||||
|
||||
export default class SettingsService {
|
||||
/**
|
||||
* Get the application settings from drive
|
||||
* @param graphClient Graph client to be used for the request
|
||||
* @param httpClient Http client to be used for the request
|
||||
* @returns Object representing the JSON settings file
|
||||
*/
|
||||
public static async getSettings(graphClient: MSGraphClient, httpClient: HttpClient): Promise<any> {
|
||||
// Get approot files
|
||||
const approotFiles = await graphClient
|
||||
.api('/me/drive/special/approot/children')
|
||||
.version("v1.0")
|
||||
.get();
|
||||
|
||||
// If any it means that a settings.json file exists
|
||||
if (approotFiles.value.length > 0){
|
||||
// Get the settings.json file download URL
|
||||
const downloadUrl = approotFiles.value[0]["@microsoft.graph.downloadUrl"];
|
||||
|
||||
// Get the settings.json file
|
||||
const settingsResponse = await httpClient.get(downloadUrl, HttpClient.configurations.v1);
|
||||
const settings = await settingsResponse.json();
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save the specified settings to drive
|
||||
* @param graphClient Graph client to be used for the request
|
||||
* @param settings Object representing the settings to be saved on the JSON settings file
|
||||
*/
|
||||
public static async saveSiteUrl(graphClient: MSGraphClient, settings: any) {
|
||||
// Save the settings in the application dedicated folder
|
||||
await graphClient
|
||||
.api('/me/drive/special/approot:/settings.json:/content')
|
||||
.version("v1.0")
|
||||
.header('content-type', 'text/plain')
|
||||
.put(JSON.stringify(settings));
|
||||
|
||||
location.reload();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "29c7e411-c4ec-4592-8b24-dbfd8bd1e1ca",
|
||||
"alias": "LeadAssistDashboardWebPart",
|
||||
"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,
|
||||
"supportsFullBleed": true,
|
||||
"supportedHosts": ["SharePointWebPart", "TeamsPersonalApp"],
|
||||
"canUpdateConfiguration": true,
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Lead Assist Dashboard" },
|
||||
"description": { "default": "Lead Assist Dashboard" },
|
||||
"officeFabricIconFontName": "BIDashboard",
|
||||
"properties": {
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Version } from '@microsoft/sp-core-library';
|
||||
import {
|
||||
IPropertyPaneConfiguration,
|
||||
PropertyPaneButton,
|
||||
PropertyPaneButtonType
|
||||
} from '@microsoft/sp-property-pane';
|
||||
import { BaseClientSideWebPart } from '@microsoft/sp-webpart-base';
|
||||
|
||||
import * as strings from 'LeadAssistDashboardWebPartStrings';
|
||||
import LeadAssistDashboard from './components/LeadAssistDashboard';
|
||||
import { ILeadAssistDashboardProps } from './components/ILeadAssistDashboardProps';
|
||||
import { Providers, SharePointProvider } from '@microsoft/mgt-spfx';
|
||||
import { sp } from "@pnp/sp/presets/all";
|
||||
import DataService from '../../services/DataService';
|
||||
import { MSGraphClient } from "@microsoft/sp-http";
|
||||
import SettingsService from '../../services/SettingsService';
|
||||
|
||||
export interface ILeadAssistDashboardWebPartProps {
|
||||
}
|
||||
|
||||
export default class LeadAssistDashboardWebPart extends BaseClientSideWebPart<ILeadAssistDashboardWebPartProps> {
|
||||
private isTeamsContext: boolean;
|
||||
private siteUrl: string;
|
||||
private graphClient: MSGraphClient;
|
||||
|
||||
protected async onInit() {
|
||||
// Determine if we are in the Teams context
|
||||
this.isTeamsContext = !!this.context.sdks.microsoftTeams;
|
||||
|
||||
if (this.isTeamsContext) {
|
||||
const teamsContext = this.context.sdks.microsoftTeams.context;
|
||||
this.applyTheme(teamsContext.theme || 'default');
|
||||
this.context.sdks.microsoftTeams.teamsJs.registerOnThemeChangeHandler(this.applyTheme);
|
||||
}
|
||||
|
||||
// Create the Microsoft Graph client
|
||||
this.graphClient = await this.context.msGraphClientFactory.getClient();
|
||||
|
||||
// Get the settings
|
||||
const settings = await SettingsService.getSettings(this.graphClient, this.context.httpClient);
|
||||
|
||||
// If there are settings specified
|
||||
if (settings) {
|
||||
// Get the site URL
|
||||
this.siteUrl = settings.siteUrl;
|
||||
}
|
||||
|
||||
// If no global provider has been specified
|
||||
if (!Providers.globalProvider) {
|
||||
// Create a global SharePoint provider
|
||||
Providers.globalProvider = new SharePointProvider(this.context);
|
||||
}
|
||||
|
||||
// If site url has been specified
|
||||
if (this.siteUrl && this.siteUrl.length > 0) {
|
||||
// Setup the SharePoint client
|
||||
sp.setup({
|
||||
spfxContext: this.context,
|
||||
sp: {
|
||||
baseUrl: this.siteUrl
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private buttonsAreDisabled() {
|
||||
return !this.siteUrl || this.siteUrl.length == 0;
|
||||
}
|
||||
|
||||
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<ILeadAssistDashboardProps> = React.createElement(
|
||||
LeadAssistDashboard,
|
||||
{
|
||||
isTeamsContext: this.isTeamsContext,
|
||||
siteUrl: this.siteUrl,
|
||||
graphClient: this.graphClient
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
|
||||
protected onDispose(): void {
|
||||
ReactDom.unmountComponentAtNode(this.domElement);
|
||||
}
|
||||
|
||||
private async generateSharePointDemoListsClick(): Promise<void> {
|
||||
await DataService.generateDemoLists();
|
||||
}
|
||||
|
||||
private async generateSharePointDemoDataClick(): Promise<void> {
|
||||
await DataService.generateListsDemoData();
|
||||
}
|
||||
|
||||
private async generateGraphDemoDataClick(): Promise<void> {
|
||||
await DataService.generateGraphDemoData();
|
||||
}
|
||||
|
||||
private async deleteSharePointDemoListsClick(): Promise<void> {
|
||||
await DataService.deleteSharePointDemoLists();
|
||||
}
|
||||
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.GenerateDemoDataGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneButton('GenerateSharePointDemoLists',
|
||||
{
|
||||
text: strings.GenerateSharePointDemoListsButton,
|
||||
buttonType: PropertyPaneButtonType.Normal,
|
||||
onClick: this.generateSharePointDemoListsClick.bind(this),
|
||||
disabled: this.buttonsAreDisabled()
|
||||
}),
|
||||
PropertyPaneButton('GenerateSharePointDemoData',
|
||||
{
|
||||
text: strings.GenerateSharePointDemoDataButton,
|
||||
buttonType: PropertyPaneButtonType.Normal,
|
||||
onClick: this.generateSharePointDemoDataClick.bind(this),
|
||||
disabled: this.buttonsAreDisabled()
|
||||
}),
|
||||
PropertyPaneButton('GenerateGraphDemoData',
|
||||
{
|
||||
text: strings.GenerateGraphDemoDataButton,
|
||||
buttonType: PropertyPaneButtonType.Normal,
|
||||
onClick: this.generateGraphDemoDataClick.bind(this),
|
||||
disabled: this.buttonsAreDisabled()
|
||||
})
|
||||
]
|
||||
},
|
||||
{
|
||||
groupName: strings.CleanDemoDataGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneButton('DeleteSharePointDemoLists',
|
||||
{
|
||||
text: strings.DeleteDemoDataButton,
|
||||
buttonType: PropertyPaneButtonType.Normal,
|
||||
onClick: this.deleteSharePointDemoListsClick.bind(this),
|
||||
disabled: this.buttonsAreDisabled()
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
import * as React from 'react';
|
||||
import * as strings from 'LeadAssistDashboardWebPartStrings';
|
||||
import styles from './LeadAssistDashboard.module.scss';
|
||||
import { MgtTemplateProps } from '@microsoft/mgt-react/dist/es6/spfx';
|
||||
import { Separator, Spinner } from 'office-ui-fabric-react';
|
||||
import DataService from '../../../services/DataService';
|
||||
|
||||
export default class CustomAgendaTemplate {
|
||||
/**
|
||||
* Template to display a single event
|
||||
* @param props
|
||||
* @returns The element that display a single event
|
||||
*/
|
||||
public static eventTemplate = (props: MgtTemplateProps): JSX.Element => {
|
||||
const { event } = props.dataContext;
|
||||
|
||||
let eventLocation: string = "";
|
||||
let isTeamsMeeting: boolean = false;
|
||||
|
||||
if (event.isOnlineMeeting == true) {
|
||||
eventLocation = strings.MicrosoftTeams;
|
||||
isTeamsMeeting = true;
|
||||
}
|
||||
else {
|
||||
if (event.location) {
|
||||
if (event.location.displayName && event.location.displayName.length > 0) {
|
||||
eventLocation = event.location.displayName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const startDate: Date = new Date(event.start.dateTime);
|
||||
const endDate: Date = new Date(event.end.dateTime);
|
||||
|
||||
return <div className={styles.event}>
|
||||
<div className={((isTeamsMeeting == true) ? styles.teamsEventBorder : styles.eventBorder)}>
|
||||
<div className={styles.leftPadding}>
|
||||
<div className={styles.eventTitle}>{event.subject}</div>
|
||||
<div>{DataService.getTime(startDate)} - {DataService.getTime(endDate)}</div>
|
||||
<div>{eventLocation}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template to show when events are being loaded
|
||||
* @param props
|
||||
* @returns The element to display the loading state
|
||||
*/
|
||||
public static loadingTemplate = (props: MgtTemplateProps): JSX.Element => {
|
||||
return <Spinner label="Loading..."></Spinner>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Template to show when there are no events available
|
||||
* @param props
|
||||
* @returns The element to display that no event is available
|
||||
*/
|
||||
public static noDataTemplate = (props: MgtTemplateProps): JSX.Element => {
|
||||
return <div>{strings.NoEventFound}</div>;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
import * as React from 'react';
|
||||
import styles from './LeadAssistDashboard.module.scss';
|
||||
import { MgtTemplateProps } from '@microsoft/mgt-react/dist/es6/spfx';
|
||||
import { Separator } from 'office-ui-fabric-react';
|
||||
import DataService from '../../../services/DataService';
|
||||
|
||||
export default class CustomTodoTemplate {
|
||||
/**
|
||||
* Template to display a single todo
|
||||
* @param props
|
||||
* @returns The element that display a single todo
|
||||
*/
|
||||
public static todoTemplate = (props: MgtTemplateProps): JSX.Element => {
|
||||
const { task } = props.dataContext;
|
||||
|
||||
const title: string = task.title;
|
||||
const dueDateTime: Date = (task.dueDateTime) ? new Date(task.dueDateTime.dateTime) : undefined;
|
||||
|
||||
let result: JSX.Element = <div></div>;
|
||||
|
||||
// If task is not completed
|
||||
if (task.status != "completed") {
|
||||
// Set the template
|
||||
result = <div className={styles.task}>
|
||||
<div>
|
||||
<div className={styles.leftPadding}>
|
||||
{dueDateTime &&
|
||||
<div className={styles.taskDateTime}>{DataService.getDate(dueDateTime)}, {DataService.getTime(dueDateTime)}</div>}
|
||||
<div className={styles.taskTitle}>{title}</div>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
</div>;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
import { MSGraphClient } from "@microsoft/sp-http";
|
||||
export interface ILeadAssistDashboardProps {
|
||||
isTeamsContext: boolean;
|
||||
siteUrl: string;
|
||||
graphClient: MSGraphClient;
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
import { ISaleListItem } from "../../../services/ISaleListItem";
|
||||
import { IValueListItem } from "../../../services/IValueListItem";
|
||||
|
||||
export interface ILeadAssistDashboardState {
|
||||
activityCallItems: IValueListItem[];
|
||||
activityEmailItems: IValueListItem[];
|
||||
activityTextItems: IValueListItem[];
|
||||
progressItems: IValueListItem[];
|
||||
recentlyDoneSalesContractItems: ISaleListItem[];
|
||||
isLoading: boolean;
|
||||
listsAreEmpty: boolean;
|
||||
siteUrl: string;
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
@import '../../../common/colors.module.scss';
|
||||
@import '../../../common/Global.dark.module.scss';
|
||||
@import '../../../common/Global.default.module.scss';
|
||||
@import '../../../common/Global.contrast.module.scss';
|
||||
|
||||
:export {
|
||||
callRGBColor: $leadAssistDashboard-callRGBColor;
|
||||
emailRGBColor: $leadAssistDashboard-emailRGBColor;
|
||||
textRGBColor: $leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.leadAssistDashboard {
|
||||
max-width: 1648px;
|
||||
|
||||
.dot {
|
||||
height: 10px;
|
||||
width: 10px;
|
||||
border-radius: 50%;
|
||||
display: inline-block;
|
||||
margin: 2px;
|
||||
}
|
||||
|
||||
.chartCallDot {
|
||||
@extend .dot;
|
||||
background-color: $leadAssistDashboard-callRGBColor;
|
||||
}
|
||||
|
||||
.chartEmailDot {
|
||||
@extend .dot;
|
||||
background-color: $leadAssistDashboard-emailRGBColor;
|
||||
}
|
||||
|
||||
.chartTextDot {
|
||||
@extend .dot;
|
||||
background-color: $leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.chartNumber {
|
||||
font-size: large;
|
||||
}
|
||||
|
||||
.tip {
|
||||
padding: 8px;
|
||||
}
|
||||
|
||||
.grid {
|
||||
@include ms-Grid;
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg10;
|
||||
@include ms-xl8;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.smallColumn {
|
||||
@extend .column;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.smallColumnTeams {
|
||||
@extend .column;
|
||||
max-width: 180px;
|
||||
}
|
||||
|
||||
.chartColumn {
|
||||
@extend .column;
|
||||
max-width: 100px;
|
||||
}
|
||||
|
||||
.chartColumnTeams {
|
||||
@extend .column;
|
||||
max-width: 200px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
|
||||
.event {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.eventBorder {
|
||||
border-left: 4px solid $leadAssistDashboard-eventColor;
|
||||
}
|
||||
|
||||
.teamsEventBorder{
|
||||
@extend .eventBorder;
|
||||
border-left: 4px solid $leadAssistDashboard-eventTeamsColor;
|
||||
}
|
||||
|
||||
.leftPadding{
|
||||
padding-left: 9px;
|
||||
}
|
||||
|
||||
.eventTitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.task {
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.taskTitle {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.taskDateTime {
|
||||
font-weight: lighter;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.paddedContainer {
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.padding5 {
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
.loader {
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.centeredContainer {
|
||||
margin: 0;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
-ms-transform: translateY(-50%);
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.progressDescription {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.marginLeft {
|
||||
margin-left: -30px;
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='default'] {
|
||||
|
||||
:export {
|
||||
callRGBColor: $default-leadAssistDashboard-callRGBColor;
|
||||
emailRGBColor: $default-leadAssistDashboard-emailRGBColor;
|
||||
textRGBColor: $default-leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.leadAssistDashboard {
|
||||
background: $default-leadAssistDashboard-background;
|
||||
color: $default-leadAssistDashboard-color;
|
||||
|
||||
.button {
|
||||
background: $default-leadAssistDashboard-buttonBackground;
|
||||
color: $default-leadAssistDashboard-buttonColor;
|
||||
}
|
||||
|
||||
.chartCallDot {
|
||||
@extend .dot;
|
||||
background-color: $default-leadAssistDashboard-callRGBColor;
|
||||
}
|
||||
|
||||
.chartEmailDot {
|
||||
@extend .dot;
|
||||
background-color: $default-leadAssistDashboard-emailRGBColor;
|
||||
}
|
||||
|
||||
.chartTextDot {
|
||||
@extend .dot;
|
||||
background-color: $default-leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.eventBorder {
|
||||
border-left: 4px solid $default-leadAssistDashboard-eventColor;
|
||||
}
|
||||
|
||||
.teamsEventBorder{
|
||||
@extend .eventBorder;
|
||||
border-left: 4px solid $default-leadAssistDashboard-eventTeamsColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='dark'] {
|
||||
|
||||
:export {
|
||||
callRGBColor: $dark-leadAssistDashboard-callRGBColor;
|
||||
emailRGBColor: $dark-leadAssistDashboard-emailRGBColor;
|
||||
textRGBColor: $dark-leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.leadAssistDashboard {
|
||||
background: $dark-leadAssistDashboard-background;
|
||||
color: $dark-leadAssistDashboard-color;
|
||||
|
||||
.button {
|
||||
background: $dark-leadAssistDashboard-buttonBackground;
|
||||
color: $dark-leadAssistDashboard-buttonColor;
|
||||
}
|
||||
|
||||
.chartCallDot {
|
||||
@extend .dot;
|
||||
background-color: $dark-leadAssistDashboard-callRGBColor;
|
||||
}
|
||||
|
||||
.chartEmailDot {
|
||||
@extend .dot;
|
||||
background-color: $dark-leadAssistDashboard-emailRGBColor;
|
||||
}
|
||||
|
||||
.chartTextDot {
|
||||
@extend .dot;
|
||||
background-color: $dark-leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.eventBorder {
|
||||
border-left: 4px solid $dark-leadAssistDashboard-eventColor;
|
||||
}
|
||||
|
||||
.teamsEventBorder{
|
||||
@extend .eventBorder;
|
||||
border-left: 4px solid $dark-leadAssistDashboard-eventTeamsColor;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[data-theme='contrast'] {
|
||||
|
||||
:export {
|
||||
callRGBColor: $contrast-leadAssistDashboard-callRGBColor;
|
||||
emailRGBColor: $contrast-leadAssistDashboard-emailRGBColor;
|
||||
textRGBColor: $contrast-leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.leadAssistDashboard {
|
||||
background: $contrast-leadAssistDashboard-background;
|
||||
color: $contrast-leadAssistDashboard-color;
|
||||
|
||||
.button {
|
||||
background: $contrast-leadAssistDashboard-buttonBackground;
|
||||
color: $contrast-leadAssistDashboard-buttonColor;
|
||||
}
|
||||
.chartCallDot {
|
||||
@extend .dot;
|
||||
background-color: $contrast-leadAssistDashboard-callRGBColor;
|
||||
}
|
||||
|
||||
.chartEmailDot {
|
||||
@extend .dot;
|
||||
background-color: $contrast-leadAssistDashboard-emailRGBColor;
|
||||
}
|
||||
|
||||
.chartTextDot {
|
||||
@extend .dot;
|
||||
background-color: $contrast-leadAssistDashboard-textRGBColor;
|
||||
}
|
||||
|
||||
.eventBorder {
|
||||
border-left: 4px solid $contrast-leadAssistDashboard-eventColor;
|
||||
}
|
||||
|
||||
.teamsEventBorder{
|
||||
@extend .eventBorder;
|
||||
border-left: 4px solid $contrast-leadAssistDashboard-eventTeamsColor;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,573 @@
|
|||
import * as React from 'react';
|
||||
import styles from './LeadAssistDashboard.module.scss';
|
||||
import { ILeadAssistDashboardProps } from './ILeadAssistDashboardProps';
|
||||
import { ILeadAssistDashboardState } from './ILeadAssistDashboardState';
|
||||
import * as strings from 'LeadAssistDashboardWebPartStrings';
|
||||
import { WidgetSize, Dashboard, IWidget } from '@pnp/spfx-controls-react/lib/Dashboard';
|
||||
import { ChartControl, ChartType } from '@pnp/spfx-controls-react/lib/ChartControl';
|
||||
import { ListView, IViewField, SelectionMode } from "@pnp/spfx-controls-react/lib/ListView";
|
||||
import { IColumn, Icon, Label, Separator, Spinner, SpinnerSize, TextField, initializeIcons, PrimaryButton } from 'office-ui-fabric-react';
|
||||
import { Agenda, Todo } from '@microsoft/mgt-react/dist/es6/spfx';
|
||||
import CustomAgendaTemplate from "./CustomAgendaTemplate";
|
||||
import CustomTodoTemplate from "./CustomTodoTemplate";
|
||||
import DataService from '../../../services/DataService';
|
||||
import SettingsService from '../../../services/SettingsService';
|
||||
import { IValueListItem } from '../../../services/IValueListItem';
|
||||
import { ISaleListItem } from '../../../services/ISaleListItem';
|
||||
|
||||
export default class LeadAssistDashboard extends React.Component<ILeadAssistDashboardProps, ILeadAssistDashboardState> {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
// Initialize Office UI Fabric icons
|
||||
initializeIcons();
|
||||
|
||||
// Initialize the state
|
||||
this.state = {
|
||||
activityCallItems: undefined,
|
||||
activityEmailItems: undefined,
|
||||
activityTextItems: undefined,
|
||||
progressItems: undefined,
|
||||
recentlyDoneSalesContractItems: undefined,
|
||||
isLoading: true,
|
||||
listsAreEmpty: true,
|
||||
siteUrl: props.siteUrl
|
||||
};
|
||||
}
|
||||
|
||||
public async componentDidMount(): Promise<void> {
|
||||
// If a site url has been specified
|
||||
if (this.props.siteUrl && this.props.siteUrl.length > 0) {
|
||||
// If all of the state properties containing SharePoint list items are not defined or are empty load them
|
||||
if ((!this.state.activityCallItems || this.state.activityCallItems.length == 0)
|
||||
&& (!this.state.activityEmailItems || this.state.activityEmailItems.length == 0)
|
||||
&& (!this.state.activityTextItems || this.state.activityTextItems.length == 0)
|
||||
&& (!this.state.progressItems || this.state.progressItems.length == 0)
|
||||
&& (!this.state.recentlyDoneSalesContractItems || this.state.recentlyDoneSalesContractItems.length == 0)) {
|
||||
|
||||
try
|
||||
{
|
||||
// Get the list items
|
||||
const callValues: IValueListItem[] = await DataService.getItemsWithValueFromList(DataService.ActivityCallsListName, (i): IValueListItem => ({ value: i[DataService.FieldValueName] }));
|
||||
const emailValues: IValueListItem[] = await DataService.getItemsWithValueFromList(DataService.ActivityEmailsListName, (i): IValueListItem => ({ value: i[DataService.FieldValueName] }));
|
||||
const textValues: IValueListItem[] = await DataService.getItemsWithValueFromList(DataService.ActivityTextsListName, (i): IValueListItem => ({ value: i[DataService.FieldValueName] }));
|
||||
const progressValues: IValueListItem[] = await DataService.getItemsWithValueFromList(DataService.ProgressListName, (i): IValueListItem => ({ value: i[DataService.FieldValueName] }));
|
||||
const recentlyDoneSalesContractValues: ISaleListItem[] = await DataService.getSalesItemsFromList(DataService.RecentlyDoneSalesContractsListName, (i): ISaleListItem => ({ id: i.Id, title: i[DataService.FieldTitleName], description: i[DataService.FieldDescriptionName]}));
|
||||
|
||||
// Update state
|
||||
this.setState({
|
||||
activityCallItems: callValues,
|
||||
activityEmailItems: emailValues,
|
||||
activityTextItems: textValues,
|
||||
progressItems: progressValues,
|
||||
recentlyDoneSalesContractItems: recentlyDoneSalesContractValues,
|
||||
isLoading: false,
|
||||
listsAreEmpty: callValues.length == 0
|
||||
&& emailValues.length == 0
|
||||
&& textValues.length == 0
|
||||
&& progressValues.length == 0
|
||||
&& recentlyDoneSalesContractValues.length == 0
|
||||
});
|
||||
}
|
||||
catch (e) {
|
||||
console.log(e);
|
||||
this.setState({
|
||||
isLoading: false
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.setState({
|
||||
isLoading: false
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
this.setState({
|
||||
isLoading: false,
|
||||
listsAreEmpty: true
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the change of the site url
|
||||
* @param event
|
||||
*/
|
||||
private changeSiteUrlHandler = async (event) => {
|
||||
const value = event.target.value;
|
||||
|
||||
this.setState({
|
||||
siteUrl: value
|
||||
});
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<ILeadAssistDashboardProps> {
|
||||
let content: JSX.Element = null;
|
||||
|
||||
// If the site url has been specified
|
||||
if (this.props.siteUrl && this.props.siteUrl.length > 0) {
|
||||
// If the state properties containing the SharePoint list items data are specified
|
||||
if (this.state.activityCallItems && this.state.activityEmailItems && this.state.activityTextItems && this.state.progressItems && this.state.recentlyDoneSalesContractItems) {
|
||||
// If the SharePoint lists are empty
|
||||
if (this.state.listsAreEmpty == true) {
|
||||
// Specify that no item has been found
|
||||
content = <div className={styles.paddedContainer}>
|
||||
<b>{strings.NoListItemsFound}</b>
|
||||
</div>;
|
||||
}
|
||||
else {
|
||||
// Create the dashboard element
|
||||
content = <Dashboard widgets={this.getDashboardWidgets()} />;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Specify that the SharePoint lists are missing
|
||||
content = <div className={styles.paddedContainer}>
|
||||
<b>{strings.CreateListsLabel}</b>
|
||||
</div>;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// If the site url has not been yet specified returns a form to specify it
|
||||
content = <div className={styles.paddedContainer}>
|
||||
<div className={styles.padding5}>
|
||||
<Label>{strings.TargetSiteUrl}</Label>
|
||||
<TextField value={this.props.siteUrl} onChange={this.changeSiteUrlHandler} />
|
||||
</div>
|
||||
<div className={styles.padding5}>
|
||||
<PrimaryButton text={strings.SaveConfiguration} onClick={() => { SettingsService.saveSiteUrl(this.props.graphClient, { siteUrl: this.state.siteUrl }); }} />
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={styles.leadAssistDashboard}>
|
||||
{this.state.isLoading && <Spinner size={SpinnerSize.large} title={strings.Loading} className={styles.loader} />}
|
||||
{!this.state.isLoading && content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dashboard widgets
|
||||
* @returns An array of the widgets to be added to the dashboard element
|
||||
*/
|
||||
private getDashboardWidgets() : IWidget[] {
|
||||
return [{
|
||||
title: strings.ActivityChartTitle,
|
||||
size: WidgetSize.Double,
|
||||
body: [
|
||||
{
|
||||
id: "activityChartTab",
|
||||
title: strings.ActivityChartTitle,
|
||||
content: (
|
||||
this.getActivityChartTab()
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: strings.ProgressChartTitle,
|
||||
size: this.props.isTeamsContext ? WidgetSize.Double : WidgetSize.Single,
|
||||
body: [
|
||||
{
|
||||
id: "progressChartTab",
|
||||
title: strings.ProgressChartTitle,
|
||||
content: (
|
||||
this.getProgressChartTab()
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: strings.MyDayTitle,
|
||||
size: WidgetSize.Single,
|
||||
body: [
|
||||
{
|
||||
id: "myDayTab",
|
||||
title: strings.MyDayTitle,
|
||||
content: (
|
||||
this.getMyDayTab()
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: strings.RecentlyDoneSalesTitle,
|
||||
size: this.props.isTeamsContext ? WidgetSize.Double : WidgetSize.Single,
|
||||
body: [
|
||||
{
|
||||
id: "recentlyDoneSalesTab",
|
||||
title: strings.RecentlyDoneSalesTitle,
|
||||
content: (
|
||||
this.getRecentlyDoneSalesTab()
|
||||
)
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: strings.ToDoTitle,
|
||||
size: WidgetSize.Single,
|
||||
body: [
|
||||
{
|
||||
id: "toDoTab",
|
||||
title: strings.ToDoTitle,
|
||||
content: (
|
||||
this.getToDoTab()
|
||||
)
|
||||
}
|
||||
]
|
||||
}];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content for the Activity chart widget
|
||||
* @returns Element representing the Activity chart tab
|
||||
*/
|
||||
private getActivityChartTab() {
|
||||
const data = this.getActivityData();
|
||||
|
||||
// Options for the chart element
|
||||
const options = {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false
|
||||
};
|
||||
|
||||
const accessibility = {
|
||||
enable: true
|
||||
};
|
||||
|
||||
// Get the total for each category
|
||||
const callsTotal = data.datasets[0].data.reduce((sum, current) => sum + current, 0);
|
||||
const emailsTotal = data.datasets[1].data.reduce((sum, current) => sum + current, 0);
|
||||
const textsTotal = data.datasets[2].data.reduce((sum, current) => sum + current, 0);
|
||||
|
||||
return <div>
|
||||
<div className={styles.grid} dir="ltr">
|
||||
<div className={`${styles.row} ${styles.padding5}`}>
|
||||
<div className={(this.props.isTeamsContext) ? styles.smallColumnTeams : styles.smallColumn}>
|
||||
<div>
|
||||
<span className={styles.chartCallDot}></span>
|
||||
{strings.ActivityChartLegendCalls}
|
||||
</div>
|
||||
<div className={styles.chartNumber}>
|
||||
<b>{callsTotal}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.smallColumn}>
|
||||
<div>
|
||||
<span className={styles.chartEmailDot}></span>
|
||||
{strings.ActivityChartLegendEmails}
|
||||
</div>
|
||||
<div className={styles.chartNumber}>
|
||||
<b>{emailsTotal}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.smallColumn}>
|
||||
<div>
|
||||
<span className={styles.chartTextDot}></span>
|
||||
{strings.ActivityChartLegendTexts}
|
||||
</div>
|
||||
<div className={styles.chartNumber}>
|
||||
<b>{textsTotal}</b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<ChartControl
|
||||
type={ChartType.Line}
|
||||
data={data}
|
||||
accessibility={accessibility}
|
||||
options={options}
|
||||
loadingtemplate={() => <Spinner size={SpinnerSize.large} label={strings.Loading} />}
|
||||
/>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content for the Progress chart widget
|
||||
* @returns Element representing the Progress chart tab
|
||||
*/
|
||||
private getProgressChartTab() {
|
||||
// Get data
|
||||
const data1 = this.getProgressData(0);
|
||||
const data2 = this.getProgressData(1);
|
||||
const data3 = this.getProgressData(2);
|
||||
|
||||
// Options for the chart element
|
||||
const options = {
|
||||
legend: {
|
||||
display: false,
|
||||
},
|
||||
title: {
|
||||
display: false
|
||||
},
|
||||
responsive: true,
|
||||
maintainAspectRatio: false,
|
||||
circumference: Math.PI,
|
||||
rotation: Math.PI
|
||||
};
|
||||
|
||||
const accessibility = {
|
||||
enable: true
|
||||
};
|
||||
|
||||
return <div className={styles.centeredContainer}>
|
||||
<div className={styles.grid} dir="ltr">
|
||||
<div className={`${styles.row} ${styles.marginLeft}`}>
|
||||
<div className={(this.props.isTeamsContext == true) ? styles.chartColumnTeams : styles.chartColumn}>
|
||||
<ChartControl
|
||||
type={ChartType.Doughnut}
|
||||
data={data1}
|
||||
accessibility={accessibility}
|
||||
options={options}
|
||||
loadingtemplate={() => <Spinner size={SpinnerSize.large} label={strings.Loading} />}
|
||||
/>
|
||||
<div className={styles.progressDescription}>
|
||||
<b>{strings.ProgressSales}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className={(this.props.isTeamsContext == true) ? styles.chartColumnTeams : styles.chartColumn}>
|
||||
<ChartControl
|
||||
type={ChartType.Doughnut}
|
||||
data={data2}
|
||||
accessibility={accessibility}
|
||||
options={options}
|
||||
loadingtemplate={() => <Spinner size={SpinnerSize.large} label={strings.Loading} />}
|
||||
/>
|
||||
<div className={styles.progressDescription}>
|
||||
<b>{strings.ProgressRevenues}</b>
|
||||
</div>
|
||||
</div>
|
||||
<div className={(this.props.isTeamsContext == true) ? styles.chartColumnTeams : styles.chartColumn}>
|
||||
<ChartControl
|
||||
type={ChartType.Doughnut}
|
||||
data={data3}
|
||||
accessibility={accessibility}
|
||||
options={options}
|
||||
loadingtemplate={() => <Spinner size={SpinnerSize.large} label={strings.Loading} />}
|
||||
/>
|
||||
<div className={styles.progressDescription}>
|
||||
<b>{strings.ProgressMarketShare}</b>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Separator />
|
||||
<div className={styles.tip}>
|
||||
{strings.ProgressChartTip}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content for the My Day widget
|
||||
* @returns Element representing the My Day tab
|
||||
*/
|
||||
private getMyDayTab() {
|
||||
return <Agenda
|
||||
days={30}>
|
||||
<CustomAgendaTemplate.loadingTemplate template="loading" />
|
||||
<CustomAgendaTemplate.eventTemplate template="event" />
|
||||
<CustomAgendaTemplate.noDataTemplate template="no-data" />
|
||||
</Agenda>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content for the Recently done sales widget
|
||||
* @returns Element representing the Recently done sales tab
|
||||
*/
|
||||
private getRecentlyDoneSalesTab() {
|
||||
// Get items to display
|
||||
const items = this.state.recentlyDoneSalesContractItems;
|
||||
|
||||
// Define the columns of the ListView element
|
||||
const viewFields: IViewField[] = [
|
||||
{
|
||||
name: "title",
|
||||
displayName: strings.RecentlyDoneSalesViewTitle,
|
||||
sorting: true,
|
||||
isResizable: true,
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
name: "description",
|
||||
displayName: strings.RecentlyDoneSalesViewDescription,
|
||||
sorting: true,
|
||||
isResizable: true,
|
||||
minWidth: 100
|
||||
},
|
||||
{
|
||||
name: "buttonColumn",
|
||||
displayName: " ",
|
||||
minWidth: 50,
|
||||
render: (item?: any, index?: number, column?: IColumn) => {
|
||||
var content = <div></div>;
|
||||
// If the element exists
|
||||
if (item) {
|
||||
// Create an clickable icon to open the SharePoint list item
|
||||
content = <Icon iconName={'Link'}
|
||||
onMouseOver={() => {}}
|
||||
onClick={() => {
|
||||
const addSlash = this.props.siteUrl.endsWith("/") == false;
|
||||
|
||||
var tempLink = document.createElement('a');
|
||||
tempLink.href = this.props.siteUrl + ((addSlash == true) ? "/" : "") + "Lists/" + DataService.RecentlyDoneSalesContractsListName + "/DispForm.aspx?ID=" + item.id;
|
||||
tempLink.target = "_blank";
|
||||
tempLink.click();
|
||||
}} />;
|
||||
}
|
||||
return content;
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
return <div>
|
||||
<ListView
|
||||
items={items}
|
||||
viewFields={viewFields}
|
||||
iconFieldName="ServerRelativeUrl"
|
||||
compact={true}
|
||||
selectionMode={SelectionMode.none}
|
||||
showFilter={false}
|
||||
filterPlaceHolder={strings.RecentlyDoneSalesViewFilterPlaceHolder}
|
||||
sortItems={this.sortItems}
|
||||
stickyHeader={true} />
|
||||
</div>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the content for the ToDo widget
|
||||
* @returns Element representing the ToDo tab
|
||||
*/
|
||||
private getToDoTab() {
|
||||
return <Todo hideHeader={true}>
|
||||
<CustomTodoTemplate.todoTemplate template="task" />
|
||||
</Todo>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the data to be displayed in the Activity widget
|
||||
* @returns Datasets for the chart control in the Activity widget
|
||||
*/
|
||||
private getActivityData() {
|
||||
return {
|
||||
labels: DataService.MonthNames,
|
||||
datasets: [
|
||||
{
|
||||
label: strings.ActivityChartLegendCalls,
|
||||
fill: false,
|
||||
data: this.state.activityCallItems.map(i => i.value),
|
||||
backgroundColor: styles.callRGBColor,
|
||||
borderColor: styles.callRGBColor,
|
||||
borderWidth: 3
|
||||
},
|
||||
{
|
||||
label: strings.ActivityChartLegendEmails,
|
||||
fill: false,
|
||||
data: this.state.activityEmailItems.map(i => i.value),
|
||||
backgroundColor: styles.emailRGBColor,
|
||||
borderColor: styles.emailRGBColor,
|
||||
borderWidth: 3
|
||||
},
|
||||
{
|
||||
label: strings.ActivityChartLegendTexts,
|
||||
fill: false,
|
||||
data: this.state.activityTextItems.map(i => i.value),
|
||||
backgroundColor: styles.textRGBColor,
|
||||
borderColor: styles.textRGBColor,
|
||||
borderWidth: 3
|
||||
},
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data for the Progress widget
|
||||
* @param index Index to differentiate the chunks, available values are 0,1,2
|
||||
* @returns
|
||||
*/
|
||||
private getProgressData(index: number) {
|
||||
const totalLength = this.state.progressItems.length;
|
||||
// Get the chunk length
|
||||
const chunk = totalLength / 3;
|
||||
// Get the items of the chunk
|
||||
const items = this.state.progressItems.slice(chunk * index, chunk * (index + 1));
|
||||
|
||||
return {
|
||||
labels: DataService.MonthNames,
|
||||
datasets: [
|
||||
{
|
||||
label: strings.ProgressChartTitle,
|
||||
data: items.map(i => i.value)
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort the specified items for a specific column
|
||||
* @param items Items to be sorted
|
||||
* @param columnName Column used for sorting
|
||||
* @param descending Specify if the sorting is descending
|
||||
* @returns Input items sorted for the specified column
|
||||
*/
|
||||
private sortItems = (items: any[], columnName: string, descending: boolean): any[] => {
|
||||
let properties: string[];
|
||||
|
||||
// Support for nested properties
|
||||
if (columnName.toString().indexOf(".") > 0) {
|
||||
properties = columnName.toString().split(".");
|
||||
}
|
||||
|
||||
if (!items || items.length == 0) {
|
||||
return items;
|
||||
}
|
||||
|
||||
return items.sort((a, b) => {
|
||||
if (a === null || a === undefined) {
|
||||
return 1;
|
||||
}
|
||||
else if (b === null || b === undefined) {
|
||||
return -1;
|
||||
}
|
||||
else if (a === b) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var valueA = a[columnName];
|
||||
var valueB = b[columnName];
|
||||
|
||||
// If it's a complex property
|
||||
if (properties && properties.length > 0) {
|
||||
valueA = a[properties[0]][properties[1]];
|
||||
valueB = b[properties[0]][properties[1]];
|
||||
}
|
||||
|
||||
var dateValueB = new Date(valueB.toString());
|
||||
var dateValueA = new Date(valueA.toString());
|
||||
|
||||
// Check if the value is a date
|
||||
if ((Object.prototype.toString.call(dateValueA) === "[object Date]" && !isNaN(dateValueA.getTime()))
|
||||
&& (Object.prototype.toString.call(dateValueB) === "[object Date]" && !isNaN(dateValueB.getTime()))) {
|
||||
if (descending) {
|
||||
return (dateValueB.getTime() - dateValueA.getTime()) > 0 ? 1 : -1;
|
||||
}
|
||||
else {
|
||||
return (dateValueA.getTime() - dateValueB.getTime()) > 0 ? 1 : -1;
|
||||
}
|
||||
}
|
||||
|
||||
return (descending ? valueA < valueB : valueA > valueB) ? -1 : 1;
|
||||
});
|
||||
}
|
||||
}
|
79
samples/react-teams-lead-dashboard/src/webparts/leadAssistDashboard/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,79 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Use the buttons to populate or delete demo data.",
|
||||
"GenerateDemoDataGroupName": "Generate demo data",
|
||||
"CleanDemoDataGroupName": "Remove demo data",
|
||||
"GenerateSharePointDemoListsButton": "Create SharePoint demo lists",
|
||||
"GenerateSharePointDemoDataButton": "SharePoint demo data",
|
||||
"GenerateGraphDemoDataButton": "Graph demo data",
|
||||
"DeleteDemoDataButton": "Clean SharePoint demo lists",
|
||||
"ConfirmCreateDemoLists": "Confirm to create demo lists?",
|
||||
"ConfirmAddDemoData": "Confirm to add demo data to the demo lists?",
|
||||
"ConfirmAddGraphDemoData": "Confirm to add demo data via Microsoft Graph?",
|
||||
"ConfirmDeleteSharePointDemoLists": "Confirm to delete all the SharePoint demo lists and their contents?",
|
||||
"DemoListsCreated": "Demo lists created.",
|
||||
"DemoDataGenerated": "Demo data added to the demo lists.",
|
||||
"GraphDemoDataGenerated": "Demo data added via Microsoft Graph.",
|
||||
"SharePointDemoListsDeleted": "SharePoint demo lists deleted.",
|
||||
|
||||
"ActivityChartTitle": "Activity overview",
|
||||
"ActivityChartLegendCalls": "Team calls",
|
||||
"ActivityChartLegendEmails": "Emails",
|
||||
"ActivityChartLegendTexts": "Texts",
|
||||
|
||||
"ProgressChartTitle": "Progress",
|
||||
"ProgressChartTip": "Tip: You would have to talk to 15 leads per day to achieve your sales goals per your current lead conversion rate.",
|
||||
"ProgressSales": "Sales",
|
||||
"ProgressRevenues": "Revenues",
|
||||
"ProgressMarketShare": "Market Share",
|
||||
|
||||
"MyDayTitle": "My day",
|
||||
|
||||
"RecentlyDoneSalesTitle": "Open leads",
|
||||
"RecentlyDoneSalesSeeAll": "See all",
|
||||
"RecentlyDoneSalesViewTitle": "Title",
|
||||
"RecentlyDoneSalesViewDescription": "Description",
|
||||
"RecentlyDoneSalesViewFilterPlaceHolder": "Search",
|
||||
|
||||
"ToDoTitle": "To-do",
|
||||
"ToDoSeeAll": "See all",
|
||||
|
||||
"MicrosoftTeams": "Microsoft Teams",
|
||||
"Loading": "Loading...",
|
||||
"NoEventFound": "No event found",
|
||||
"NoListItemsFound": "No list items found, populate the lists via the web part property pane and then refresh the page.",
|
||||
"CreateListsLabel": "No lists found, create the demo lists via the web part property pane to use the Dashboard.",
|
||||
|
||||
"DemoDocumentTitle": "Test",
|
||||
"DemoDocumentDescription": "Test description",
|
||||
"DemoActivityCallTitle": "Call",
|
||||
"DemoActivityEmailTitle": "Email",
|
||||
"DemoActivityTextTitle": "Text",
|
||||
"DemoTaskTitle": "Demo task",
|
||||
"DemoTaskWithDateTitle": "Demo task with date",
|
||||
"DemoApplicationName": "Lead Assist Dashboard",
|
||||
"DemoDisplayName": "PnP",
|
||||
"DemoEventTitle": "Demo event",
|
||||
"DemoEventContent": "This is a demo event",
|
||||
"DemoEventLocation": "Office",
|
||||
"DemoEventOnlineTitle": "Online demo event",
|
||||
"DemoEventOnlineContent": "This is an online demo event",
|
||||
|
||||
"ActivityCallsListName": "Activity Teams Calls Demo Data",
|
||||
"ActivityEmailsListName": "Activity Emails Demo Data",
|
||||
"ActivityTextsListName": "Activity Texts Demo Data",
|
||||
"ProgressListName": "Progress Demo Data",
|
||||
"RecentlyDoneSalesContractsListName": "Recently Done Sales Contracts Demo Data",
|
||||
|
||||
"TargetSiteUrl": "Specify target site URL",
|
||||
"SaveConfiguration": "Save",
|
||||
|
||||
"January": "January",
|
||||
"February": "February",
|
||||
"March": "March",
|
||||
"April": "April",
|
||||
"May": "May",
|
||||
"June": "June",
|
||||
"July": "July"
|
||||
}
|
||||
});
|
82
samples/react-teams-lead-dashboard/src/webparts/leadAssistDashboard/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,82 @@
|
|||
declare interface ILeadAssistDashboardWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
GenerateDemoDataGroupName: string;
|
||||
CleanDemoDataGroupName: string;
|
||||
GenerateSharePointDemoListsButton: string;
|
||||
GenerateSharePointDemoDataButton: string;
|
||||
GenerateGraphDemoDataButton: string;
|
||||
DeleteDemoDataButton: string;
|
||||
ConfirmCreateDemoLists: string;
|
||||
ConfirmAddDemoData: string;
|
||||
ConfirmAddGraphDemoData: string;
|
||||
ConfirmDeleteSharePointDemoLists: string;
|
||||
DemoListsCreated: string;
|
||||
DemoDataGenerated: string;
|
||||
GraphDemoDataGenerated: string;
|
||||
SharePointDemoListsDeleted: string;
|
||||
|
||||
ActivityChartTitle: string;
|
||||
ActivityChartLegendCalls: string;
|
||||
ActivityChartLegendEmails: string;
|
||||
ActivityChartLegendTexts: string;
|
||||
|
||||
ProgressChartTitle: string;
|
||||
ProgressChartTip: string;
|
||||
ProgressSales: string;
|
||||
ProgressRevenues: string;
|
||||
ProgressMarketShare: string;
|
||||
|
||||
MyDayTitle: string;
|
||||
|
||||
RecentlyDoneSalesTitle: string;
|
||||
RecentlyDoneSalesSeeAll: string;
|
||||
RecentlyDoneSalesViewTitle: string;
|
||||
RecentlyDoneSalesViewDescription: string;
|
||||
RecentlyDoneSalesViewFilterPlaceHolder: string;
|
||||
|
||||
ToDoTitle: string;
|
||||
ToDoSeeAll: string;
|
||||
|
||||
MicrosoftTeams: string;
|
||||
Loading: string;
|
||||
NoEventFound: string;
|
||||
NoListItemsFound: string;
|
||||
CreateListsLabel: string;
|
||||
|
||||
DemoDocumentTitle: string;
|
||||
DemoDocumentDescription: string;
|
||||
DemoActivityCallTitle: string;
|
||||
DemoActivityEmailTitle: string;
|
||||
DemoActivityTextTitle: string;
|
||||
DemoTaskTitle: string;
|
||||
DemoTaskWithDateTitle: string;
|
||||
DemoApplicationName: string;
|
||||
DemoDisplayName: string;
|
||||
DemoEventTitle: string;
|
||||
DemoEventContent: string;
|
||||
DemoEventLocation: string;
|
||||
DemoEventOnlineTitle: string;
|
||||
DemoEventOnlineContent: string;
|
||||
|
||||
ActivityCallsListName: string;
|
||||
ActivityEmailsListName: string;
|
||||
ActivityTextsListName: string;
|
||||
ProgressListName: string;
|
||||
RecentlyDoneSalesContractsListName: string;
|
||||
|
||||
TargetSiteUrl: string;
|
||||
SaveConfiguration: string;
|
||||
|
||||
January: string;
|
||||
February: string;
|
||||
March: string;
|
||||
April: string;
|
||||
May: string;
|
||||
June: string;
|
||||
July: string;
|
||||
}
|
||||
|
||||
declare module 'LeadAssistDashboardWebPartStrings' {
|
||||
const strings: ILeadAssistDashboardWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "9899641b-7026-46c6-92a3-66f052075a82",
|
||||
"alias": "LeadAssistDashboardSettingsWebPart",
|
||||
"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": [ "TeamsPersonalApp" ],
|
||||
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70", // Other
|
||||
"group": { "default": "Other" },
|
||||
"title": { "default": "Lead Assist Dashboard Settings" },
|
||||
"description": { "default": "Lead Assist Dashboard Settings" },
|
||||
"officeFabricIconFontName": "Settings",
|
||||
"properties": {
|
||||
"description": "LeadAssistDashboardSettings"
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,91 @@
|
|||
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 { sp } from "@pnp/sp/presets/all";
|
||||
import * as strings from 'LeadAssistDashboardSettingsWebPartStrings';
|
||||
import LeadAssistDashboardSettings from './components/LeadAssistDashboardSettings';
|
||||
import { ILeadAssistDashboardSettingsProps } from './components/ILeadAssistDashboardSettingsProps';
|
||||
import SettingsService from '../../services/SettingsService';
|
||||
|
||||
export interface ILeadAssistDashboardSettingsWebPartProps {
|
||||
}
|
||||
|
||||
export default class LeadAssistDashboardSettingsWebPart extends BaseClientSideWebPart<ILeadAssistDashboardSettingsWebPartProps> {
|
||||
private siteUrl: string;
|
||||
|
||||
protected async onInit() {
|
||||
if (this.context.sdks.microsoftTeams != undefined) {
|
||||
const teamsContext = this.context.sdks.microsoftTeams.context;
|
||||
this.applyTheme(teamsContext.theme || 'default');
|
||||
this.context.sdks.microsoftTeams.teamsJs.registerOnThemeChangeHandler(this.applyTheme);
|
||||
}
|
||||
|
||||
const graphClient = await this.context.msGraphClientFactory.getClient();
|
||||
|
||||
// Get the settings
|
||||
const settings = await SettingsService.getSettings(graphClient, this.context.httpClient);
|
||||
|
||||
// If there are settings specified
|
||||
if (settings) {
|
||||
// Get the site URL
|
||||
this.siteUrl = settings.siteUrl;
|
||||
}
|
||||
|
||||
// If site url has been specified
|
||||
if (this.siteUrl && this.siteUrl.length > 0) {
|
||||
// Setup the SharePoint client
|
||||
sp.setup({
|
||||
spfxContext: this.context,
|
||||
sp: {
|
||||
baseUrl: this.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<ILeadAssistDashboardSettingsProps> = React.createElement(
|
||||
LeadAssistDashboardSettings,
|
||||
{}
|
||||
);
|
||||
|
||||
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: [
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
import { MSGraphClient } from "@microsoft/sp-http";
|
||||
|
||||
export interface ILeadAssistDashboardSettingsProps {
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
@import '~office-ui-fabric-react/dist/sass/References.scss';
|
||||
|
||||
.leadAssistDashboardSettings {
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
}
|
||||
|
||||
.buttonRow {
|
||||
margin: 3px;
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,83 @@
|
|||
import * as React from 'react';
|
||||
import styles from './LeadAssistDashboardSettings.module.scss';
|
||||
import { ILeadAssistDashboardSettingsProps } from './ILeadAssistDashboardSettingsProps';
|
||||
import DataService from '../../../services/DataService';
|
||||
import * as strings from 'LeadAssistDashboardWebPartStrings';
|
||||
import * as settingsStrings from 'LeadAssistDashboardSettingsWebPartStrings';
|
||||
import { PrimaryButton } from 'office-ui-fabric-react';
|
||||
import { WidgetSize, Dashboard, IWidget } from '@pnp/spfx-controls-react/lib/Dashboard';
|
||||
|
||||
export default class LeadAssistDashboardSettings extends React.Component<ILeadAssistDashboardSettingsProps, {}> {
|
||||
private async generateSharePointDemoListsClick(): Promise<void> {
|
||||
await DataService.generateDemoLists();
|
||||
}
|
||||
|
||||
private async generateSharePointDemoDataClick(): Promise<void> {
|
||||
await DataService.generateListsDemoData();
|
||||
}
|
||||
|
||||
private async generateGraphDemoDataClick(): Promise<void> {
|
||||
await DataService.generateGraphDemoData();
|
||||
}
|
||||
|
||||
private async deleteSharePointDemoListsClick(): Promise<void> {
|
||||
await DataService.deleteSharePointDemoLists();
|
||||
}
|
||||
|
||||
public render(): React.ReactElement<ILeadAssistDashboardSettingsProps> {
|
||||
const content: JSX.Element = <Dashboard widgets={this.getDashboardWidgets()} />;
|
||||
return (
|
||||
<div className={ styles.leadAssistDashboardSettings }>
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the dashboard widgets
|
||||
* @returns An array of the widgets to be added to the dashboard element
|
||||
*/
|
||||
private getDashboardWidgets() : IWidget[] {
|
||||
return [{
|
||||
title: settingsStrings.DataSettingsTabTitle,
|
||||
size: WidgetSize.Single,
|
||||
body: [
|
||||
{
|
||||
id: "settingsTab",
|
||||
title: settingsStrings.DataSettingsTabTitle,
|
||||
content: (
|
||||
<div className={ styles.container }>
|
||||
<div>
|
||||
{strings.PropertyPaneDescription}
|
||||
</div>
|
||||
<br />
|
||||
<div>
|
||||
{strings.GenerateDemoDataGroupName}
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.buttonRow}>
|
||||
<PrimaryButton text={strings.GenerateSharePointDemoListsButton} onClick={this.generateSharePointDemoListsClick.bind(this)} />
|
||||
</div>
|
||||
<div className={styles.buttonRow}>
|
||||
<PrimaryButton text={strings.GenerateSharePointDemoDataButton} onClick={this.generateSharePointDemoDataClick.bind(this)} />
|
||||
</div>
|
||||
<div className={styles.buttonRow}>
|
||||
<PrimaryButton text={strings.GenerateGraphDemoDataButton} onClick={this.generateGraphDemoDataClick.bind(this)} />
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div>
|
||||
{strings.CleanDemoDataGroupName}
|
||||
</div>
|
||||
<div>
|
||||
<div className={styles.buttonRow}>
|
||||
<PrimaryButton text={strings.DeleteDemoDataButton} onClick={this.deleteSharePointDemoListsClick.bind(this)} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
]
|
||||
}];
|
||||
}
|
||||
}
|
7
samples/react-teams-lead-dashboard/src/webparts/leadAssistDashboardSettings/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
define([], function() {
|
||||
return {
|
||||
"PropertyPaneDescription": "Description",
|
||||
"BasicGroupName": "Group Name",
|
||||
"DataSettingsTabTitle": "Demo data settings"
|
||||
}
|
||||
});
|
10
samples/react-teams-lead-dashboard/src/webparts/leadAssistDashboardSettings/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
declare interface ILeadAssistDashboardSettingsWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DataSettingsTabTitle: string;
|
||||
}
|
||||
|
||||
declare module 'LeadAssistDashboardSettingsWebPartStrings' {
|
||||
const strings: ILeadAssistDashboardSettingsWebPartStrings;
|
||||
export = strings;
|
||||
}
|
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 383 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 383 B |
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,63 @@
|
|||
{
|
||||
"$schema": "https://developer.microsoft.com/en-us/json-schemas/teams/v1.9/MicrosoftTeams.schema.json",
|
||||
"manifestVersion": "1.9",
|
||||
"version": "1.0.0",
|
||||
"showLoadingIndicator": false,
|
||||
"id": "1c198bf5-aae4-497a-ac0f-74b78a77b0aa",
|
||||
"packageName": "com.pnp.leadassistdashboard",
|
||||
"developer": {
|
||||
"name": "PnP",
|
||||
"websiteUrl": "https://pnp.github.io/",
|
||||
"privacyUrl": "https://pnp.github.io/",
|
||||
"termsOfUseUrl": "https://pnp.github.io/"
|
||||
},
|
||||
"icons": {
|
||||
"color": "color.png",
|
||||
"outline": "outline.png"
|
||||
},
|
||||
"name": {
|
||||
"short": "PnP Lead Assist Dashboard",
|
||||
"full": "PnP Lead Assist Dashboard"
|
||||
},
|
||||
"description": {
|
||||
"short": "Example short description",
|
||||
"full": "Exampe full description"
|
||||
},
|
||||
"accentColor": "#FFFFFF",
|
||||
"staticTabs": [
|
||||
{
|
||||
"entityId": "70001",
|
||||
"name": "Dashboard",
|
||||
"contentUrl": "https://{teamSiteDomain}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/_layouts/15/teamshostedapp.aspx%3Fteams%26personal%26componentId=29c7e411-c4ec-4592-8b24-dbfd8bd1e1ca%26forceLocale={locale}",
|
||||
"scopes": [
|
||||
"personal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entityId": "70002",
|
||||
"name": "Settings",
|
||||
"contentUrl": "https://{teamSiteDomain}/_layouts/15/TeamsLogon.aspx?SPFX=true&dest=/_layouts/15/teamshostedapp.aspx%3Fteams%26personal%26componentId=9899641b-7026-46c6-92a3-66f052075a82%26forceLocale={locale}",
|
||||
"scopes": [
|
||||
"personal"
|
||||
]
|
||||
},
|
||||
{
|
||||
"entityId": "about",
|
||||
"scopes": [
|
||||
"personal"
|
||||
]
|
||||
}
|
||||
],
|
||||
"permissions": [
|
||||
"identity"
|
||||
],
|
||||
"validDomains": [
|
||||
"piasysdev.sharepoint.com",
|
||||
"*.login.microsoftonline.com",
|
||||
"*.sharepoint-df.com",
|
||||
"spoppe-a.akamaihd.net",
|
||||
"spoprod-a.akamaihd.net",
|
||||
"resourceseng.blob.core.windows.net",
|
||||
"msft.spoppe.com"
|
||||
]
|
||||
}
|
After Width: | Height: | Size: 383 B |
|
@ -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"
|
||||
]
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|