Added the Aggregated Calendar Webpart using React framework (#566)
* Initial Release * Updated Readme * Update Read Me * Updated readme with technolgies
This commit is contained in:
parent
293bbba17a
commit
ea3b8efe1e
|
@ -0,0 +1,25 @@
|
|||
# EditorConfig helps developers define and maintain consistent
|
||||
# coding styles between different editors and IDEs
|
||||
# editorconfig.org
|
||||
|
||||
root = true
|
||||
|
||||
|
||||
[*]
|
||||
|
||||
# change these settings to your own preference
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
# we recommend you to keep these unchanged
|
||||
end_of_line = lf
|
||||
charset = utf-8
|
||||
trim_trailing_whitespace = true
|
||||
insert_final_newline = true
|
||||
|
||||
[*.md]
|
||||
trim_trailing_whitespace = false
|
||||
|
||||
[{package,bower}.json]
|
||||
indent_style = space
|
||||
indent_size = 2
|
|
@ -0,0 +1,32 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
|
||||
# Dependency directories
|
||||
node_modules
|
||||
|
||||
# Build generated files
|
||||
dist
|
||||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# OSX
|
||||
.DS_Store
|
||||
|
||||
# Visual Studio files
|
||||
.ntvs_analysis.dat
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
|
||||
# Resx Generated Code
|
||||
*.resx.ts
|
||||
|
||||
# Styles Generated Code
|
||||
*.scss.ts
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"@microsoft/generator-sharepoint": {
|
||||
"version": "1.4.1",
|
||||
"libraryName": "react-aggregated-calendar",
|
||||
"libraryId": "e904a5f2-6d72-4ac2-96d8-5679cb4c7384",
|
||||
"environment": "spo"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,100 @@
|
|||
# React Aggregated Calendar Webpart
|
||||
|
||||
## Summary
|
||||
This is a sample webpart developed using React Framework to gather the aggregated events from the multiple calendars from multiple sites using Full Calendar from fullcalendar.io
|
||||
|
||||
|
||||
![The web part in action](./assets/react-aggregated-calendar.gif)
|
||||
|
||||
|
||||
|
||||
The webpart was designed to create an aggregated view of calendar to fetch events from multiple calendars across the sites and site collection.
|
||||
The webpart will show the event information using the callout functionality of Office UI Fabric
|
||||
|
||||
Webpart is developed using below technologies
|
||||
* React Framework
|
||||
* Full Calendar(fullcalendar.io)
|
||||
* jQuery
|
||||
* Office UI Fabric
|
||||
|
||||
|
||||
## Used SharePoint Framework Version
|
||||
![drop](https://img.shields.io/badge/version-GA-green.svg)
|
||||
|
||||
## Applies to
|
||||
|
||||
* [SharePoint Framework](https:/dev.office.com/sharepoint)
|
||||
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
|
||||
|
||||
|
||||
|
||||
## Prerequisites
|
||||
|
||||
Before you can use this webpart exmaple, you will need atleast one Out of the Box Calendar created.
|
||||
|
||||
It is required that the users have view access on the calendar.
|
||||
|
||||
## Solution
|
||||
|
||||
Solution|Author(s)
|
||||
--------|---------
|
||||
react-aggregated-calendar | [Dhaval Shah](https://www.linkedin.com/in/dhavalshah27) ([@beingdhavalshah](https://twitter.com/BeingDhavalShah))
|
||||
|
||||
## Version history
|
||||
|
||||
Version|Date|Comments
|
||||
-------|----|--------
|
||||
|
||||
1.0 |July 16, 2018 | Initial Release
|
||||
|
||||
## Disclaimer
|
||||
**THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT.**
|
||||
|
||||
---
|
||||
|
||||
## Minimal Path to Awesome
|
||||
|
||||
- Clone this repository
|
||||
- in the command line run:
|
||||
- `npm install`
|
||||
- `gulp serve`
|
||||
|
||||
|
||||
## Features
|
||||
This Web Part displays the events from multiple calendars located in various sites/site collection of sharepoint:
|
||||
|
||||
- Aggregated events for Calendar
|
||||
- Supports Sub-Sites and Site Collection level
|
||||
- Display of Legend for each Calendar
|
||||
- Formatted Date time
|
||||
- Display of Event Details over event click
|
||||
|
||||
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-aggregated-calendar" />
|
||||
|
||||
## React Aggregated Calendar
|
||||
|
||||
|
||||
|
||||
|
||||
### Building the code
|
||||
|
||||
```bash
|
||||
git clone the repo
|
||||
npm i
|
||||
npm i -g gulp
|
||||
gulp
|
||||
```
|
||||
|
||||
This package produces the following:
|
||||
|
||||
* lib/* - intermediate-stage commonjs build artifacts
|
||||
* dist/* - the bundled script, along with other resources
|
||||
* deploy/* - all resources which should be uploaded to a CDN.
|
||||
|
||||
### Build options
|
||||
|
||||
gulp clean
|
||||
gulp test
|
||||
gulp serve
|
||||
gulp bundle
|
||||
gulp package-solution
|
Binary file not shown.
After Width: | Height: | Size: 11 MiB |
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/config.2.0.schema.json",
|
||||
"version": "2.0",
|
||||
"bundles": {
|
||||
"react-aggregated-calendar-web-part": {
|
||||
"components": [{
|
||||
"entrypoint": "./lib/webparts/reactAggregatedCalendar/ReactAggregatedCalendarWebPart.js",
|
||||
"manifest": "./src/webparts/reactAggregatedCalendar/ReactAggregatedCalendarWebPart.manifest.json"
|
||||
}]
|
||||
}
|
||||
},
|
||||
"externals": {
|
||||
"sp-client-custom-fields": "node_modules/sp-client-custom-fields/dist/sp-client-custom-fields.bundle.js",
|
||||
"jquery": {
|
||||
"path": "https://code.jquery.com/jquery-1.11.1.min.js",
|
||||
"globalName": "jQuery"
|
||||
},
|
||||
"moment": "https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.10.6/moment.min.js",
|
||||
"fullcalendar": {
|
||||
"path": "https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.js",
|
||||
"globalName": "jQuery",
|
||||
"globalDependencies": [
|
||||
"jquery"
|
||||
]
|
||||
}
|
||||
},
|
||||
"localizedResources": {
|
||||
"sp-client-custom-fields/strings": "node_modules/sp-client-custom-fields/lib/loc/{locale}.js",
|
||||
"ReactAggregatedCalendarWebPartStrings": "lib/webparts/reactAggregatedCalendar/loc/{locale}.js"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/copy-assets.schema.json",
|
||||
"deployCdnPath": "temp/deploy"
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/deploy-azure-storage.schema.json",
|
||||
"workingDir": "./temp/deploy/",
|
||||
"account": "<!-- STORAGE ACCOUNT NAME -->",
|
||||
"container": "react-aggregated-calendar",
|
||||
"accessKey": "<!-- ACCESS KEY -->"
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/package-solution.schema.json",
|
||||
"solution": {
|
||||
"name": "react-aggregated-calendar-app",
|
||||
"id": "e904a5f2-6d72-4ac2-96d8-5679cb4c7384",
|
||||
"version": "1.0.0.0",
|
||||
"includeClientSideAssets": true,
|
||||
"skipFeatureDeployment": true
|
||||
},
|
||||
"paths": {
|
||||
"zippedPackage": "solution/react-aggregated-calendar.sppkg"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"$schema": "https://dev.office.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,38 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/core-build/tslint.schema.json",
|
||||
|
||||
"displayAsWarning": true,
|
||||
"removeExistingRules": true,
|
||||
"useDefaultConfigAsBase": false,
|
||||
"lintConfig": {
|
||||
"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-case": true,
|
||||
"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,
|
||||
"valid-typeof": true,
|
||||
"variable-name": false,
|
||||
"whitespace": false
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx-build/write-manifests.schema.json",
|
||||
"cdnBasePath": "https://sharepointarena.sharepoint.com/sites/SPFX/CDN/react-aggregated-calendar"
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
'use strict';
|
||||
|
||||
const gulp = require('gulp');
|
||||
const build = require('@microsoft/sp-build-web');
|
||||
const path = require('path');
|
||||
const bundleAnalyzer = require('webpack-bundle-analyzer');
|
||||
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
|
||||
build.configureWebpack.mergeConfig({
|
||||
additionalConfiguration: (generatedConfiguration) => {
|
||||
const lastDirName = path.basename(__dirname);
|
||||
const dropPath = path.join(__dirname, 'temp', 'stats');
|
||||
generatedConfiguration.plugins.push(new bundleAnalyzer.BundleAnalyzerPlugin({
|
||||
openAnalyzer: false,
|
||||
analyzerMode: 'static',
|
||||
reportFilename: path.join(dropPath, `${lastDirName}.stats.html`),
|
||||
generateStatsFile: true,
|
||||
statsFilename: path.join(dropPath, `${lastDirName}.stats.json`),
|
||||
logLevel: 'error'
|
||||
}));
|
||||
|
||||
return generatedConfiguration;
|
||||
}
|
||||
});
|
||||
|
||||
build.initialize(gulp);
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,38 @@
|
|||
{
|
||||
"name": "react-aggregated-calendar",
|
||||
"version": "0.0.1",
|
||||
"private": true,
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "gulp bundle",
|
||||
"clean": "gulp clean",
|
||||
"test": "gulp test"
|
||||
},
|
||||
"dependencies": {
|
||||
"@microsoft/sp-core-library": "~1.4.1",
|
||||
"@microsoft/sp-lodash-subset": "~1.4.1",
|
||||
"@microsoft/sp-office-ui-fabric-core": "~1.4.1",
|
||||
"@microsoft/sp-webpart-base": "~1.4.1",
|
||||
"@types/fullcalendar": "^3.8.0",
|
||||
"@types/jquery": "^3.3.2",
|
||||
"@types/react": "15.6.6",
|
||||
"@types/react-dom": "15.5.6",
|
||||
"@types/webpack-env": ">=1.12.1 <1.14.0",
|
||||
"moment": "^2.22.2",
|
||||
"react": "15.6.2",
|
||||
"react-dom": "15.6.2",
|
||||
"sp-client-custom-fields": "^1.3.7"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@microsoft/sp-build-web": "~1.4.1",
|
||||
"@microsoft/sp-module-interfaces": "~1.4.1",
|
||||
"@microsoft/sp-webpart-workbench": "~1.4.1",
|
||||
"@types/chai": ">=3.4.34 <3.6.0",
|
||||
"@types/mocha": ">=2.2.33 <2.6.0",
|
||||
"ajv": "~5.2.2",
|
||||
"gulp": "~3.9.1",
|
||||
"webpack-bundle-analyzer": "^2.13.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"$schema": "https://dev.office.com/json-schemas/spfx/client-side-web-part-manifest.schema.json",
|
||||
"id": "be293fec-29f2-4db2-869a-604e560980fa",
|
||||
"alias": "ReactAggregatedCalendarWebPart",
|
||||
"componentType": "WebPart",
|
||||
"version": "*",
|
||||
"manifestVersion": 2,
|
||||
"requiresCustomScript": false,
|
||||
"preconfiguredEntries": [{
|
||||
"groupId": "5c03119e-3074-46fd-976b-c60198311f70",
|
||||
"group": {
|
||||
"default": "Ayka"
|
||||
},
|
||||
"title": {
|
||||
"default": "React Aggreagated Calendar App"
|
||||
},
|
||||
"description": {
|
||||
"default": "React Aggreagated Calendar App developed by Dhaval Shah"
|
||||
},
|
||||
"officeFabricIconFontName": "SearchCalendar",
|
||||
"properties": {
|
||||
"header": "My Calendar",
|
||||
"showWeekends": "Off",
|
||||
"showLegend": true,
|
||||
"dateFormat": "MMMM Do YYYY, h:mm a",
|
||||
"defaultView": "month",
|
||||
"availableViews": [
|
||||
"month",
|
||||
"agendaWeek",
|
||||
"agendaDay",
|
||||
"listMonth"
|
||||
]
|
||||
}
|
||||
}]
|
||||
}
|
|
@ -0,0 +1,181 @@
|
|||
import * as React from 'react';
|
||||
import * as ReactDom from 'react-dom';
|
||||
import { Log, Version } from '@microsoft/sp-core-library';
|
||||
import { BaseClientSideWebPart, IPropertyPaneConfiguration, PropertyPaneTextField, PropertyPaneDropdown, PropertyPaneToggle } from '@microsoft/sp-webpart-base';
|
||||
import { SPComponentLoader } from '@microsoft/sp-loader';
|
||||
import * as strings from 'ReactAggregatedCalendarWebPartStrings';
|
||||
import ReactAggregatedCalendar from './components/ReactAggregatedCalendar';
|
||||
import { IReactAggregatedCalendarProps } from './components/IReactAggregatedCalendarProps';
|
||||
import MessageComponent, { IMessageComponentProps } from '../shared/components/MessageComponent';
|
||||
import { MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
|
||||
import { PropertyFieldCustomList, CustomListFieldType } from 'sp-client-custom-fields/lib/PropertyFieldCustomList';
|
||||
import { SelectedCalendar } from './model/SelectedCalendar';
|
||||
import { PropertyFieldDropDownSelect } from 'sp-client-custom-fields/lib/PropertyFieldDropDownSelect';
|
||||
import { IDropdownOption } from 'office-ui-fabric-react/lib/Dropdown';
|
||||
|
||||
/**
|
||||
* Interface for the Aggregated Calendar Webpart Class Properties
|
||||
*
|
||||
* @export
|
||||
* @interface IReactAggregatedCalendarWebPartProps
|
||||
*/
|
||||
export interface IReactAggregatedCalendarWebPartProps {
|
||||
header: string;
|
||||
calendarList: SelectedCalendar[];
|
||||
dateFormat: string;
|
||||
showLegend: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aggregated Calendar Webpart Class
|
||||
*
|
||||
* @export
|
||||
* @class ReactAggregatedCalendarWebPart
|
||||
* @extends {BaseClientSideWebPart<IReactAggregatedCalendarWebPartProps>}
|
||||
*/
|
||||
export default class ReactAggregatedCalendarWebPart extends BaseClientSideWebPart<IReactAggregatedCalendarWebPartProps> {
|
||||
private availableViews: IDropdownOption[] = require("../shared/availableViews.json");
|
||||
private timeFormat: IDropdownOption[] = require("../shared/timeFormat.json");
|
||||
protected onInit(): Promise<void> {
|
||||
SPComponentLoader.loadCss('https://cdnjs.cloudflare.com/ajax/libs/fullcalendar/3.4.0/fullcalendar.min.css');
|
||||
return super.onInit();
|
||||
}
|
||||
/**
|
||||
* Renders the React Agggregated Calendar Webpart
|
||||
*
|
||||
* @memberof ReactAggregatedCalendarWebPart
|
||||
*/
|
||||
public render(): void {
|
||||
Log.verbose("render()", "Inside Render", this.context.serviceScope);
|
||||
if (this.needsConfiguration()) {
|
||||
Log.warn("render()", "Webpart not configured", this.context.serviceScope);
|
||||
this.renderMessage(strings.WebPartNotConfigured, MessageBarType.error, true);
|
||||
} else {
|
||||
Log.info("render()", "Webpart configuration not needed", this.context.serviceScope);
|
||||
const element: React.ReactElement<IReactAggregatedCalendarProps> = React.createElement(
|
||||
ReactAggregatedCalendar,
|
||||
{
|
||||
header: this.properties.header,
|
||||
selectedCalendarLists: this.properties.calendarList,
|
||||
context: this.context,
|
||||
domElement: this.domElement,
|
||||
dateFormat: this.properties.dateFormat,
|
||||
showLegend: this.properties.showLegend
|
||||
|
||||
}
|
||||
);
|
||||
ReactDom.render(element, this.domElement);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the data Version of the Webpart
|
||||
*
|
||||
* @readonly
|
||||
* @protected
|
||||
* @type {Version}
|
||||
* @memberof ReactAggregatedCalendarWebPart
|
||||
*/
|
||||
protected get dataVersion(): Version {
|
||||
return Version.parse('1.0');
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the SPFx Property Pane of the Aggregated Calendar Webpart
|
||||
*
|
||||
* @protected
|
||||
* @returns {IPropertyPaneConfiguration}
|
||||
* @memberof ReactAggregatedCalendarWebPart
|
||||
*/
|
||||
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
|
||||
return {
|
||||
pages: [
|
||||
{
|
||||
header: {
|
||||
description: strings.PropertyPaneDescription
|
||||
},
|
||||
groups: [
|
||||
{
|
||||
groupName: strings.BasicGroupName,
|
||||
groupFields: [
|
||||
PropertyPaneTextField('header', {
|
||||
label: strings.HeaderFieldLabel
|
||||
}),
|
||||
PropertyFieldCustomList('calendarList', {
|
||||
label: strings.SelectCalendarLabel,
|
||||
value: this.properties.calendarList,
|
||||
headerText: 'Manage Calendar',
|
||||
fields: [
|
||||
{ id: 'CalendarTitle', title: 'Calendar Title', required: true, type: CustomListFieldType.string },
|
||||
{ id: 'SiteUrl', title: 'Site Url', required: true, type: CustomListFieldType.string },
|
||||
{
|
||||
id: 'CalendarListTitle', title: 'Calendar List Title', required: true,
|
||||
type: CustomListFieldType.string
|
||||
},
|
||||
{ id: 'Color', title: 'Color', required: false, type: CustomListFieldType.color }
|
||||
],
|
||||
disabled: false,
|
||||
onPropertyChange: this.onPropertyPaneFieldChanged.bind(this),
|
||||
render: this.render.bind(this),
|
||||
disableReactivePropertyChanges: this.disableReactivePropertyChanges,
|
||||
properties: this.properties,
|
||||
context: this.context,
|
||||
key: 'calendarList'
|
||||
}),
|
||||
PropertyPaneDropdown('dateFormat', {
|
||||
label: strings.SelectDateFormatFieldLabel,
|
||||
selectedKey: "MMMM Do YYYY, h: mm a",
|
||||
options: this.timeFormat
|
||||
}),
|
||||
PropertyPaneToggle('showLegend', {
|
||||
label: strings.ShowLegendFieldLabel,
|
||||
onText: strings.OnTextFieldLabel,
|
||||
offText: strings.OffTextFieldLabel,
|
||||
checked: false
|
||||
})
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check whether Aggregated Calendar needs configuration
|
||||
* or not
|
||||
* @private
|
||||
* @returns {boolean}
|
||||
* @memberof ReactAggregatedCalendarWebPart
|
||||
*/
|
||||
private needsConfiguration(): boolean {
|
||||
Log.verbose("needsConfiguration()", "calendarList : " + this.properties.calendarList, this.context.serviceScope);
|
||||
return this.properties.calendarList === null ||
|
||||
this.properties.calendarList === undefined ||
|
||||
this.properties.calendarList.length === 0;
|
||||
}
|
||||
/**
|
||||
* Render Message method to render the message component
|
||||
*
|
||||
* @private
|
||||
* @param {string} statusMessage
|
||||
* @param {MessageBarType} statusMessageType
|
||||
* @param {boolean} display
|
||||
* @memberof ReactAggregatedCalendarWebPart
|
||||
*/
|
||||
private renderMessage(statusMessage: string, statusMessageType: MessageBarType,
|
||||
display: boolean): void {
|
||||
Log.verbose("renderMessage()", "Rendering Message " + statusMessage + " of type " + statusMessageType, this.context.serviceScope);
|
||||
const messageElement: React.ReactElement<IMessageComponentProps> = React.createElement(
|
||||
MessageComponent,
|
||||
{
|
||||
Message: statusMessage,
|
||||
Type: statusMessageType,
|
||||
Display: display
|
||||
}
|
||||
);
|
||||
|
||||
ReactDom.render(messageElement, this.domElement);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
import { SelectedCalendar } from "../model/SelectedCalendar";
|
||||
import { WebPartContext } from "@microsoft/sp-webpart-base";
|
||||
|
||||
/**
|
||||
*
|
||||
*
|
||||
* @export
|
||||
* @interface IReactAggregatedCalendarProps
|
||||
*/
|
||||
export interface IReactAggregatedCalendarProps {
|
||||
header: string;
|
||||
selectedCalendarLists: SelectedCalendar[];
|
||||
context: WebPartContext;
|
||||
domElement: HTMLElement;
|
||||
dateFormat: string;
|
||||
showLegend: boolean;
|
||||
}
|
|
@ -0,0 +1,146 @@
|
|||
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
|
||||
@import '~office-ui-fabric-react/dist/sass/_References.scss';
|
||||
@import '~office-ui-fabric-react/dist/sass/Fabric.scss';
|
||||
.closeIconFocus:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.calloutInnerEventContent {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.msCalloutclose {
|
||||
margin: 0;
|
||||
border: 0;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 12px;
|
||||
padding: 8px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
font-size: $ms-font-size-m;
|
||||
color: $ms-color-neutralSecondary;
|
||||
//z-index: ($ms-zIndex-Callout + $ms-zIndex-front);
|
||||
}
|
||||
|
||||
.msCalloutheader {
|
||||
//z-index: ($ms-zIndex-Callout + $ms-zIndex-middle);
|
||||
padding: 18px 24px 12px;
|
||||
background-color: "[theme: inputBackgroundCheckedHovered, default: #0078d7]" !important ;
|
||||
}
|
||||
|
||||
.msCallouttitle {
|
||||
margin: 0; //font-family: $ms-font-family-semilight;
|
||||
//font-size: $ms-font-size-xl;
|
||||
}
|
||||
|
||||
.msCalloutinner {
|
||||
height: 100%;
|
||||
padding: 0 24px 20px;
|
||||
}
|
||||
|
||||
.msCalloutsubText {
|
||||
margin: 0; //font-family: $ms-font-family-semilight;
|
||||
color: $ms-color-neutralPrimary;
|
||||
font-size: $ms-font-size-s;
|
||||
}
|
||||
|
||||
.reactAggregatedCalendar {
|
||||
.legend {
|
||||
border: solid 1px #eee;
|
||||
font-size: 14px;
|
||||
text-align: right;
|
||||
white-space: normal;
|
||||
}
|
||||
.outerLegendDiv {
|
||||
display: inline-block;
|
||||
padding: 5px 20px 5px 5px;
|
||||
line-height: 15px;
|
||||
max-width: 300px;
|
||||
cursor: pointer;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.innerLegendDiv {
|
||||
border: 1px solid gray;
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
font-size: 1px;
|
||||
position: relative;
|
||||
float: left;
|
||||
margin-right: 5px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.container {
|
||||
max-width: 700px;
|
||||
margin: 0px auto;
|
||||
box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.2), 0 25px 50px 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.row {
|
||||
@include ms-Grid-row;
|
||||
|
||||
// background-color: $ms-color-themeDark;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.column {
|
||||
@include ms-Grid-col;
|
||||
@include ms-lg12;
|
||||
@include ms-xl12;
|
||||
@include ms-xlPush2;
|
||||
@include ms-lgPush1;
|
||||
}
|
||||
|
||||
.title {
|
||||
@include ms-font-xl;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.subTitle {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.description {
|
||||
@include ms-font-l;
|
||||
@include ms-fontColor-white;
|
||||
}
|
||||
|
||||
.button {
|
||||
// Our button
|
||||
text-decoration: none;
|
||||
height: 32px;
|
||||
|
||||
// Primary Button
|
||||
min-width: 80px;
|
||||
background-color: $ms-color-themePrimary;
|
||||
border-color: $ms-color-themePrimary;
|
||||
color: $ms-color-white;
|
||||
|
||||
// Basic Button
|
||||
outline: transparent;
|
||||
position: relative;
|
||||
font-family: "Segoe UI WestEuropean","Segoe UI",-apple-system,BlinkMacSystemFont,Roboto,"Helvetica Neue",sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
font-size: $ms-font-size-m;
|
||||
font-weight: $ms-font-weight-regular;
|
||||
border-width: 0;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
padding: 0 16px;
|
||||
|
||||
.label {
|
||||
font-weight: $ms-font-weight-semibold;
|
||||
font-size: $ms-font-size-m;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
margin: 0 4px;
|
||||
vertical-align: top;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,277 @@
|
|||
import * as React from 'react';
|
||||
import * as $ from 'jquery';
|
||||
import * as moment from 'moment';
|
||||
import 'fullcalendar';
|
||||
import styles from './ReactAggregatedCalendar.module.scss';
|
||||
import { IReactAggregatedCalendarProps } from './IReactAggregatedCalendarProps';
|
||||
import { EnvironmentType, Environment } from '@microsoft/sp-core-library';
|
||||
import { AggregatedCalendarService } from '../service/AggregatedCalendarService';
|
||||
import { AggregatedCalendarMockService } from '../service/AggregatedCalendarMockService';
|
||||
import { css } from 'office-ui-fabric-react/lib/Utilities';
|
||||
import * as strings from 'ReactAggregatedCalendarWebPartStrings';
|
||||
import { FullCalendarEvent } from '../model/FullCalendarEvent';
|
||||
import { DirectionalHint, Callout } from 'office-ui-fabric-react/lib/Callout';
|
||||
import { Label } from 'office-ui-fabric-react/lib/Label';
|
||||
|
||||
/**
|
||||
* Interface for maintaining ReactAggregatedCalendar webpart state
|
||||
*
|
||||
* @export
|
||||
* @interface IReactAggregatedCalendarState
|
||||
*/
|
||||
export interface IReactAggregatedCalendarState {
|
||||
isCalloutVisible?: boolean;
|
||||
selectedEvent: FullCalendarEvent;
|
||||
directionalHint?: DirectionalHint;
|
||||
isBeakVisible?: boolean;
|
||||
gapSpace?: number;
|
||||
beakWidth?: number;
|
||||
EventElement: HTMLElement;
|
||||
}
|
||||
|
||||
/**
|
||||
* React Component for ReactAggregatedCalendar Webpart
|
||||
*
|
||||
* @export
|
||||
* @class ReactAggregatedCalendar
|
||||
* @extends {React.Component<IReactAggregatedCalendarProps, IReactAggregatedCalendarState>}
|
||||
*/
|
||||
export default class ReactAggregatedCalendar
|
||||
extends React.Component<IReactAggregatedCalendarProps, IReactAggregatedCalendarState> {
|
||||
|
||||
/**
|
||||
*Creates an instance of ReactAggregatedCalendar.
|
||||
* @param {IReactAggregatedCalendarProps} props
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
public constructor(props: IReactAggregatedCalendarProps) {
|
||||
super(props);
|
||||
|
||||
this.onCalloutDismiss = this.onCalloutDismiss.bind(this);
|
||||
this.eventClickHandler = this.eventClickHandler.bind(this);
|
||||
|
||||
// Initialize the State for ReactAggregatedCalendar
|
||||
this.state = {
|
||||
isCalloutVisible: false,
|
||||
selectedEvent: {
|
||||
id: 0,
|
||||
title: '',
|
||||
color: '',
|
||||
start: moment(),
|
||||
end: moment(),
|
||||
description: '',
|
||||
location: '',
|
||||
allDay: false,
|
||||
category: ''
|
||||
},
|
||||
directionalHint: DirectionalHint.bottomCenter,
|
||||
isBeakVisible: true,
|
||||
gapSpace: 10,
|
||||
beakWidth: 20,
|
||||
EventElement: null
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* componentDidMount
|
||||
*
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
public componentDidMount() {
|
||||
this.renderContents();
|
||||
}
|
||||
|
||||
/**
|
||||
* componentDidUpdate
|
||||
*
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
public componentDidUpdate() {
|
||||
this.renderContents();
|
||||
}
|
||||
/**
|
||||
* Render method for the ReactAggregatedCalendar React Component
|
||||
*
|
||||
* @returns {React.ReactElement<IReactAggregatedCalendarProps>}
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
public render(): React.ReactElement<IReactAggregatedCalendarProps> {
|
||||
const { isCalloutVisible } = this.state;
|
||||
let calendarLegend: JSX.Element[] = ([]);
|
||||
|
||||
// Render the Legend for the Calendar Events
|
||||
calendarLegend = this.props.selectedCalendarLists.map((calendar) => {
|
||||
let calendarLegendColor = {
|
||||
'background-color': `${calendar.Color}`
|
||||
};
|
||||
return (
|
||||
<div className={styles.outerLegendDiv} title={calendar.CalendarTitle}>
|
||||
<div className={styles.innerLegendDiv} style={calendarLegendColor}>
|
||||
</div>
|
||||
{calendar.CalendarTitle}
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
// Render the FullCalendar container
|
||||
return (
|
||||
<div className={styles.reactAggregatedCalendar}>
|
||||
<h1>{this.props.header}</h1>
|
||||
<div >
|
||||
<div>
|
||||
<div>
|
||||
<div id="aggregatedCalendarComp">
|
||||
</div>
|
||||
{
|
||||
this.props.showLegend &&
|
||||
<div className={styles.legend}>
|
||||
{calendarLegend}
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isCalloutVisible && (
|
||||
<Callout
|
||||
className="ms-CalloutExample"
|
||||
ariaLabelledBy={'callout-label-1'}
|
||||
ariaDescribedBy={'callout-description-1'}
|
||||
role={'alertdialog'}
|
||||
target={this.state.EventElement}
|
||||
onDismiss={this.onCalloutDismiss}
|
||||
gapSpace={this.state.gapSpace}
|
||||
isBeakVisible={this.state.isBeakVisible}
|
||||
beakWidth={this.state.beakWidth}
|
||||
directionalHint={this.state.directionalHint}
|
||||
setInitialFocus={true}>
|
||||
<button onClick={this.onCalloutDismiss}
|
||||
className={css(styles.msCalloutclose, styles.closeIconFocus, 'ms-fontColor-white')} >
|
||||
<i className="ms-Icon ms-Icon--Clear"></i>
|
||||
</button>
|
||||
<div className={css(styles.msCalloutheader, 'ms-fontColor-white')}>
|
||||
<p className={styles.msCallouttitle}>{this.state.selectedEvent.title}</p>
|
||||
</div>
|
||||
<div className={css(styles.msCalloutinner, styles.calloutInnerEventContent)}>
|
||||
<div className="ms-Callout-content">
|
||||
<p className={styles.msCalloutsubText} dangerouslySetInnerHTML={this.createMarkup(this.state.selectedEvent.description)} />
|
||||
<p className={styles.msCalloutsubText}>
|
||||
<Label>{strings.StartTimeLabel}{this.state.selectedEvent.start.format(this.props.dateFormat)} </Label>
|
||||
{
|
||||
this.state.selectedEvent.end !== null &&
|
||||
<Label>{strings.EndTimeLabel} {this.state.selectedEvent.end.format(this.props.dateFormat)}</Label>
|
||||
}
|
||||
{
|
||||
this.state.selectedEvent.location !== '' &&
|
||||
<Label>{strings.LocationLabel}{this.state.selectedEvent.location}</Label>
|
||||
}
|
||||
{
|
||||
this.state.selectedEvent.category !== '' &&
|
||||
<Label>{strings.CategoryLabel}{this.state.selectedEvent.category}</Label>
|
||||
}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Callout>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render the Full Calendar Plugin
|
||||
*
|
||||
* @private
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
private renderContents() {
|
||||
let containerEl: JQuery = $('#aggregatedCalendarComp');
|
||||
let eventSourcesArray: any[] = [];
|
||||
const dataService = (Environment.type === EnvironmentType.Test
|
||||
|| Environment.type === EnvironmentType.Local) ? new AggregatedCalendarMockService() :
|
||||
this.props.context.serviceScope.consume(AggregatedCalendarService.serviceKey);
|
||||
console.log(this.props.selectedCalendarLists);
|
||||
this.props.selectedCalendarLists.forEach((calendarData) => {
|
||||
const calendarRestApi: string = calendarData.SiteUrl.trim()
|
||||
+ '/_api/Web/Lists/GetByTitle(\'' + calendarData.CalendarListTitle.trim() + '\')/items';
|
||||
|
||||
eventSourcesArray.push({
|
||||
events: ((start: moment.Moment, end: moment.Moment, timezone, callback) => {
|
||||
const startDate = start.format('YYYY-MM-DD');
|
||||
const endDate = end.format('YYYY-MM-DD');
|
||||
dataService.getEventsForCalendar(calendarRestApi, calendarData.Color, startDate, endDate)
|
||||
.then((response: FullCalendarEvent[]) => {
|
||||
callback(response);
|
||||
});
|
||||
})
|
||||
});
|
||||
});
|
||||
containerEl.fullCalendar({
|
||||
timezone: 'local',
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title'
|
||||
},
|
||||
defaultDate: new Date(),
|
||||
navLinks: true,
|
||||
editable: true,
|
||||
eventLimit: true,
|
||||
eventSources: eventSourcesArray,
|
||||
eventClick: this.eventClickHandler
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Click Event handler when the event is clicked on the Calendar
|
||||
* Display the Callout function to display event details
|
||||
* @private
|
||||
* @param {*} eventObj
|
||||
* @param {*} jsEvent
|
||||
* @param {*} view
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
private eventClickHandler(eventObj: any, jsEvent: any, view: any) {
|
||||
|
||||
this.setState(() => {
|
||||
return {
|
||||
isCalloutVisible: !this.state.isCalloutVisible,
|
||||
selectedEvent: {
|
||||
id: eventObj.id,
|
||||
title: eventObj.title,
|
||||
color: eventObj.color,
|
||||
start: moment(eventObj.start),
|
||||
end: moment(eventObj.end),
|
||||
description: eventObj.description,
|
||||
location: eventObj.location,
|
||||
allDay: eventObj.allDay,
|
||||
category: eventObj.category
|
||||
},
|
||||
EventElement: jsEvent.toElement
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the call out component on close
|
||||
*
|
||||
* @private
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
private onCalloutDismiss() {
|
||||
this.setState({
|
||||
isCalloutVisible: false
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Create markup for rendering HTML on react component
|
||||
*
|
||||
* @private
|
||||
* @returns
|
||||
* @memberof ReactAggregatedCalendar
|
||||
*/
|
||||
private createMarkup(description: string) {
|
||||
return { __html: description };
|
||||
}
|
||||
|
||||
}
|
22
samples/react-aggregated-calendar/src/webparts/reactAggregatedCalendar/loc/en-us.js
vendored
Normal file
22
samples/react-aggregated-calendar/src/webparts/reactAggregatedCalendar/loc/en-us.js
vendored
Normal file
|
@ -0,0 +1,22 @@
|
|||
define([], function () {
|
||||
return {
|
||||
"PropertyPaneDescription": "React Aggregated Calendar View App",
|
||||
"BasicGroupName": "Dynamic Properties",
|
||||
"DescriptionFieldLabel": "Description Field",
|
||||
"SelectCalendarLabel": "Add Calendars",
|
||||
"HeaderFieldLabel": "App Header Value",
|
||||
"WebPartNotConfigured": "Webpart is not configured. Please configure webpart by Editing the page.",
|
||||
"StartTimeLabel": "Start Time : ",
|
||||
"EndTimeLabel": "End Time : ",
|
||||
"LocationLabel": "Location : ",
|
||||
"CategoryLabel": "Category : ",
|
||||
"SelectDateFormatFieldLabel": "Select Date & Time Format",
|
||||
"ShowWeekendsFieldLabel": "Show Weekends",
|
||||
"OnTextFieldLabel": "On",
|
||||
"OffTextFieldLabel": "Off",
|
||||
"DefautlViewFieldLabel": "Default Calendar View",
|
||||
"ShowLegendFieldLabel": "Show Legend",
|
||||
"AvailableViewFieldLabel": "Select Available Views",
|
||||
"HeightFieldLabel":"Enter Calendar Height"
|
||||
}
|
||||
});
|
25
samples/react-aggregated-calendar/src/webparts/reactAggregatedCalendar/loc/mystrings.d.ts
vendored
Normal file
25
samples/react-aggregated-calendar/src/webparts/reactAggregatedCalendar/loc/mystrings.d.ts
vendored
Normal file
|
@ -0,0 +1,25 @@
|
|||
declare interface IReactAggregatedCalendarWebPartStrings {
|
||||
PropertyPaneDescription: string;
|
||||
BasicGroupName: string;
|
||||
DescriptionFieldLabel: string;
|
||||
SelectDateFormatFieldLabel: string;
|
||||
ShowWeekendsFieldLabel: string;
|
||||
WebPartNotConfigured: string;
|
||||
SelectCalendarLabel: string;
|
||||
HeaderFieldLabel: string;
|
||||
StartTimeLabel: string;
|
||||
EndTimeLabel: string;
|
||||
LocationLabel: string;
|
||||
CategoryLabel: string;
|
||||
OnTextFieldLabel: string;
|
||||
OffTextFieldLabel: string;
|
||||
DefautlViewFieldLabel: string;
|
||||
ShowLegendFieldLabel: string;
|
||||
AvailableViewFieldLabel: string;
|
||||
HeightFieldLabel: string;
|
||||
}
|
||||
|
||||
declare module 'ReactAggregatedCalendarWebPartStrings' {
|
||||
const strings: IReactAggregatedCalendarWebPartStrings;
|
||||
export = strings;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
import * as moment from 'moment';
|
||||
|
||||
/**
|
||||
* Interface for FullCalendarEvent
|
||||
*
|
||||
* @export
|
||||
* @interface FullCalendarEvent
|
||||
*/
|
||||
export interface FullCalendarEvent {
|
||||
id: number;
|
||||
title: string;
|
||||
start: moment.Moment;
|
||||
end: moment.Moment;
|
||||
color: string;
|
||||
allDay: boolean;
|
||||
description: string;
|
||||
location: string;
|
||||
category: string;
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* Interface for SPCalendarItemsValue
|
||||
*
|
||||
* @export
|
||||
* @interface SPCalendarItemsValue
|
||||
*/
|
||||
export interface SPCalendarItemsValue {
|
||||
Id: number;
|
||||
Title: string;
|
||||
Location?: any;
|
||||
EventDate: Date;
|
||||
EndDate: Date;
|
||||
Description?: any;
|
||||
fAllDayEvent?: boolean;
|
||||
Category?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Interface for SPCalendarItems
|
||||
*
|
||||
* @export
|
||||
* @interface SPCalendarItems
|
||||
*/
|
||||
export interface SPCalendarItems {
|
||||
value: SPCalendarItemsValue[];
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
/**
|
||||
* Interface for SelectedCalendar
|
||||
*
|
||||
* @export
|
||||
* @interface SelectedCalendar
|
||||
*/
|
||||
export interface SelectedCalendar {
|
||||
CalendarTitle: string;
|
||||
SiteUrl: string;
|
||||
CalendarListTitle: string;
|
||||
Color: string;
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
import { IAggregatedCalendarService } from './IAggregatedCalendarService';
|
||||
import { SelectedCalendar } from '../model/SelectedCalendar';
|
||||
import { FullCalendarEvent } from '../model/FullCalendarEvent';
|
||||
import * as moment from 'moment';
|
||||
/**
|
||||
* Mock Service for AggregatedCalendarService
|
||||
*
|
||||
* @export
|
||||
* @class AggregatedCalendarMockService
|
||||
* @implements {IAggregatedCalendarService}
|
||||
*/
|
||||
export class AggregatedCalendarMockService implements IAggregatedCalendarService {
|
||||
|
||||
/**
|
||||
* Returns the mock data for the calendar events
|
||||
*
|
||||
* @param {string} calendarRestApi
|
||||
* @param {string} calendarColor
|
||||
* @param {string} startDate
|
||||
* @param {string} endDate
|
||||
* @returns {Promise<FullCalendarEvent[]>}
|
||||
* @memberof AggregatedCalendarMockService
|
||||
*/
|
||||
public getEventsForCalendar(calendarRestApi: string, calendarColor: string, startDate: string,
|
||||
endDate: string):
|
||||
Promise<FullCalendarEvent[]> {
|
||||
return new Promise<FullCalendarEvent[]>((resolve, reject) => {
|
||||
let calendarLists: FullCalendarEvent[] = [
|
||||
{
|
||||
id: 1,
|
||||
title: "Lunch",
|
||||
start: moment().add(1, 'days'),
|
||||
end: moment().add(1, 'days').add(1, "h"),
|
||||
color: "blue",
|
||||
allDay: false,
|
||||
description: "",
|
||||
location: "18223 Kilmacolm Drive, Richmond, TX 77407",
|
||||
category: "Get-together"
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: "Lunch & Learn",
|
||||
start: moment(),
|
||||
end: moment().add(1, "h"),
|
||||
color: "blue",
|
||||
allDay: false,
|
||||
description: "<p>Lunch & Learn Session</p>\r\n",
|
||||
location: "Microsoft Store, 5015 Westheimer Rd Ste A2421, Houston, TX, United States",
|
||||
category: "Meeting"
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: "Town Hall",
|
||||
start: moment("2018-07-08T21:30:00.000Z"),
|
||||
end: moment("2018-07-08T22:30:00.000Z"),
|
||||
color: "red",
|
||||
allDay: false,
|
||||
description: "",
|
||||
location: "Deer Park, Texas, United States",
|
||||
category: ""
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: "Team Outing",
|
||||
start: moment("2018-07-12T00:00:00.000Z"),
|
||||
end: moment("2018-07-12T23:59:00.000Z"),
|
||||
color: "red",
|
||||
allDay: false,
|
||||
description: "",
|
||||
location: "Seaworld San Antonio, San Antonio, Texas, United States",
|
||||
category: ""
|
||||
}
|
||||
];
|
||||
|
||||
resolve(calendarLists);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
import { IAggregatedCalendarService } from './IAggregatedCalendarService';
|
||||
import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';
|
||||
import { ServiceKey, ServiceScope, Log } from '@microsoft/sp-core-library';
|
||||
import { PageContext } from '@microsoft/sp-page-context';
|
||||
import * as moment from 'moment';
|
||||
import { SPCalendarItems } from '../model/SPCalendarItems';
|
||||
import { FullCalendarEvent } from '../model/FullCalendarEvent';
|
||||
|
||||
/**
|
||||
* Aggregated Calendar Service for teh Aggregated Calendar Webpart to get the Calendar Events
|
||||
*
|
||||
* @export
|
||||
* @class AggregatedCalendarService
|
||||
* @implements {IAggregatedCalendarService}
|
||||
*/
|
||||
export class AggregatedCalendarService implements IAggregatedCalendarService {
|
||||
public static readonly serviceKey: ServiceKey<IAggregatedCalendarService>
|
||||
= ServiceKey.create<IAggregatedCalendarService>('ayka:IAggregatedCalendarService', AggregatedCalendarService);
|
||||
private _spHttpClient: SPHttpClient;
|
||||
private _serviceScope: ServiceScope;
|
||||
/**
|
||||
*Creates an instance of AggregatedCalendarService.
|
||||
* @param {ServiceScope} serviceScope
|
||||
* @memberof AggregatedCalendarService
|
||||
*/
|
||||
constructor(serviceScope: ServiceScope) {
|
||||
serviceScope.whenFinished(() => {
|
||||
this._spHttpClient = serviceScope.consume(SPHttpClient.serviceKey);
|
||||
this._serviceScope = serviceScope;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the Events from the SharePoint Calendar between startDate and endDate
|
||||
*
|
||||
* @param {string} calendarRestApi
|
||||
* @param {string} calendarColor
|
||||
* @param {string} startDate
|
||||
* @param {string} endDate
|
||||
* @returns {Promise<any[]>}
|
||||
* @memberof AggregatedCalendarService
|
||||
*/
|
||||
public getEventsForCalendar(calendarRestApi: string, calendarColor: string, startDate: string, endDate: string): Promise<any[]> {
|
||||
return new Promise<FullCalendarEvent[]>((resolve, reject) => {
|
||||
let _webRestApi: string = calendarRestApi +
|
||||
'?$Select=Title,EventDate,EndDate,Location,Description,Category,fAllDayEvent&$filter=((EventDate ge \''
|
||||
+ startDate + '\' and EventDate le \'' + endDate + '\'))';
|
||||
Log.info("getEventsForCalendar()", "REST API : " + calendarRestApi, this._serviceScope);
|
||||
this._spHttpClient.get(_webRestApi, SPHttpClient.configurations.v1)
|
||||
.then((response: SPHttpClientResponse) => {
|
||||
response.json().then((spEvents: SPCalendarItems) => {
|
||||
Log.verbose("getEventsForCalendar()", JSON.stringify(spEvents), this._serviceScope);
|
||||
let fullCalendarEvents: FullCalendarEvent[] = [];
|
||||
|
||||
// Convert the SharePoint Events into compatible Full Calendar Events
|
||||
spEvents.value.forEach((spEvent) => {
|
||||
|
||||
fullCalendarEvents.push({
|
||||
id: spEvent.Id,
|
||||
title: spEvent.Title,
|
||||
start: moment(spEvent.EventDate),
|
||||
end: moment(spEvent.EndDate),
|
||||
color: calendarColor,
|
||||
allDay: spEvent.fAllDayEvent,
|
||||
description: spEvent.Description || '',
|
||||
location: spEvent.Location || '',
|
||||
category: spEvent.Category || ''
|
||||
});
|
||||
|
||||
});
|
||||
Log.info("getEventsForCalendar()", "Returning Full Calendar Events ", this._serviceScope);
|
||||
Log.verbose("getEventsForCalendar()", JSON.stringify(fullCalendarEvents), this._serviceScope);
|
||||
resolve(fullCalendarEvents);
|
||||
}).catch((error) => {
|
||||
Log.error("getEventsForCalendar()", new Error("Error Fetching events from Calendar"), this._serviceScope);
|
||||
reject(error);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
import { FullCalendarEvent } from "../model/FullCalendarEvent";
|
||||
|
||||
/**
|
||||
* Interface Service for the AggregatedCalendarService
|
||||
*
|
||||
* @export
|
||||
* @interface IAggregatedCalendarService
|
||||
*/
|
||||
export interface IAggregatedCalendarService {
|
||||
getEventsForCalendar(calendarRestApi: string, calendarColor: string,
|
||||
startDate: string, endDate: string): Promise<FullCalendarEvent[]>;
|
||||
}
|
|
@ -0,0 +1,19 @@
|
|||
[{
|
||||
"key": "month",
|
||||
"text": "Month"
|
||||
|
||||
},
|
||||
{
|
||||
"key": "agendaWeek",
|
||||
"text": "Weekly"
|
||||
},
|
||||
{
|
||||
"key": "agendaDay",
|
||||
"text": "Daily"
|
||||
},
|
||||
{
|
||||
"key": "listMonth",
|
||||
"text": "Monthly List"
|
||||
|
||||
}
|
||||
]
|
|
@ -0,0 +1,60 @@
|
|||
import * as React from 'react';
|
||||
import { MessageBar, MessageBarType } from 'office-ui-fabric-react/lib/MessageBar';
|
||||
|
||||
/**
|
||||
* Interface to implement the MessageComponent Webpart
|
||||
*
|
||||
* @export
|
||||
* @interface IMessageComponentProps
|
||||
*/
|
||||
export interface IMessageComponentProps {
|
||||
Message: string;
|
||||
Type: MessageBarType;
|
||||
Display: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* React MessageComponent for displaying the messages
|
||||
*
|
||||
* @export
|
||||
* @class MessageComponent
|
||||
* @extends {React.Component<IMessageComponentProps, any>}
|
||||
*/
|
||||
export default class MessageComponent extends React.Component<IMessageComponentProps, any> {
|
||||
|
||||
/**
|
||||
*Creates an instance of MessageComponent.
|
||||
* @param {IMessageComponentProps} props
|
||||
* @memberof MessageComponent
|
||||
*/
|
||||
public constructor(props: IMessageComponentProps) {
|
||||
super(props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render method of the Message Component
|
||||
*
|
||||
* @returns {React.ReactElement<IMessageComponentProps>}
|
||||
* @memberof MessageComponent
|
||||
*/
|
||||
public render(): React.ReactElement<IMessageComponentProps> {
|
||||
|
||||
return (
|
||||
<div className={`ms-Grid-row`}>
|
||||
<div className={`ms-Grid-col ms-sm12`}>
|
||||
{
|
||||
this.props.Display &&
|
||||
<div>
|
||||
<MessageBar
|
||||
messageBarType={MessageBarType.error}
|
||||
isMultiline={false}
|
||||
dismissButtonAriaLabel="Close">
|
||||
{this.props.Message}
|
||||
</MessageBar>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
[{
|
||||
"key": "MMMM Do YYYY, h:mm a",
|
||||
"text": "MMMM Do YYYY, h:mm a"
|
||||
},
|
||||
{
|
||||
"key": "ddd MMM Do HH:mm YYYY",
|
||||
"text": "ddd MMM Do HH:mm YYYY"
|
||||
},
|
||||
{
|
||||
"key": "dddd, MMMM Do YYYY, h:mm:ss a",
|
||||
"text": "dddd, MMMM Do YYYY, h:mm:ss a"
|
||||
},
|
||||
{
|
||||
"key": "dddd, DD MMMM YYYY [at] hh:mm:ss A",
|
||||
"text": "dddd, DD MMMM YYYY [at] hh:mm:ss A"
|
||||
},
|
||||
{
|
||||
"key": "MM/d/YYYY HH:mm:ss",
|
||||
"text": "MM/d/YYYYY HH:mm:ss"
|
||||
},
|
||||
{
|
||||
"key": "d/M/YYYY HH:mm:ss",
|
||||
"text": "d/M/YYYYY HH:mm:ss"
|
||||
}, {
|
||||
"key": "dddd, MMMM Do, YYYY, h:mm:ss",
|
||||
"text": "dddd, MMMM Do, YYYY, h:mm:ss"
|
||||
}
|
||||
]
|
|
@ -0,0 +1,25 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"module": "commonjs",
|
||||
"jsx": "react",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"experimentalDecorators": true,
|
||||
"skipLibCheck": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
"./node_modules/@microsoft"
|
||||
],
|
||||
"types": [
|
||||
"es6-promise",
|
||||
"webpack-env"
|
||||
],
|
||||
"lib": [
|
||||
"es5",
|
||||
"dom",
|
||||
"es2015.collection"
|
||||
]
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue