Merge pull request #882 from joaojmendes/dev

Modern Calendar Web Part - Added Recurrence Events
This commit is contained in:
Laura Kokkarinen 2019-06-13 09:11:54 +03:00 committed by GitHub
commit a8f951d7a5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 5178 additions and 1294 deletions

View File

@ -11,7 +11,7 @@ dist
lib
solution
temp
*.sppkg
#*.sppkg
# Coverage directory used by tools like istanbul
coverage

View File

@ -22,7 +22,7 @@
"framework": "react",
"plusBeta": true,
"isCreatingSolution": true,
"version": "1.8.0",
"version": "1.8.2",
"libraryName": "react-calendar",
"libraryId": "3a13208b-3874-4036-9262-4edd22e88187",
"packageManager": "npm",

View File

@ -5,13 +5,10 @@ This Web Part allows you to manage events in a calendar.
Uses a list of existing calendars on any website.
The location and name of the list and the dates of the events to be displayed are defined in the properties of the web part.
The Events are created in Site TimeZone, defined in site Regional Settings.
Each category has its own color that is generated in the load.
The Web Part checks the user's permissions for the View, Add, Edit, and Delete events.
The Web Part does not show recurring events, I will work on it soon.
@ -19,11 +16,28 @@ The Web Part does not show recurring events, I will work on it soon.
![callendar](/samples/react-calendar/assets/animatevideo.gif)
##
![callendar](/samples/react-calendar/assets/weekly_moderncalendar.gif)
##
![callendar](/samples/react-calendar/assets/modercalendar_monthly.gif)
##
![callendar](/samples/react-calendar/assets/moderncalendar_yearly.gif)
## Web Part - Screenshots
![callendar](/samples/react-calendar//assets/screen1.png)
![callendar](/samples/react-calendar/assets/calendar_teams.jpg)
![callendar](/samples/react-calendar/assets/calendar_teams2.jpg)
![callendar](/samples/react-calendar/assets/screen1.png)
![callendar](/samples/react-calendar/assets/screen1.0.jpg)
![callendar](/samples/react-calendar/assets/screen1.0.png)
![callendar](/samples/react-calendar/assets/screen1.1.png)
@ -35,10 +49,10 @@ The Web Part does not show recurring events, I will work on it soon.
![callendar](/samples/react-calendar/assets/screen1.3.png)
![callendar](/samples/react-calendar//assets/screen1.4.png)
![callendar](/samples/react-calendar/assets/screen1.4.png)
![callendar](/samples/react-calendar//assets/screen2.png)
![callendar](/samples/react-calendar/assets/screen2.png)
@ -46,47 +60,45 @@ The Web Part does not show recurring events, I will work on it soon.
![callendar](/samples/react-calendar//assets/screen4.png)
![callendar](/samples/react-calendar/assets/screen4.png)
![callendar](/samples/react-calendar/assets/screen5.png)
![callendar](/samples/react-calendar//assets/screen6.png)
![callendar](/samples/react-calendar/assets/screen6.png)
![callendar](/samples/react-calendar//assets/screen7.png)
![callendar](/samples/react-calendar/assets/screen7.png)
![callendar](/samples/react-calendar/assets/screen8.png)
![callendar](/samples/react-calendar//assets/screen9.png)
![callendar](/samples/react-calendar/assets/screen9.png)
##
## Used SharePoint Framework Version
![drop](https://img.shields.io/badge/version-GA-green.svg)
![drop](https://img.shields.io/badge/version-1.8.2-green.svg)
## Applies to
* [SharePoint Framework](https:/dev.office.com/sharepoint)
* [Office 365 tenant](https://dev.office.com/sharepoint/docs/spfx/set-up-your-development-environment)
> Update accordingly as needed.
## WebPart Properties
Property |Type|Required| comments
--------------------|----|--------|----------
Site Url of Calendar List | Text| yes|
Calendar list| Text| yes| this is filled with all list of type "event list" created
Calendar list| Choice/Dropdown | yes| this is filled with all list of type "event list" created
Start Date | Date | yes | Event Date
End Date| Date| yes | Event Date
@ -103,6 +115,7 @@ Calendar Web Part|João Mendes
Version|Date|Comments
-------|----|--------
1.0.0|April 25, 2019|Initial release
1.0.1|June 10, 2019|update add recurrence events
## 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.**
@ -122,4 +135,4 @@ Version|Date|Comments
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/readme-template" />
<img src="https://telemetry.sharepointpnp.com/sp-dev-fx-webparts/samples/react-calendar" />

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

View File

@ -8,6 +8,7 @@
"entrypoint": "./lib/webparts/calendar/CalendarWebPart.js",
"manifest": "./src/webparts/calendar/CalendarWebPart.manifest.json"
}
]
}
},

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
{
"name": "react-calendar",
"version": "1.0.0",
"version": "1.0.1",
"private": true,
"engines": {
"node": ">=0.10.0"
@ -14,12 +14,11 @@
"test:watch": "./node_modules/.bin/jest --config ./config/jest.config.json --watchAll"
},
"dependencies": {
"@microsoft/rush-stack-compiler-3.2": "^0.3.6",
"@microsoft/sp-core-library": "1.8.0-plusbeta",
"@microsoft/sp-lodash-subset": "1.8.0-plusbeta",
"@microsoft/sp-office-ui-fabric-core": "1.8.0-plusbeta",
"@microsoft/sp-property-pane": "1.8.0-plusbeta",
"@microsoft/sp-webpart-base": "1.8.0-plusbeta",
"@microsoft/sp-core-library": "1.8.2",
"@microsoft/sp-lodash-subset": "1.8.2",
"@microsoft/sp-office-ui-fabric-core": "1.8.2",
"@microsoft/sp-property-pane": "1.8.2",
"@microsoft/sp-webpart-base": "1.8.2",
"@pnp/pnpjs": "^1.3.0",
"@pnp/spfx-controls-react": "1.12.0",
"@pnp/spfx-property-controls": "1.14.1",
@ -29,32 +28,37 @@
"@types/jquery": "^3.3.29",
"@types/react": "16.7.22",
"@types/react-big-calendar": "^0.20.13",
"@types/react-dom": "16.0.5",
"@types/react-dom": "16.8.0",
"@types/webpack-env": "1.13.1",
"@uifabric/fluent-theme": "^0.16.7",
"draft-js": "^0.10.5",
"draftjs-to-html": "^0.8.4",
"globalize": "^1.4.2",
"immutable": "^4.0.0-rc.12",
"jquery": "^3.3.1",
"moment": "^2.24.0",
"moment-timezone": "^0.5.25",
"office-ui-fabric-react": "6.143.0",
"react": "16.7.0",
"react-big-calendar": "^0.20.4",
"react-dom": "16.7.0",
"react-draft-wysiwyg": "^1.13.2",
"typescript": "^3.2.4"
"spfx-uifabric-themes": "^0.6.0",
"typescript": "^3.2.4",
"xml2js": "^0.4.19"
},
"resolutions": {
"@types/react": "16.4.2"
"@types/react": "16.7.22"
},
"devDependencies": {
"@microsoft/rush-stack-compiler-2.7": "0.4.0",
"@microsoft/sp-build-web": "1.8.0-plusbeta",
"@microsoft/sp-module-interfaces": "1.8.0-plusbeta",
"@microsoft/sp-tslint-rules": "1.8.0-plusbeta",
"@microsoft/sp-webpart-workbench": "1.8.0-plusbeta",
"@microsoft/rush-stack-compiler-2.9": "0.7.7",
"@microsoft/rush-stack-compiler-3.2": "0.3.17",
"@microsoft/sp-build-web": "1.8.2",
"@microsoft/sp-module-interfaces": "1.8.2",
"@microsoft/sp-tslint-rules": "1.8.2",
"@microsoft/sp-webpart-workbench": "1.8.2",
"@types/chai": "3.4.34",
"@types/mocha": "2.2.38",
"@types/xml2js": "^0.4.4",
"@voitanos/jest-preset-spfx-react16": "^1.1.0",
"ajv": "~5.2.2",
"gulp": "~3.9.1",

View File

@ -15,7 +15,6 @@
.description:hover {
border-color: rgb( 51, 51, 51 );
}
.calendar {
.container {
max-width: 100%;
@ -23,13 +22,11 @@
height: 600px;
margin: 0px auto;
}
.eventTitle {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.row {
@include ms-Grid-row;
@include ms-fontColor-white;
@ -37,7 +34,6 @@
padding: 20px;
border-style: solid;
}
.column {
@include ms-Grid-col;
@include ms-lg10;
@ -45,22 +41,18 @@
@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;
@ -84,6 +76,7 @@
cursor: pointer;
display: inline-block;
padding: 0 16px;
}
.label {
font-weight: $ms-font-weight-semibold;
@ -94,5 +87,4 @@
vertical-align: top;
display: inline-block;
}
}
}

View File

@ -25,4 +25,8 @@ export interface IEventState {
userPermissions?: IUserPermissions;
isloading:boolean;
siteRegionalSettings: any;
recurrenceSeriesEdited?:boolean;
showRecurrenceSeriesInfo:boolean;
newRecurrenceEvent:boolean;
recurrenceAction:string;
}

View File

@ -37,12 +37,14 @@ import {
Dialog,
DialogType,
DialogFooter,
Toggle
Toggle,
ActionButton,
IButtonProps
}
from 'office-ui-fabric-react';
import { addMonths, addYears } from 'office-ui-fabric-react/lib/utilities/dateMath/DateMath';
import { _ComponentBaseKillSwitches } from '@microsoft/sp-component-base';
import { IPanelModelEnum } from './IPanelModeEnum';
import { EditorState, convertToRaw, ContentState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg';
@ -51,11 +53,13 @@ import htmlToDraft from 'html-to-draftjs';
import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import spservices from '../../services/spservices';
import { Map, ICoordinates, MapType } from "@pnp/spfx-controls-react/lib/Map";
import { EventRecurrenceInfo } from '../../controls/EventRecurrenceInfo/EventRecurrenceInfo';
import { string } from 'prop-types';
import { getGUID } from '@pnp/common';
const today: Date = new Date(Date.now());
const DayPickerStrings: IDatePickerStrings = {
months: [strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July, strings.August, strings.September, strings.October, strings.November, strings.Dezember],
months: [strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July, strings.August, strings.September, strings.October, strings.November, strings.December],
shortMonths: [strings.Jan, strings.Feb, strings.Mar, strings.Apr, strings.May, strings.Jun, strings.Jul, strings.Aug, strings.Sep, strings.Oct, strings.Nov, strings.Dez],
days: [strings.Sunday, strings.Monday, strings.Tuesday, strings.Wednesday, strings.Thursday, strings.Friday, strings.Saturday],
shortDays: [strings.ShortDay_S, strings.ShortDay_M, strings.ShortDay_T, strings.ShortDay_W, strings.ShortDay_Tursday, strings.ShortDay_Friday, strings.ShortDay_Saunday],
@ -74,6 +78,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
private attendees: IPersonaProps[] = [];
private latitude: number = 41.1931819;
private longitude: number = -8.4897452;
private returnedRecurrenceInfo: { recurrenceData: string, eventDate: Date, endDate: Date } = undefined;
private categoryDropdownOption: IDropdownOption[] = [];
@ -93,7 +98,6 @@ export class Event extends React.Component<IEventProps, IEventState> {
}
// Initialize Map coordinates
console.log('ini', this.latitude, this.longitude);
this.state = {
showPanel: false,
eventData: this.props.event,
@ -112,6 +116,10 @@ export class Event extends React.Component<IEventProps, IEventState> {
displayDialog: false,
isloading: false,
siteRegionalSettings: undefined,
recurrenceSeriesEdited: false,
showRecurrenceSeriesInfo:false,
newRecurrenceEvent:false,
recurrenceAction: 'display',
userPermissions: { hasPermissionAdd: false, hasPermissionDelete: false, hasPermissionEdit: false, hasPermissionView: false },
};
// local copia of props
@ -131,8 +139,9 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.onDelete = this.onDelete.bind(this);
this.closeDialog = this.closeDialog.bind(this);
this.confirmDelete = this.confirmDelete.bind(this);
this.onAllDayEventChange = this.onAllDayEventChange.bind(this);
this.onCategoryChanged = this.onCategoryChanged.bind(this);
this.onEditRecurrence = this.onEditRecurrence.bind(this);
this.returnRecurrenceInfo = this.returnRecurrenceInfo.bind(this);
this.spService = new spservices(this.props.context);
}
/**
@ -152,27 +161,58 @@ export class Event extends React.Component<IEventProps, IEventState> {
private async onSave() {
let eventData: IEventData = this.state.eventData;
// All Day event ?
let panelMode = this.props.panelMode;
let startDate: string = null;
let endDate: string = null;
eventData.fRecurrence = false;
// if there are new Event recurrence or Edited recurrence series
if (this.state.recurrenceSeriesEdited || this.state.newRecurrenceEvent) {
eventData.RecurrenceData = this.returnedRecurrenceInfo.recurrenceData;
startDate = `${moment(this.returnedRecurrenceInfo.eventDate).format('YYYY/MM/DD')}`;
endDate = `${moment(this.returnedRecurrenceInfo.endDate).format('YYYY/MM/DD')}`;
if (eventData.EventType == "0" && this.state.newRecurrenceEvent) {
eventData.EventType = "1";
eventData.fRecurrence= true;
eventData.UID = getGUID();
}
if (eventData.EventType == "1" && this.state.recurrenceSeriesEdited) {
eventData.fRecurrence= true;
eventData.UID = getGUID();
}
} else {
if (this.state.eventData.EventType == '1'){ // recurrence exception
eventData.RecurrenceID = eventData.EventDate.toString();
eventData.MasterSeriesItemID = eventData.ID.toString();
eventData.EventType = "4";
eventData.fRecurrence = true;
eventData.UID = getGUID();
panelMode = IPanelModelEnum.add;
}
startDate = `${moment(this.state.startDate).format('YYYY/MM/DD')}`;
endDate = `${moment(this.state.endDate).format('YYYY/MM/DD')}`;
}
const startDate = `${moment(this.state.startDate).format('YYYY/MM/DD')}`;
const startTime = `${this.state.startSelectedHour.key}:${this.state.startSelectedMin.key}`;
const startDateTime = `${startDate} ${startTime}`;
const start = moment(startDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.start = new Date(start);
eventData.EventDate = new Date(start);
// End Date
const endDate = `${moment(this.state.endDate).format('YYYY/MM/DD')}`;
const endTime = `${this.state.endSelectedHour.key}:${this.state.endSelectedMin.key}`;
const endDateTime = `${endDate} ${endTime}`;
const end = moment(endDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.end = new Date(end);
eventData.EndDate = new Date(end);
// get Geolocation
eventData.geolocation = { Latitude: this.latitude, Longitude: this.longitude };
const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude);
eventData.location = locationInfo ? locationInfo.display_name : 'N/A';
console.log('beforeupd',eventData.geolocation);
// get Attendees
if (!eventData.attendes) { //vinitialize if no attendees
eventData.attendes = [];
@ -184,13 +224,13 @@ export class Event extends React.Component<IEventProps, IEventState> {
try {
for (const user of this.attendees) {
const userInfo: any= await this.spService.getUserByLoginName(user.id, this.props.siteUrl);
eventData.attendes.push(parseInt(userInfo.Id));
const userInfo: any = await this.spService.getUserByLoginName(user.id, this.props.siteUrl);
eventData.attendes.push(Number(userInfo.Id));
}
this.setState({ isSaving: true });
switch (this.props.panelMode) {
switch (panelMode) {
case IPanelModelEnum.edit:
await this.spService.updateEvent(eventData, this.props.siteUrl, this.props.listId);
break;
@ -215,16 +255,22 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event
*/
public componentDidCatch(error: any, errorInfo: any) {
this.setState({ hasError: true, errorMessage: errorInfo.componentStack });
this.setState({ hasError: true, errorMessage: errorInfo.message });
}
/**
*
*
* @private
* @param {number} [eventId]
* @memberof Event
*/
public async componentDidMount() {
private async renderEventData(eventId?: number) {
this.setState({ isloading: true });
let editorState:EditorState;
const event: IEventData = !eventId ? this.props.event : await this.spService.getEvent(this.props.siteUrl, this.props.listId, eventId);
let editorState: EditorState;
// Load Regional Settings
const siteRegionalSettigns = await this.spService.getSiteRegionalSettingsTimeZone(this.props.siteUrl);
// chaeck User list Permissions
@ -232,16 +278,16 @@ export class Event extends React.Component<IEventProps, IEventState> {
// Load Categories
this.categoryDropdownOption = await this.spService.getChoiceFieldOptions(this.props.siteUrl, this.props.listId, 'Category');
// Edit Mode ?
if (this.props.panelMode == IPanelModelEnum.edit && this.props.event) {
if (this.props.panelMode == IPanelModelEnum.edit && event) {
// Get hours of event
const startHour = moment(this.props.event.start).format('HH').toString();
const startMin = moment(this.props.event.start).format('mm').toString();
const endHour = moment(this.props.event.end).format('HH').toString();
const endMin = moment(this.props.event.end).format('mm').toString();
const startHour = moment(event.EventDate).format('HH').toString();
const startMin = moment(event.EventDate).format('mm').toString();
const endHour = moment(event.EndDate).format('HH').toString();
const endMin = moment(event.EndDate).format('mm').toString();
// Get Descript and covert to RichText Control
const html = this.props.event.Description;
const html = event.Description;
const contentBlock = htmlToDraft(html);
if (contentBlock) {
@ -250,7 +296,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
}
// testa attendees
const attendees = this.props.event.attendes;
const attendees = event.attendes;
let selectedUsers: string[] = [];
if (attendees && attendees.length > 0) {
for (const userId of attendees) {
@ -261,14 +307,16 @@ export class Event extends React.Component<IEventProps, IEventState> {
}
}
// Has geolocation ?
this.latitude = this.props.event.geolocation && this.props.event.geolocation.Latitude ? this.props.event.geolocation.Latitude : this.latitude;
this.longitude = this.props.event.geolocation && this.props.event.geolocation.Longitude ? this.props.event.geolocation.Longitude : this.longitude;
this.latitude = event.geolocation && event.geolocation.Latitude ? event.geolocation.Latitude : this.latitude;
this.longitude = event.geolocation && event.geolocation.Longitude ? event.geolocation.Longitude : this.longitude;
event.geolocation.Latitude = this.latitude;
event.geolocation.Longitude = this.longitude;
// Update Component Data
this.setState({
eventData: this.props.event,
startDate: this.props.event.start,
endDate: this.props.event.end,
eventData: event,
startDate: event.EventDate,
endDate: event.EndDate,
startSelectedHour: { key: startHour, text: startHour },
startSelectedMin: { key: startMin, text: startMin },
endSelectedHour: { key: endHour, text: endHour },
@ -290,24 +338,30 @@ export class Event extends React.Component<IEventProps, IEventState> {
userPermissions: userListPermissions,
isloading: false,
siteRegionalSettings: siteRegionalSettigns,
eventData: { ...event, EventType: "0" },
});
}
}
/**
*
*
* @memberof Event
*/
public componentWillMount() {
public async componentDidMount() {
await this.renderEventData();
}
/**
* @private
* @memberof Event
*/
private onStartChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
ev.preventDefault();
this.setState({ startSelectedHour: item });
}
@ -316,7 +370,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event
*/
private onEndChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
ev.preventDefault();
this.setState({ endSelectedHour: item });
}
@ -325,7 +379,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event
*/
private onStartChangeMin = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
ev.preventDefault();
this.setState({ startSelectedMin: item });
}
@ -374,12 +428,11 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event
*/
private onEndChangeMin(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
ev.preventDefault();
this.setState({ endSelectedMin: item });
}
/**
*
*
* @private
* @param {React.FormEvent<HTMLDivElement>} ev
@ -387,7 +440,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event
*/
private onCategoryChanged(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
ev.preventDefault();
this.setState({ eventData: { ...this.state.eventData, Category: item.text } });
}
@ -413,6 +466,13 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.setState({ displayDialog: false });
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLDivElement>} ev
* @memberof Event
*/
private async confirmDelete(ev: React.MouseEvent<HTMLDivElement>) {
ev.preventDefault();
try {
@ -420,7 +480,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
switch (this.props.panelMode) {
case IPanelModelEnum.edit:
await this.spService.deleteEvent(this.state.eventData, this.props.siteUrl, this.props.listId);
await this.spService.deleteEvent(this.state.eventData, this.props.siteUrl, this.props.listId, this.state.recurrenceSeriesEdited);
break;
default:
break;
@ -428,7 +488,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.setState({ isDeleting: false });
this.props.onDissmissPanel(true);
} catch (error) {
this.setState({ hasError: true, errorMessage: error.message, isDeleting: false });
this.setState({ hasError: true, errorMessage: error.message, isDeleting: false, displayDialog:false });
}
}
@ -445,14 +505,20 @@ export class Event extends React.Component<IEventProps, IEventState> {
</DefaultButton>
{
this.props.panelMode == IPanelModelEnum.edit && this.state.userPermissions.hasPermissionDelete && (
<DefaultButton onClick={this.onDelete} style={{ marginBottom: '15px', marginRight: '8px', float: 'right' }}>
<DefaultButton
onClick={this.onDelete}
style={{ marginBottom: '15px', marginRight: '8px', float: 'right' }}>
{strings.DeleteButtonLabel}
</DefaultButton>
)
}
{
(this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit) &&
<PrimaryButton disabled={this.state.disableButton} onClick={this.onSave} style={{ marginBottom: '15px', marginRight: '8px', float: 'right' }}>
<PrimaryButton
disabled={this.state.disableButton}
onClick={this.onSave}
style={{ marginBottom: '15px', marginRight: '8px', float: 'right' }}>
{strings.SaveButtonLabel}
</PrimaryButton>
@ -485,10 +551,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
}
private onAllDayEventChange(ev: React.MouseEvent<HTMLElement>, checked: boolean) {
ev.preventDefault();
this.setState({ eventData: { ...this.state.eventData, allDayEvent: checked } });
}
/**
*
* @private
@ -498,13 +561,45 @@ export class Event extends React.Component<IEventProps, IEventState> {
private async onUpdateCoordinates(coordinates: ICoordinates) {
this.latitude = coordinates.latitude;
this.longitude = coordinates.longitude;
console.log('upcoor',this.latitude + ' ' + this.longitude);
const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude);
this.setState({ eventData: { ...this.state.eventData, location: locationInfo.display_name } });
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof Event
*/
private async onEditRecurrence(ev: React.MouseEvent<HTMLButtonElement>) {
ev.preventDefault();
// EventType = 4 Recurrence Exception
await this.renderEventData(this.state.eventData.EventType == '4' ? Number(this.state.eventData.MasterSeriesItemID) : this.state.eventData.Id);
this.setState({ showRecurrenceSeriesInfo: true, recurrenceSeriesEdited: true });
}
/**
*
*
* @param {Date} startDate
* @param {string} recurrenceData
* @memberof Event
*/
public async returnRecurrenceInfo(startDate: Date, recurrenceData: string) {
this.returnedRecurrenceInfo = { recurrenceData: recurrenceData, eventDate: startDate, endDate: moment().add(20, 'years').toDate() };
//this.setState({ editRecurrenceSeries:false})
//console.log(this.returnedRecurrenceInfo);
}
/**
*
*
* @returns {React.ReactElement<IEventProps>}
* @memberof Event
*/
public render(): React.ReactElement<IEventProps> {
console.log(this.state.locationLatitude + '-' + this.state.locationLongitude);
const { editorState } = this.state;
return (
<div>
@ -531,7 +626,23 @@ export class Event extends React.Component<IEventProps, IEventState> {
{
!this.state.isloading &&
<div>
{
(this.state.eventData && (this.state.eventData.EventType !== "0" && this.state.showRecurrenceSeriesInfo !== true)) ?
<div>
<h2 style={{ display: 'inline-block', verticalAlign: 'top' }}>Recurrence Event</h2>
<DefaultButton
style={{ display: 'inline-block', marginLeft: '330px', verticalAlign: 'top', width: 'auto' }}
iconProps={{ iconName: 'RecurringEvent' }}
allowDisabledFocus={true}
onClick={this.onEditRecurrence}
>
Edit Recurrence Series
</DefaultButton>
</div>
: ''
}
<div style={{ marginTop: 10 }} >
<TextField
label={strings.EventTitleLabel}
value={this.state.eventData ? this.state.eventData.title : ''}
@ -562,6 +673,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
label={strings.StartDateLabel}
onSelectDate={this.onSelectDateStart}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
hidden={this.state.showRecurrenceSeriesInfo}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
@ -632,6 +744,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
label={strings.EndDateLabel}
onSelectDate={this.onSelectDateEnd}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
hidden={this.state.showRecurrenceSeriesInfo}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
@ -693,7 +806,42 @@ export class Event extends React.Component<IEventProps, IEventState> {
</div>
<Label>{this.state.siteRegionalSettings ? this.state.siteRegionalSettings.Description : ''}</Label>
<br />
<Label>Event Description</Label>
{
this.state.eventData && (this.state.eventData.EventType == "0") ?
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '200px' }}>
<Toggle
defaultChecked={false}
inlineLabel={true}
label="Recurrence ?"
onText="On"
offText="Off"
onChange={(ev, checked: boolean) => {
ev.preventDefault();
this.setState({ showRecurrenceSeriesInfo: checked, newRecurrenceEvent: checked });
}}
/>
</div>
:
''
}
{
this.state.showRecurrenceSeriesInfo && (
<EventRecurrenceInfo
context={this.props.context}
display={true}
recurrenceData={this.state.eventData.RecurrenceData}
startDate={this.state.startDate}
siteUrl={this.props.siteUrl}
returnRecurrenceData={this.returnRecurrenceInfo}
>
</EventRecurrenceInfo>
)
}
< Label > Event Description</Label>
<div className={styles.description}>
<Editor
@ -760,7 +908,9 @@ export class Event extends React.Component<IEventProps, IEventState> {
</DialogFooter>
</Dialog>
</div>
}
</Panel>
</div>
);

View File

@ -0,0 +1,7 @@
.divWrraper {
border-width:1px;
border-color:#adadad;
padding: 20px;
border-style: solid;
}

View File

@ -0,0 +1,172 @@
import * as React from 'react';
import styles from './EventRecurrenceInfo.module.scss';
import * as strings from 'CalendarWebPartStrings';
import { IEventRecurrenceInfoProps } from './IEventRecurrenceInfoProps';
import { IEventRecurrenceInfoState } from './IEventRecurrenceInfoState';
import { escape } from '@microsoft/sp-lodash-subset';
import * as moment from 'moment';
import { parseString, Builder } from "xml2js";
import {
ChoiceGroup,
IChoiceGroupOption,
} from 'office-ui-fabric-react';
import { EventRecurrenceInfoDaily } from './../EventRecurrenceInfoDaily/EventRecurrenceInfoDaily';
import { EventRecurrenceInfoWeekly } from './../EventRecurrenceInfoWeekly/EventRecurrenceInfoWeekly';
import { EventRecurrenceInfoMonthly } from './../EventRecurrenceInfoMonthly/EventRecurrenceInfoMonthly';
import { EventRecurrenceInfoYearly } from './../EventRecurrenceInfoYearly/EventRecurrenceInfoYearly';
export class EventRecurrenceInfo extends React.Component<IEventRecurrenceInfoProps, IEventRecurrenceInfoState> {
public constructor(props) {
super(props);
this._onRecurrenceFrequenceChange = this._onRecurrenceFrequenceChange.bind(this);
this.state = {
selectedKey: 'daily',
selectPatern: 'every',
startDate: moment().toDate(),
endDate: moment().endOf('month').toDate(),
numberOcurrences: '1',
numberOfDays: '1',
disableNumberOfDays: false,
disableNumberOcurrences: true,
selectdateRangeOption: 'noDate',
disableEndDate: true,
selectedRecurrenceRule: 'daily',
};
}
private _onRecurrenceFrequenceChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
this.setState({
selectedRecurrenceRule: option.key
});
}
/**
*
*
* @memberof EventRecurrenceInfo
*/
public async componentDidMount() {
if (this.props.recurrenceData) {
if (this.props.recurrenceData.indexOf('<daily') != -1) {
this.setState({ selectedRecurrenceRule: 'daily' });
}
if (this.props.recurrenceData.indexOf('<weekly') != -1) {
this.setState({ selectedRecurrenceRule: 'weekly' });
}
if (this.props.recurrenceData.indexOf('<monthly') != -1) {
this.setState({ selectedRecurrenceRule: 'monthly' });
}
if (this.props.recurrenceData.indexOf('<monthlyByDay') != -1) {
this.setState({ selectedRecurrenceRule: 'monthly' });
}
if (this.props.recurrenceData.indexOf('<yearly') != -1) {
this.setState({ selectedRecurrenceRule: 'yearly' });
}
}
}
/**
*
*
* @returns {React.ReactElement<IEventRecurrenceInfoProps>}
* @memberof EventRecurrenceInfo
*/
public render(): React.ReactElement<IEventRecurrenceInfoProps> {
return (
<div className={styles.divWrraper} >
<div style={{ display: 'inline-block', verticalAlign: 'top' }}>
<ChoiceGroup
label="Recurrence Information"
selectedKey={this.state.selectedRecurrenceRule}
options={[
{
key: 'daily',
iconProps: { iconName: 'CalendarDay' },
text: 'Daily'
},
{
key: 'weekly',
iconProps: { iconName: 'CalendarWeek' },
text: 'Weekly'
},
{
key: 'monthly',
iconProps: { iconName: 'Calendar' },
text: 'Monthly',
},
{
key: 'yearly',
iconProps: { iconName: 'Calendar' },
text: 'Yearly',
}
]}
onChange={this._onRecurrenceFrequenceChange}
/>
</div>
{
this.state.selectedRecurrenceRule === 'daily' && (
<EventRecurrenceInfoDaily
display={true}
recurrenceData={this.props.recurrenceData}
startDate={this.props.startDate}
context={this.props.context}
siteUrl={this.props.siteUrl}
returnRecurrenceData={this.props.returnRecurrenceData}
/>
)
}
{
this.state.selectedRecurrenceRule === 'weekly' && (
<EventRecurrenceInfoWeekly
display={true}
recurrenceData={this.props.recurrenceData}
startDate={this.props.startDate}
context={this.props.context}
siteUrl={this.props.siteUrl}
returnRecurrenceData={this.props.returnRecurrenceData}
/>
)
}
{
this.state.selectedRecurrenceRule === 'monthly' && (
<EventRecurrenceInfoMonthly
display={true}
recurrenceData={this.props.recurrenceData}
startDate={this.props.startDate}
context={this.props.context}
siteUrl={this.props.siteUrl}
returnRecurrenceData={this.props.returnRecurrenceData}
/>
)
}
{
this.state.selectedRecurrenceRule === 'yearly' && (
<EventRecurrenceInfoYearly
display={true}
recurrenceData={this.props.recurrenceData}
startDate={this.props.startDate}
context={this.props.context}
siteUrl={this.props.siteUrl}
returnRecurrenceData={this.props.returnRecurrenceData}
/>
)
}
</div>
);
}
}

View File

@ -0,0 +1,9 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IEventRecurrenceInfoProps {
display:boolean;
recurrenceData: string;
startDate:Date;
context:WebPartContext;
siteUrl:string;
returnRecurrenceData: (startDate:Date,recurrenceData:string) => void;
}

View File

@ -0,0 +1,13 @@
export interface IEventRecurrenceInfoState {
selectedKey:string;
selectPatern:string;
startDate: Date;
endDate:Date;
numberOcurrences:string;
numberOfDays:string;
disableNumberOfDays: boolean;
disableNumberOcurrences: boolean;
selectdateRangeOption:string;
disableEndDate:boolean;
selectedRecurrenceRule:string;
}

View File

@ -0,0 +1,6 @@
.divWrraper {
border-width:1px;
border-color:#adadad;
padding: 20px;
border-style: solid;
}

View File

@ -0,0 +1,408 @@
import * as React from 'react';
import styles from './EventRecurrenceInfoDaily.module.scss';
import * as strings from 'CalendarWebPartStrings';
import { IEventRecurrenceInfoDailyProps } from './IEventRecurrenceInfoDailyProps';
import { IEventRecurrenceInfoDailyState } from './IEventRecurrenceInfoDailyState';
import { escape } from '@microsoft/sp-lodash-subset';
import * as moment from 'moment';
import { parseString, Builder } from "xml2js";
import {
ChoiceGroup,
IChoiceGroupOption,
Label,
MaskedTextField,
} from 'office-ui-fabric-react';
import { DatePicker, DayOfWeek, IDatePickerStrings } from 'office-ui-fabric-react/lib/DatePicker';
import spservices from '../../services/spservices';
const DayPickerStrings: IDatePickerStrings = {
months: [strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July, strings.August, strings.September, strings.October, strings.November, strings.December],
shortMonths: [strings.Jan, strings.Feb, strings.Mar, strings.Apr, strings.May, strings.Jun, strings.Jul, strings.Aug, strings.Sep, strings.Oct, strings.Nov, strings.Dez],
days: [strings.Sunday, strings.Monday, strings.Tuesday, strings.Wednesday, strings.Thursday, strings.Friday, strings.Saturday],
shortDays: [strings.ShortDay_S, strings.ShortDay_M, strings.ShortDay_T, strings.ShortDay_W, strings.ShortDay_Tursday, strings.ShortDay_Friday, strings.ShortDay_Saunday],
goToToday: strings.GoToDay,
prevMonthAriaLabel: strings.PrevMonth,
nextMonthAriaLabel: strings.NextMonth,
prevYearAriaLabel: strings.PrevYear,
nextYearAriaLabel: strings.NextYear,
closeButtonAriaLabel: strings.CloseDate,
isRequiredErrorMessage: strings.IsRequired,
invalidInputErrorMessage: strings.InvalidDateFormat,
};
/**
*
*
* @export
* @class EventRecurrenceInfoDaily
* @extends {React.Component<IEventRecurrenceInfoDailyProps, IEventRecurrenceInfoDailyState>}
*/
export class EventRecurrenceInfoDaily extends React.Component<IEventRecurrenceInfoDailyProps, IEventRecurrenceInfoDailyState> {
private spService: spservices = null;
public constructor(props) {
super(props);
this.onPaternChange = this.onPaternChange.bind(this);
this.state = {
selectedKey: 'daily',
selectPatern: 'every',
startDate: this.props.startDate ? this.props.startDate : moment().toDate(),
endDate: moment().endOf('month').toDate(),
numberOcurrences: '1',
numberOfDays: '1',
disableNumberOfDays: false,
disableNumberOcurrences: true,
selectdateRangeOption: 'noDate',
disableEndDate: true,
selectedRecurrenceRule: 'daily',
isLoading: false,
errorMessageNumberOcurrences: '',
errorMessageNumberOfDays: '',
};
//
this.onNumberOfDaysChange = this.onNumberOfDaysChange.bind(this);
this.onNumberOfOcurrencesChange = this.onNumberOfOcurrencesChange.bind(this);
this.onDataRangeOptionChange = this.onDataRangeOptionChange.bind(this);
this.onEndDateChange = this.onEndDateChange.bind(this);
this.onStartDateChange = this.onStartDateChange.bind(this);
this.onApplyRecurrence = this.onApplyRecurrence.bind(this);
this.spService = new spservices(this.props.context);
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onStartDateChange(date: Date) {
this.setState({ startDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onEndDateChange(date: Date) {
this.setState({ endDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onNumberOfDaysChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
let errorMessage = '';
setTimeout(() => {
if (Number(value.trim()) == 0 || Number(value.trim()) > 255) {
value = '1 ';
errorMessage = 'Allowed values 1 to 255';
}
this.setState({ numberOfDays: value, errorMessageNumberOfDays: errorMessage });
this.applyRecurrence();
}, 2500);
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onNumberOfOcurrencesChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
let errorMessage = '';
setTimeout(() => {
if (Number(value.trim()) == 0 || Number(value.trim()) > 999) {
value = '1 ';
errorMessage = 'Allowed values 1 to 999';
}
this.setState({ numberOcurrences: value , errorMessageNumberOcurrences: errorMessage });
this.applyRecurrence();
}, 2500);
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {IChoiceGroupOption} option
* @memberof EventRecurrenceInfoDaily
*/
private onDataRangeOptionChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectdateRangeOption: option.key,
disableNumberOcurrences: option.key == 'endAfter' ? false : true,
disableEndDate: option.key == 'endDate' ? false : true,
});
this.applyRecurrence();
}
private onPaternChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectPatern: option.key,
disableNumberOfDays: option.key == 'every' ? false : true,
});
this.applyRecurrence();
}
public async componentWillMount() {
// await this.load();
await this.load();
}
public async componentDidUpdate(prevProps: IEventRecurrenceInfoDailyProps, prevState: IEventRecurrenceInfoDailyState) {
}
private async load() {
let patern: any = {};
let dateRange: { repeatForever?: string, repeatInstances?: string, windowEnd?: Date } = {};
let dailyPatern: { dayFrequency?: string, weekDay?: string } = {};
let recurrenceRule: string;
if (this.props.recurrenceData) {
parseString(this.props.recurrenceData, { explicitArray: false }, (error, result) => {
if (result.recurrence.rule.repeat) {
patern = result.recurrence.rule.repeat;
}
//
if (result.recurrence.rule.repeatForever) {
dateRange = { repeatForever: result.recurrence.rule.repeatForever };
}
if (result.recurrence.rule.repeatInstances) {
dateRange = { repeatInstances: result.recurrence.rule.repeatInstances };
}
if (result.recurrence.rule.windowEnd) {
dateRange = { windowEnd: result.recurrence.rule.windowEnd };
}
});
// daily Patern
if (patern.daily) {
recurrenceRule = 'daily';
if (patern.daily.$.dayFrequency) {
dailyPatern = { dayFrequency: patern.daily.$.dayFrequency };
}
if (patern.daily.$.weekday) {
dailyPatern = { weekDay: 'weekDay' };
}
}
let selectDateRangeOption: string = 'noDate';
if (dateRange.repeatForever) {
selectDateRangeOption = 'noDate';
} else if (dateRange.repeatInstances) {
selectDateRangeOption = 'endAfter';
} else if (dateRange.windowEnd) {
selectDateRangeOption = 'endDate';
}
// weekday patern
this.setState({
selectedRecurrenceRule: recurrenceRule,
selectPatern: dailyPatern.dayFrequency ? 'every' : 'everweekday',
numberOfDays: dailyPatern.dayFrequency ? dailyPatern.dayFrequency : '1',
disableNumberOfDays: dailyPatern.dayFrequency ? false : true,
selectdateRangeOption: selectDateRangeOption,
numberOcurrences: dateRange.repeatInstances ? dateRange.repeatInstances : '10',
disableNumberOcurrences: dateRange.repeatInstances ? false : true,
endDate: dateRange.windowEnd ? new Date(moment(dateRange.windowEnd).format('YYYY/MM/DD')) : this.state.endDate,
disableEndDate: dateRange.windowEnd ? false : true,
isLoading: false,
});
}
await this.applyRecurrence();
}
private async onApplyRecurrence(ev: React.MouseEvent<HTMLButtonElement>) {
await this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof EventRecurrenceInfoDaily
*/
private async applyRecurrence() {
const siteTimeZoneHours: number = await this.spService.getSiteTimeZoneHours(this.props.siteUrl);
const eventDate = new Date(moment(this.state.startDate).add(siteTimeZoneHours, 'hours').toISOString());
const endDate = moment(this.state.endDate).add(siteTimeZoneHours, 'hours').toISOString();
let selectDateRangeOption;
switch (this.state.selectdateRangeOption) {
case 'noDate':
selectDateRangeOption = `<repeatForever>FALSE</repeatForever>`;
break;
case 'endAfter':
selectDateRangeOption = `<repeatInstances>${this.state.numberOcurrences}</repeatInstances>`;
break;
case 'endDate':
selectDateRangeOption = `<windowEnd>${endDate}</windowEnd>`;
break;
default:
break;
}
const recurrenceXML = `<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat>` +
`<daily ${ this.state.selectPatern === 'every' ? `dayFrequency="${this.state.numberOfDays.trim()}"/>` : 'weekday'}</repeat>${selectDateRangeOption}</rule></recurrence>`;
// console.log(recurrenceXML);
this.props.returnRecurrenceData(this.state.startDate, recurrenceXML);
}
/**
*
*
* @returns {React.ReactElement<IEventRecurrenceInfoDailyProps>}
* @memberof EventRecurrenceInfoDaily
*/
public render(): React.ReactElement<IEventRecurrenceInfoDailyProps> {
return (
<div >
{
<div>
<div style={{ display: 'inline-block', float: 'right', paddingTop: '10px', height: '40px' }}>
</div>
<div style={{ width: '100%', paddingTop: '10px' }}>
<Label>Patern</Label>
<ChoiceGroup
selectedKey={this.state.selectPatern}
options={[
{
key: 'every',
text: strings.every,
ariaLabel: 'every',
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="999"
maskChar=' '
disabled={this.state.disableNumberOfDays}
value={this.state.numberOfDays}
errorMessage={this.state.errorMessageNumberOfDays}
onChange={this.onNumberOfDaysChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '60px', paddingLeft: '10px' } }}>{strings.days}</Label>
</div>
);
}
},
{
key: 'everweekday',
text: strings.everyweekdays,
}
]}
onChange={this.onPaternChange}
required={true}
/>
</div>
<div style={{ paddingTop: '22px' }}>
<Label>Date Range</Label>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: '35px', paddingTop: '10px' }}>
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
label={strings.StartDateLabel}
value={this.state.startDate}
onSelectDate={this.onStartDateChange}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingTop: '10px' }}>
<ChoiceGroup
selectedKey={this.state.selectdateRangeOption}
onChange={this.onDataRangeOptionChange}
options={[
{
key: 'noDate',
text: strings.noEndDate,
},
{
key: 'endDate',
text: strings.EndByLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel="Select a date"
style={{ display: 'inline-block', verticalAlign: 'top', paddingLeft: '22px', }}
onSelectDate={this.onEndDateChange}
value={this.state.endDate}
disabled={this.state.disableEndDate}
/>
</div>
);
}
},
{
key: 'endAfter',
text: strings.EndAfterLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="999"
maskChar=' '
value={this.state.numberOcurrences}
disabled={this.state.disableNumberOcurrences}
errorMessage={this.state.errorMessageNumberOcurrences}
onChange={this.onNumberOfOcurrencesChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', paddingLeft: '10px' } }}>Ocurrences</Label>
</div>
);
}
},
]}
required={true}
/>
</div>
</div>
</div>
}
</div>
);
}
}

View File

@ -0,0 +1,9 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IEventRecurrenceInfoDailyProps {
display:boolean;
recurrenceData: string;
startDate:Date;
context: WebPartContext;
siteUrl:string;
returnRecurrenceData: (startDate:Date,recurrenceData:string) => void;
}

View File

@ -0,0 +1,16 @@
export interface IEventRecurrenceInfoDailyState {
selectedKey:string;
selectPatern:string;
startDate: Date;
endDate:Date;
numberOcurrences:string;
numberOfDays:string;
disableNumberOfDays: boolean;
disableNumberOcurrences: boolean;
selectdateRangeOption:string;
disableEndDate:boolean;
selectedRecurrenceRule:string;
isLoading:boolean;
errorMessageNumberOfDays: string;
errorMessageNumberOcurrences: string;
}

View File

@ -0,0 +1,6 @@
.divWrraper {
border-width:1px;
border-color:#adadad;
padding: 20px;
border-style: solid;
}

View File

@ -0,0 +1,645 @@
import * as React from 'react';
import styles from './EventRecurrenceInfoMonthly.module.scss';
import * as strings from 'CalendarWebPartStrings';
import { IEventRecurrenceInfoMonthlyProps } from './IEventRecurrenceInfoMonthlyProps';
import { IEventRecurrenceInfoMonthlyState } from './IEventRecurrenceInfoMonthlyState';
import { escape } from '@microsoft/sp-lodash-subset';
import * as moment from 'moment';
import { parseString, Builder } from "xml2js";
import {
ChoiceGroup,
IChoiceGroupOption,
Dropdown,
IDropdownOption,
Label,
MaskedTextField,
} from 'office-ui-fabric-react';
import { DatePicker, DayOfWeek, IDatePickerStrings } from 'office-ui-fabric-react/lib/DatePicker';
import spservices from '../../services/spservices';
import { string } from 'prop-types';
const DayPickerStrings: IDatePickerStrings = {
months: [strings.January, strings.February, strings.March, strings.April, strings.May, strings.June, strings.July, strings.August, strings.September, strings.October, strings.November, strings.December],
shortMonths: [strings.Jan, strings.Feb, strings.Mar, strings.Apr, strings.May, strings.Jun, strings.Jul, strings.Aug, strings.Sep, strings.Oct, strings.Nov, strings.Dez],
days: [strings.Sunday, strings.Monday, strings.Tuesday, strings.Wednesday, strings.Thursday, strings.Friday, strings.Saturday],
shortDays: [strings.ShortDay_S, strings.ShortDay_M, strings.ShortDay_T, strings.ShortDay_W, strings.ShortDay_Tursday, strings.ShortDay_Friday, strings.ShortDay_Saunday],
goToToday: strings.GoToDay,
prevMonthAriaLabel: strings.PrevMonth,
nextMonthAriaLabel: strings.NextMonth,
prevYearAriaLabel: strings.PrevYear,
nextYearAriaLabel: strings.NextYear,
closeButtonAriaLabel: strings.CloseDate,
isRequiredErrorMessage: strings.IsRequired,
invalidInputErrorMessage: strings.InvalidDateFormat,
};
/**
*
*
* @export
* @class EventRecurrenceInfoDaily
* @extends {React.Component<IEventRecurrenceInfoMonthlyProps, IEventRecurrenceInfoMonthlyState>}
*/
export class EventRecurrenceInfoMonthly extends React.Component<IEventRecurrenceInfoMonthlyProps, IEventRecurrenceInfoMonthlyState> {
private spService: spservices = null;
public constructor(props) {
super(props);
this.onPaternChange = this.onPaternChange.bind(this);
this.state = {
selectedKey: 'daily',
selectPatern: 'monthly',
startDate: this.props.startDate ? this.props.startDate : moment().toDate(),
endDate: moment(this.props.startDate).add('month', 1).toDate(),
numberOcurrences: '10',
disableDayOfMonth: false,
disableNumberOcurrences: true,
selectdateRangeOption: 'noDate',
disableEndDate: true,
selectedRecurrenceRule: 'monthly',
dayOfMonth: this.props.startDate ? moment(this.props.startDate).format('D') : moment().format('D'),
everyNumberOfMonths: '1',
isLoading: false,
errorMessageNumberOfMonth: '',
errorMessageDayOfMonth: '',
selectedWeekOrderMonth: 'first',
selectedWeekDay: 'day',
errorMessageNumberOfMonthWeekDay: '',
everyNumberOfMonthsWeekDay: '1',
};
//
this.onDayOfMonthChange = this.onDayOfMonthChange.bind(this);
this.onNumberOfOcurrencesChange = this.onNumberOfOcurrencesChange.bind(this);
this.onDataRangeOptionChange = this.onDataRangeOptionChange.bind(this);
this.onEndDateChange = this.onEndDateChange.bind(this);
this.onStartDateChange = this.onStartDateChange.bind(this);
this.onApplyRecurrence = this.onApplyRecurrence.bind(this);
this.onDayOfMonthGetErrorMessage = this.onDayOfMonthGetErrorMessage.bind(this);
this.onEveryNumberOfMonthsChange = this.onEveryNumberOfMonthsChange.bind(this);
this.onEveryNumberOfMonthsWeekDayChange = this.onEveryNumberOfMonthsWeekDayChange.bind(this);
this.onSelectedWeekDayChange = this.onSelectedWeekDayChange.bind(this);
this.onWeekOrderMonthChange = this.onWeekOrderMonthChange.bind(this);
this.spService = new spservices(this.props.context);
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onStartDateChange(date: Date) {
this.setState({ startDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onEndDateChange(date: Date) {
this.setState({ endDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onDayOfMonthChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
setTimeout(() => {
let errorMessage = '';
if (Number(value.trim()) == 0 || Number(value.trim()) > 31) {
value = '1 ';
errorMessage = 'Allowed values 1 to 31';
}
this.setState({ dayOfMonth: value, errorMessageDayOfMonth: errorMessage });
this.applyRecurrence();
}, 2500);
}
/**
*
*
* @private
* @param {string} value
* @returns
* @memberof EventRecurrenceInfoMonthly
*/
private onDayOfMonthGetErrorMessage(value: string) {
return (Number(value.trim()) != 0 && Number(value.trim()) <= 31) ? '' : "Day must be beteween 1 and 31";
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoMonthly
*/
private onEveryNumberOfMonthsChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
setTimeout(() => {
let errorMessage = '';
if (Number(value.trim()) == 0 || Number(value.trim()) > 12) {
value = '1 ';
errorMessage = 'Allowed values 1 to 12';
}
this.setState({ everyNumberOfMonths: value, errorMessageNumberOfMonth: errorMessage });
this.applyRecurrence();
}, 2500);
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoMonthly
*/
private onEveryNumberOfMonthsWeekDayChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
setTimeout(() => {
let errorMessage = '';
if (Number(value.trim()) == 0 || Number(value.trim()) > 12) {
value = '1 ';
errorMessage = strings.AllowedValues1to12Label;
}
this.setState({ everyNumberOfMonthsWeekDay: value, errorMessageNumberOfMonthWeekDay: errorMessage });
this.applyRecurrence();
}, 2500);
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onNumberOfOcurrencesChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
this.setState({ numberOcurrences: value });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {IChoiceGroupOption} option
* @memberof EventRecurrenceInfoDaily
*/
private onDataRangeOptionChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectdateRangeOption: option.key,
disableNumberOcurrences: option.key == 'endAfter' ? false : true,
disableEndDate: option.key == 'endDate' ? false : true,
});
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {IChoiceGroupOption} option
* @memberof EventRecurrenceInfoMonthly
*/
private onPaternChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectPatern: option.key,
disableDayOfMonth: option.key == 'monthly' ? false : true,
});
this.applyRecurrence();
}
public async componentWillMount() {
await this.load();
}
/**
*
*
* @private
* @param {React.FormEvent<HTMLDivElement>} ev
* @param {IDropdownOption} item
* @memberof EventRecurrenceInfoMonthly
*/
private onWeekOrderMonthChange(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption):void {
this.setState({selectedWeekOrderMonth: item.text});
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.FormEvent<HTMLDivElement>} ev
* @param {IDropdownOption} item
* @memberof EventRecurrenceInfoMonthly
*/
private onSelectedWeekDayChange(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption):void {
this.setState({selectedWeekDay: item.key});
this.applyRecurrence();
}
public async componentDidUpdate(prevProps: IEventRecurrenceInfoMonthlyProps, prevState: IEventRecurrenceInfoMonthlyState) {
}
/**
*
*
* @private
* @memberof EventRecurrenceInfoMonthly
*/
private async load() {
let patern: any = {};
let dateRange: { repeatForever?: string, repeatInstances?: string, windowEnd?: Date } = {};
let monthlyPatern: { monthFrequency?: string, day?: string } = {};
let monthlyByDayPatern: { monthFrequency?: string, weekdayOfMonth?: string, weekDay?: string } = {};
let recurrenceRule: string;
if (this.props.recurrenceData) {
parseString(this.props.recurrenceData, { explicitArray: false }, (error, result) => {
if (result.recurrence.rule.repeat) {
patern = result.recurrence.rule.repeat;
}
//
if (result.recurrence.rule.repeatForever) {
dateRange = { repeatForever: result.recurrence.rule.repeatForever };
}
if (result.recurrence.rule.repeatInstances) {
dateRange = { repeatInstances: result.recurrence.rule.repeatInstances };
}
if (result.recurrence.rule.windowEnd) {
dateRange = { windowEnd: result.recurrence.rule.windowEnd };
}
});
// monthly Patern
if (patern.monthly) {
recurrenceRule = 'monthly';
if (patern.monthly.$.monthFrequency && patern.monthly.$.day) {
monthlyPatern = { monthFrequency: patern.monthly.$.monthFrequency, day: patern.monthly.$.day };
}
}
// monthlyByDay Patern
if (patern.monthlyByDay) {
recurrenceRule = 'monthly';
let weekDay = 'day';
if (patern.monthlyByDay.$.su) weekDay = 'sunday';
if (patern.monthlyByDay.$.mo) weekDay = 'monday';
if (patern.monthlyByDay.$.tu) weekDay = 'tuesday';
if (patern.monthlyByDay.$.we) weekDay = 'wednesday';
if (patern.monthlyByDay.$.th) weekDay = 'thursday';
if (patern.monthlyByDay.$.fr) weekDay = 'friday';
if (patern.monthlyByDay.$.sa) weekDay = 'saturday';
if (patern.monthlyByDay.$.day) weekDay = 'day';
if (patern.monthlyByDay.$.weekday) weekDay = 'weekday';
if (patern.monthlyByDay.$.weekend_day) weekDay = 'weekdendday';
monthlyByDayPatern = {
monthFrequency: patern.monthlyByDay.$.monthFrequency,
weekdayOfMonth: patern.monthlyByDay.$.weekdayOfMonth,
weekDay: weekDay,
};
}
let selectDateRangeOption: string = 'noDate';
if (dateRange.repeatForever) {
selectDateRangeOption = 'noDate';
} else if (dateRange.repeatInstances) {
selectDateRangeOption = 'endAfter';
} else if (dateRange.windowEnd) {
selectDateRangeOption = 'endDate';
}
// weekday patern
this.setState({
selectedRecurrenceRule: recurrenceRule,
selectPatern: patern.monthly ? 'monthly' : 'monthlyByDay',
dayOfMonth: monthlyPatern.day ? monthlyPatern.day : '1',
everyNumberOfMonths: monthlyPatern.monthFrequency ? monthlyPatern.monthFrequency : monthlyByDayPatern.monthFrequency ,
everyNumberOfMonthsWeekDay: monthlyByDayPatern.monthFrequency ? monthlyByDayPatern.monthFrequency : '1',
selectedWeekOrderMonth: monthlyByDayPatern.weekdayOfMonth ? monthlyByDayPatern.weekdayOfMonth : 'first',
selectedWeekDay: monthlyByDayPatern.weekDay ? monthlyByDayPatern.weekDay : 'day',
disableDayOfMonth: patern.monthly ? false : true,
selectdateRangeOption: selectDateRangeOption,
numberOcurrences: dateRange.repeatInstances ? dateRange.repeatInstances : '10',
disableNumberOcurrences: dateRange.repeatInstances ? false : true,
endDate: dateRange.windowEnd ? new Date(moment(dateRange.windowEnd).format('YYYY/MM/DD')) : this.state.endDate,
disableEndDate: dateRange.windowEnd ? false : true,
isLoading: false,
});
}
await this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof EventRecurrenceInfoMonthly
*/
private async onApplyRecurrence(ev: React.MouseEvent<HTMLButtonElement>) {
await this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof EventRecurrenceInfoDaily
*/
private async applyRecurrence() {
const siteTimeZoneHours: number = await this.spService.getSiteTimeZoneHours(this.props.siteUrl);
const eventDate = new Date(moment(this.state.startDate).add(siteTimeZoneHours, 'hours').toISOString());
const endDate = moment(this.state.endDate).add(siteTimeZoneHours, 'hours').toISOString();
let selectDateRangeOption;
switch (this.state.selectdateRangeOption) {
case 'noDate':
selectDateRangeOption = `<repeatForever>FALSE</repeatForever>`;
break;
case 'endAfter':
selectDateRangeOption = `<repeatInstances>${this.state.numberOcurrences}</repeatInstances>`;
break;
case 'endDate':
selectDateRangeOption = `<windowEnd>${endDate}</windowEnd>`;
break;
default:
break;
}
let recurrencePatern: string = '';
if (this.state.selectPatern == 'monthly') {
recurrencePatern = `<monthly monthFrequency="${this.state.everyNumberOfMonths}" day="${this.state.dayOfMonth}" /></repeat>${selectDateRangeOption}</rule></recurrence>`;
}
if (this.state.selectPatern == 'monthlyByDay') {
recurrencePatern = `<monthlyByDay weekdayOfMonth="${this.state.selectedWeekOrderMonth}" `;
switch (this.state.selectedWeekDay) {
case 'day':
recurrencePatern = recurrencePatern + `day="TRUE"`;
break;
case 'weekday':
recurrencePatern = recurrencePatern + `weekday="TRUE"`;
break;
case 'weekendday':
recurrencePatern = recurrencePatern + `weekend_day="TRUE"`;
break;
case 'sunday':
recurrencePatern = recurrencePatern + `su="TRUE"`;
break;
case 'monday':
recurrencePatern = recurrencePatern + `mo="TRUE"`;
break;
case 'tuesday':
recurrencePatern = recurrencePatern + `tu="TRUE"`;
break;
case 'wednesday':
recurrencePatern = recurrencePatern + `we="TRUE"`;
break;
case 'thursday':
recurrencePatern = recurrencePatern + `th="TRUE"`;
break;
case 'friday':
recurrencePatern = recurrencePatern + `fr="TRUE"`;
break;
case 'saturday':
recurrencePatern = recurrencePatern + `sa="TRUE"`;
break;
default:
break;
}
recurrencePatern = recurrencePatern + ` monthFrequency="${this.state.everyNumberOfMonthsWeekDay}" /></repeat>${selectDateRangeOption}</rule></recurrence>`;
}
const recurrenceXML = `<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat>` + recurrencePatern;
// console.log(recurrenceXML);
this.props.returnRecurrenceData(this.state.startDate, recurrenceXML);
}
/**
*
*
* @returns {React.ReactElement<IEventRecurrenceInfoDailyProps>}
* @memberof EventRecurrenceInfoDaily
*/
public render(): React.ReactElement<IEventRecurrenceInfoMonthlyProps> {
return (
<div >
{
<div>
<div style={{ display: 'inline-block', float: 'right', paddingTop: '10px', height: '40px' }}>
</div>
<div style={{ width: '100%', paddingTop: '10px' }}>
<Label>Patern</Label>
<ChoiceGroup
selectedKey={this.state.selectPatern}
options={[
{
key: 'monthly',
text: strings.dayLable,
ariaLabel: strings.dayLable,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="99"
maskChar=' '
disabled={this.state.disableDayOfMonth}
value={this.state.dayOfMonth}
errorMessage={this.state.errorMessageDayOfMonth}
onChange={this.onDayOfMonthChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '65px', paddingLeft: '10px' } }}>{strings.ofEveryLabel}</Label>
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="99"
maskChar=' '
disabled={this.state.disableDayOfMonth}
value={this.state.everyNumberOfMonths}
errorMessage={this.state.errorMessageNumberOfMonth}
onChange={this.onEveryNumberOfMonthsChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}>{strings.MonthsLabel}</Label>
</div>
);
}
},
{
key: 'monthlyByDay',
text: strings.theLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '90px', paddingLeft: '10px' }}>
<Dropdown
selectedKey={this.state.selectedWeekOrderMonth}
onChange={this.onWeekOrderMonthChange}
disabled={!this.state.disableDayOfMonth}
options={[
{ key: 'first', text: strings.firstLabel },
{ key: 'second', text:strings.secondLabel},
{ key: 'third', text: strings.thirdLabel },
{ key: 'fourth', text:strings.fourthLabel },
{ key: 'last', text: strings.lastLabel },
]}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '5px' }}>
<Dropdown
selectedKey={this.state.selectedWeekDay}
disabled={!this.state.disableDayOfMonth}
onChange={this.onSelectedWeekDayChange}
options={[
{ key: 'day', text: strings.dayLable },
{ key: 'weekday', text: strings.weekDayLabel },
{ key: 'weekendday', text:strings.weekEndDay },
{ key: 'sunday', text: strings.Sunday},
{ key: 'monday', text: strings.Monday },
{ key: 'tuesday', text: strings.Tuesday },
{ key: 'wednesday', text: strings.Wednesday },
{ key: 'thursday', text: strings.Thursday},
{ key: 'friday', text: strings.Friday },
{ key: 'saturday', text: strings.Saturday },
]}
/>
</div>
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '65px', paddingLeft: '10px' } }}>{strings.ofEveryLabel}</Label>
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="99"
maskChar=' '
disabled={!this.state.disableDayOfMonth}
value={this.state.everyNumberOfMonthsWeekDay}
errorMessage={this.state.errorMessageNumberOfMonthWeekDay}
onChange={this.onEveryNumberOfMonthsWeekDayChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '80px', paddingLeft: '10px' } }}>{strings.MonthsLabel}</Label>
</div>
);
}
}
]}
onChange={this.onPaternChange}
required={true}
/>
</div>
<div style={{ paddingTop: '22px' }}>
<Label>{strings.dateRangeLabel}</Label>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: '35px', paddingTop: '10px' }}>
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
label={strings.StartDateLabel}
value={this.state.startDate}
onSelectDate={this.onStartDateChange}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingTop: '10px' }}>
<ChoiceGroup
selectedKey={this.state.selectdateRangeOption}
onChange={this.onDataRangeOptionChange}
options={[
{
key: 'noDate',
text: strings.noEndDate,
},
{
key: 'endDate',
text: strings.EndByLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
style={{ display: 'inline-block', verticalAlign: 'top', paddingLeft: '22px', }}
onSelectDate={this.onEndDateChange}
value={this.state.endDate}
disabled={this.state.disableEndDate}
/>
</div>
);
}
},
{
key: 'endAfter',
text: strings.EndAfterLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="999"
maskChar=' '
value={this.state.numberOcurrences}
disabled={this.state.disableNumberOcurrences}
onChange={this.onNumberOfOcurrencesChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', paddingLeft: '10px' } }}>{strings.OcurrencesLabel}</Label>
</div>
);
}
},
]}
required={true}
/>
</div>
</div>
</div>
}
</div>
);
}
}

View File

@ -0,0 +1,9 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IEventRecurrenceInfoMonthlyProps {
display:boolean;
recurrenceData: string;
startDate:Date;
context: WebPartContext;
siteUrl:string;
returnRecurrenceData: (startDate:Date,recurrenceData:string) => void;
}

View File

@ -0,0 +1,21 @@
export interface IEventRecurrenceInfoMonthlyState {
selectedKey:string;
selectPatern:string;
startDate: Date;
endDate:Date;
numberOcurrences:string;
dayOfMonth:string;
everyNumberOfMonths: string;
disableDayOfMonth: boolean;
disableNumberOcurrences: boolean;
selectdateRangeOption:string;
disableEndDate:boolean;
selectedRecurrenceRule:string;
isLoading:boolean;
errorMessageDayOfMonth:string;
errorMessageNumberOfMonth:string;
selectedWeekOrderMonth:string;
selectedWeekDay:string | number;
everyNumberOfMonthsWeekDay:string;
errorMessageNumberOfMonthWeekDay:string;
}

View File

@ -0,0 +1,18 @@
.divWrraper {
border-width:1px;
border-color:#adadad;
padding: 20px;
border-style: solid;
}
.ckeckBoxInline {
display: inline-block;
vertical-align: top;
width: 80px;
padding-left: 10px;
}
.dateRange{
display: inline-block;
vertical-align: top;
padding-right: 35px;
padding-top: 10px;
}

View File

@ -0,0 +1,481 @@
import * as React from 'react';
import styles from './EventRecurrenceInfoWeekly.module.scss';
import * as strings from 'CalendarWebPartStrings';
import { IEventRecurrenceInfoWeeklyProps } from './IEventRecurrenceInfoWeeklyProps';
import { IEventRecurrenceInfoWeeklyState } from './IEventRecurrenceInfoWeeklyState';
import { escape } from '@microsoft/sp-lodash-subset';
import * as moment from 'moment';
import { parseString, Builder } from "xml2js";
import {
ChoiceGroup,
IChoiceGroupOption,
Dropdown,
IDropdownOption,
TextField,
SpinButton,
Label,
PrimaryButton,
MaskedTextField,
CommandBarButton, IButtonProps,
DefaultButton,
Checkbox,
} from 'office-ui-fabric-react';
import { Position } from 'office-ui-fabric-react/lib/utilities/positioning';
import { Root } from '@pnp/graph';
import { DatePicker, DayOfWeek, IDatePickerStrings } from 'office-ui-fabric-react/lib/DatePicker';
import spservices from '../../services/spservices';
const DayPickerStrings: IDatePickerStrings = {
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
goToToday: 'Go to today',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
prevYearAriaLabel: 'Go to previous year',
nextYearAriaLabel: 'Go to next year',
closeButtonAriaLabel: 'Close date picker'
};
/**
*
*
* @export
* @class EventRecurrenceInfoDaily
* @extends {React.Component<IEventRecurrenceInfoWeeklyProps, IEventRecurrenceInfoWeeklyState>}
*/
export class EventRecurrenceInfoWeekly extends React.Component<IEventRecurrenceInfoWeeklyProps, IEventRecurrenceInfoWeeklyState> {
private spService: spservices = null;
public constructor(props) {
super(props);
this.onPaternChange = this.onPaternChange.bind(this);
this.state = {
selectedKey: 'daily',
selectPatern: 'every',
startDate: this.props.startDate ? this.props.startDate : moment().toDate(),
endDate: moment().endOf('month').toDate(),
numberOcurrences: '10',
numberOfWeeks: '1',
disableNumberOfWeeks: false,
disableNumberOcurrences: true,
selectdateRangeOption: 'noDate',
disableEndDate: true,
weeklySunday: moment().weekday() === 0 ? true: false,
weeklyMonday: moment().weekday() === 1 ? true: false,
weekklyTuesday: moment().weekday() === 2 ? true: false,
weekklyWednesday: moment().weekday() === 3 ? true: false,
weekklyThursday: moment().weekday() === 4 ? true: false,
weeklyFriday: moment().weekday() === 5 ? true: false,
weeklySaturday: moment().weekday() === 6 ? true: false,
isLoading: false,
errorMessageNumberOfWeeks: '',
};
//
this.onNumberOfWeeksChange = this.onNumberOfWeeksChange.bind(this);
this.onNumberOfOcurrencesChange = this.onNumberOfOcurrencesChange.bind(this);
this.onDataRangeOptionChange = this.onDataRangeOptionChange.bind(this);
this.onEndDateChange = this.onEndDateChange.bind(this);
this.onStartDateChange = this.onStartDateChange.bind(this);
this.onApplyRecurrence = this.onApplyRecurrence.bind(this);
this.onCheckboxSundayChange = this.onCheckboxSundayChange.bind(this);
this.onCheckboxMondayChange = this.onCheckboxMondayChange.bind(this);
this.onCheckboxTuesdayChange = this.onCheckboxTuesdayChange.bind(this);
this.onCheckboxWednesdayChange = this.onCheckboxWednesdayChange.bind(this);
this.onCheckboxThursdayChange = this.onCheckboxThursdayChange.bind(this);
this.onCheckboxFridayChange = this.onCheckboxFridayChange.bind(this);
this.onCheckboxSaturdayChange = this.onCheckboxSaturdayChange.bind(this);
this.spService = new spservices(this.props.context);
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onStartDateChange(date: Date) {
this.setState({ startDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onEndDateChange(date: Date) {
this.setState({ endDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onNumberOfWeeksChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
setTimeout(() => {
let errorMessage:string ='';
if (Number(value.trim()) == 0 || Number(value.trim()) > 255) {
value = '1 ';
errorMessage = 'Allowed values 1 to 255';
}
this.setState({ numberOfWeeks: value , errorMessageNumberOfWeeks: errorMessage });
this.applyRecurrence();
}, 2000);
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onNumberOfOcurrencesChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
setTimeout(() => {
this.setState({ numberOcurrences: value.trim().length > 0 ? value : "10 " });
this.applyRecurrence();
}, 2000);
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {IChoiceGroupOption} option
* @memberof EventRecurrenceInfoDaily
*/
private onDataRangeOptionChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectdateRangeOption: option.key,
disableNumberOcurrences: option.key == 'endAfter' ? false : true,
disableEndDate: option.key == 'endDate' ? false : true,
});
this.applyRecurrence();
}
private onPaternChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectPatern: option.key,
disableNumberOfWeeks: option.key == 'every' ? false : true,
});
this.applyRecurrence();
}
public async componentWillMount() {
// await this.load();
await this.load();
}
public async componentDidUpdate(prevProps: IEventRecurrenceInfoWeeklyProps, prevState: IEventRecurrenceInfoWeeklyState) {
}
private async load() {
let patern: any = {};
let dateRange: { repeatForever?: string, repeatInstances?: string, windowEnd?: Date } = {};
let weeklyPatern: { weekFrequency?: string, su?: boolean, mo?: boolean, tu?: boolean, we?: boolean, th?: boolean, fr?: boolean, sa?: boolean } = {};
if (this.props.recurrenceData) {
parseString(this.props.recurrenceData, { explicitArray: false }, (error, result) => {
if (result.recurrence.rule.repeat) {
patern = result.recurrence.rule.repeat;
}
//
if (result.recurrence.rule.repeatForever) {
dateRange = { repeatForever: result.recurrence.rule.repeatForever };
}
if (result.recurrence.rule.repeatInstances) {
dateRange = { repeatInstances: result.recurrence.rule.repeatInstances };
}
if (result.recurrence.rule.windowEnd) {
dateRange = { windowEnd: result.recurrence.rule.windowEnd };
}
});
// daily Patern
if (patern.weekly) {
weeklyPatern = patern.weekly.$.weekFrequency ? { weekFrequency: patern.weekly.$.weekFrequency } : { weekFrequency: 1 };
const weeklysu = patern.weekly.$.su ? true : false;
const weeklymo = patern.weekly.$.mo ? true : false;
const weeklytu = patern.weekly.$.tu ? true : false;
const weeklywe = patern.weekly.$.we ? true : false;
const weeklyth = patern.weekly.$.th ? true : false;
const weeklyfr = patern.weekly.$.fr ? true : false;
const weeklysa = patern.weekly.$.sa ? true : false;
weeklyPatern = { su: weeklysu, mo: weeklymo, tu: weeklytu, we: weeklywe, th: weeklyth, fr: weeklyfr, sa: weeklysa };
}
let selectDateRangeOption: string = 'noDate';
if (dateRange.repeatForever) {
selectDateRangeOption = 'noDate';
} else if (dateRange.repeatInstances) {
selectDateRangeOption = 'endAfter';
} else if (dateRange.windowEnd) {
selectDateRangeOption = 'endDate';
}
console.log(selectDateRangeOption, new Date(moment(dateRange.windowEnd).format('YYYY/MM/DD')));
// weekday patern
this.setState({
weeklySunday: weeklyPatern.su,
weeklyMonday: weeklyPatern.mo,
weekklyTuesday: weeklyPatern.tu,
weekklyWednesday: weeklyPatern.we,
weekklyThursday: weeklyPatern.th,
weeklyFriday: weeklyPatern.fr,
weeklySaturday: weeklyPatern.sa,
selectPatern: weeklyPatern.weekFrequency,
numberOfWeeks: weeklyPatern.weekFrequency ? weeklyPatern.weekFrequency : '1',
selectdateRangeOption: selectDateRangeOption,
numberOcurrences: dateRange.repeatInstances ? dateRange.repeatInstances : '1',
disableNumberOcurrences: dateRange.repeatInstances ? false : true,
endDate: dateRange.windowEnd ? new Date(moment(dateRange.windowEnd).format('YYYY/MM/DD')) : this.state.endDate,
disableEndDate: dateRange.windowEnd ? false : true,
isLoading: false,
});
}
await this.applyRecurrence();
}
private async onApplyRecurrence(ev: React.MouseEvent<HTMLButtonElement>) {
await this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof EventRecurrenceInfoDaily
*/
private async applyRecurrence() {
const siteTimeZoneHours: number = await this.spService.getSiteTimeZoneHours(this.props.siteUrl);
const eventDate = new Date(moment(this.state.startDate).add(siteTimeZoneHours, 'hours').toISOString());
const endDate = moment(this.state.endDate).add(siteTimeZoneHours, 'hours').toISOString();
let selectDateRangeOption;
switch (this.state.selectdateRangeOption) {
case 'noDate':
selectDateRangeOption = `<repeatForever>FALSE</repeatForever>`;
break;
case 'endAfter':
selectDateRangeOption = `<repeatInstances>${this.state.numberOcurrences.trim()}</repeatInstances>`;
break;
case 'endDate':
selectDateRangeOption = `<windowEnd>${endDate}</windowEnd>`;
break;
default:
break;
}
// test weekDays
let weekdays: string = '';
if (this.state.weeklySunday) {
weekdays = 'su="TRUE" ';
}
if (this.state.weeklyMonday) {
weekdays = `${weekdays} mo="TRUE"`;
}
if (this.state.weekklyTuesday) {
weekdays = `${weekdays} tu="TRUE"`;
}
if (this.state.weekklyWednesday) {
weekdays = `${weekdays} we="TRUE"`;
}
if (this.state.weekklyThursday) {
weekdays = `${weekdays} th="TRUE"`;
}
if (this.state.weeklyFriday) {
weekdays = `${weekdays} fr="TRUE"`;
}
if (this.state.weeklySaturday) {
weekdays = `${weekdays} sa="TRUE"`;
}
const recurrenceXML = `<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat>` +
`<weekly ${weekdays} weekFrequency="${this.state.numberOfWeeks.trim()}" /></repeat>${selectDateRangeOption}</rule></recurrence>`;
console.log(recurrenceXML);
this.props.returnRecurrenceData(this.state.startDate, recurrenceXML);
}
private async onCheckboxSundayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weeklySunday: isChecked });
await this.applyRecurrence();
}
private async onCheckboxMondayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weeklyMonday: isChecked });
await this.applyRecurrence();
}
private async onCheckboxTuesdayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weekklyTuesday: isChecked });
await this.applyRecurrence();
}
private async onCheckboxWednesdayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weekklyWednesday: isChecked });
await this.applyRecurrence();
}
private async onCheckboxThursdayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weekklyThursday: isChecked });
await this.applyRecurrence();
}
private async onCheckboxFridayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weeklyFriday: isChecked });
await this.applyRecurrence();
}
private async onCheckboxSaturdayChange(ev: React.FormEvent<HTMLElement>, isChecked: boolean) {
this.setState({ weeklySaturday: isChecked });
await this.applyRecurrence();
}
/**
*
*
* @returns {React.ReactElement<IEventRecurrenceInfoWeeklyProps>}
* @memberof EventRecurrenceInfoWeekly
*/
public render(): React.ReactElement<IEventRecurrenceInfoWeeklyProps> {
return (
<div >
{
<div>
<div style={{ display: 'inline-block', float: 'right', paddingTop: '10px', height: '40px' }}>
</div>
<div style={{ width: '100%', paddingTop: '10px' }}>
<Label>{strings.PaternLabel}</Label>
<div>
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '40px' } }}>{strings.every}</Label>
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '5px' } }}
mask="999"
maskChar=' '
errorMessage={this.state.errorMessageNumberOfWeeks}
value={this.state.numberOfWeeks}
onChange={this.onNumberOfWeeksChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '80px', paddingLeft: '10px' } }}>{strings.WeeksOnLabel}</Label>
</div>
<div style={{ marginTop: '10px' }}>
<Checkbox label="Sunday" className={styles.ckeckBoxInline} checked={this.state.weeklySunday} onChange={this.onCheckboxSundayChange} />
<Checkbox label="Monday" className={styles.ckeckBoxInline} checked={this.state.weeklyMonday} onChange={this.onCheckboxMondayChange} />
<Checkbox label="Tuesday" className={styles.ckeckBoxInline} checked={this.state.weekklyTuesday} onChange={this.onCheckboxTuesdayChange} />
<Checkbox label="Wednesday" className={styles.ckeckBoxInline} checked={this.state.weekklyWednesday} onChange={this.onCheckboxWednesdayChange} />
</div>
<div style={{ marginTop: '10px' }}>
<Checkbox label="Thursday" className={styles.ckeckBoxInline} checked={this.state.weekklyThursday} onChange={this.onCheckboxThursdayChange} />
<Checkbox label="Friday" className={styles.ckeckBoxInline} checked={this.state.weeklyFriday} onChange={this.onCheckboxFridayChange} />
<Checkbox label="Saturday" className={styles.ckeckBoxInline} checked={this.state.weeklySaturday} onChange={this.onCheckboxSaturdayChange} />
</div>
</div>
<div style={{ paddingTop: '22px' }}>
<Label>{strings.dateRangeLabel}</Label>
<div className={styles.dateRange}>
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
label={strings.StartDateLabel}
value={this.state.startDate}
onSelectDate={this.onStartDateChange}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingTop: '10px' }}>
<ChoiceGroup
selectedKey={this.state.selectdateRangeOption}
onChange={this.onDataRangeOptionChange}
options={[
{
key: 'noDate',
text: strings.noEndDate,
},
{
key: 'endDate',
text: strings.EndByLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
style={{ display: 'inline-block', verticalAlign: 'top', paddingLeft: '22px', }}
onSelectDate={this.onEndDateChange}
value={this.state.endDate}
disabled={this.state.disableEndDate}
/>
</div>
);
}
},
{
key: 'endAfter',
text: strings.EndAfterLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="999"
maskChar=' '
value={this.state.numberOcurrences}
disabled={this.state.disableNumberOcurrences}
onChange={this.onNumberOfOcurrencesChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', paddingLeft: '10px' } }}>{strings.OcurrencesLabel}</Label>
</div>
);
}
},
]}
required={true}
/>
</div>
</div>
</div>
}
</div>
);
}
}

View File

@ -0,0 +1,9 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IEventRecurrenceInfoWeeklyProps {
display:boolean;
recurrenceData: string;
startDate:Date;
context: WebPartContext;
siteUrl:string;
returnRecurrenceData: (startDate:Date,recurrenceData:string) => void;
}

View File

@ -0,0 +1,21 @@
export interface IEventRecurrenceInfoWeeklyState {
selectedKey:string;
selectPatern:string;
startDate: Date;
endDate:Date;
numberOcurrences:string;
numberOfWeeks:string;
disableNumberOfWeeks: boolean;
disableNumberOcurrences: boolean;
selectdateRangeOption:string;
disableEndDate:boolean;
weeklySunday:boolean;
weeklyMonday:boolean;
weekklyTuesday:boolean;
weekklyWednesday:boolean;
weekklyThursday:boolean;
weeklyFriday:boolean;
weeklySaturday:boolean;
isLoading:boolean;
errorMessageNumberOfWeeks:string;
}

View File

@ -0,0 +1,6 @@
.divWrraper {
border-width:1px;
border-color:#adadad;
padding: 20px;
border-style: solid;
}

View File

@ -0,0 +1,644 @@
import * as React from 'react';
import styles from './EventRecurrenceInfoYearly.module.scss';
import * as strings from 'CalendarWebPartStrings';
import { IEventRecurrenceInfoYearlyProps } from './IEventRecurrenceInfoYearlyProps';
import { IEventRecurrenceInfoYearlyState } from './IEventRecurrenceInfoYearlyState';
import { escape } from '@microsoft/sp-lodash-subset';
import * as moment from 'moment';
import { parseString, Builder } from "xml2js";
import {
ChoiceGroup,
IChoiceGroupOption,
Dropdown,
IDropdownOption,
TextField,
SpinButton,
Label,
PrimaryButton,
MaskedTextField,
CommandBarButton, IButtonProps,
DefaultButton
} from 'office-ui-fabric-react';
import { Position } from 'office-ui-fabric-react/lib/utilities/positioning';
import { Root } from '@pnp/graph';
import { DatePicker, DayOfWeek, IDatePickerStrings } from 'office-ui-fabric-react/lib/DatePicker';
import spservices from '../../services/spservices';
const DayPickerStrings: IDatePickerStrings = {
months: ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'],
shortMonths: ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
days: ['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'],
shortDays: ['S', 'M', 'T', 'W', 'T', 'F', 'S'],
goToToday: 'Go to today',
prevMonthAriaLabel: 'Go to previous month',
nextMonthAriaLabel: 'Go to next month',
prevYearAriaLabel: 'Go to previous year',
nextYearAriaLabel: 'Go to next year',
closeButtonAriaLabel: 'Close date picker'
};
/**
*
*
* @export
* @class EventRecurrenceInfoDaily
* @extends {React.Component<IEventRecurrenceInfoYearlyProps, IEventRecurrenceInfoYearlyState>}
*/
export class EventRecurrenceInfoYearly extends React.Component<IEventRecurrenceInfoYearlyProps, IEventRecurrenceInfoYearlyState> {
private spService: spservices = null;
public constructor(props) {
super(props);
this.onPaternChange = this.onPaternChange.bind(this);
this.state = {
selectedKey: 'daily',
selectPatern: 'yearly',
startDate: this.props.startDate ? this.props.startDate : moment().toDate(),
endDate: moment().endOf('month').toDate(),
numberOcurrences: '1',
disableDayOfMonth: false,
disableNumberOcurrences: true,
selectdateRangeOption: 'noDate',
disableEndDate: true,
selectedRecurrenceRule: 'yearly',
dayOfMonth: this.props.startDate ? moment(this.props.startDate).format('D') : moment().format('D'),
isLoading: false,
errorMessageDayOfMonth: '',
selectedWeekOrderMonth: 'first',
selectedWeekDay: 'day',
selectedMonth: moment().format('M'),
selectedYearlyByDayMonth: moment().format('M'),
};
//
this.onDayOfMonthChange = this.onDayOfMonthChange.bind(this);
this.onNumberOfOcurrencesChange = this.onNumberOfOcurrencesChange.bind(this);
this.onDataRangeOptionChange = this.onDataRangeOptionChange.bind(this);
this.onEndDateChange = this.onEndDateChange.bind(this);
this.onStartDateChange = this.onStartDateChange.bind(this);
this.onApplyRecurrence = this.onApplyRecurrence.bind(this);
this.onYearlyByDayMonthChange = this.onYearlyByDayMonthChange.bind(this);
this.onSelectedWeekDayChange = this.onSelectedWeekDayChange.bind(this);
this.onWeekOrderMonthChange = this.onWeekOrderMonthChange.bind(this);
this.onMonthChange = this.onMonthChange.bind(this);
this.spService = new spservices(this.props.context);
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onStartDateChange(date: Date) {
this.setState({ startDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {Date} date
* @memberof EventRecurrenceInfoDaily
*/
private onEndDateChange(date: Date) {
this.setState({ endDate: date });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onDayOfMonthChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
setTimeout(() => {
let errorMessage = '';
if (Number(value.trim()) < 1 || Number(value.trim()) > 31) {
value = '1 ';
errorMessage = 'Allowed values 1 to 31';
}
this.setState({ dayOfMonth: value, errorMessageDayOfMonth: errorMessage });
this.applyRecurrence();
}, 3000);
}
private onMonthChange(ev: React.SyntheticEvent<HTMLElement>, item: IDropdownOption) {
this.setState({ selectedMonth: item.key });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {string} value
* @memberof EventRecurrenceInfoDaily
*/
private onNumberOfOcurrencesChange(ev: React.SyntheticEvent<HTMLElement>, value: string) {
ev.preventDefault();
this.setState({ numberOcurrences: value });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {IChoiceGroupOption} option
* @memberof EventRecurrenceInfoDaily
*/
private onDataRangeOptionChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectdateRangeOption: option.key,
disableNumberOcurrences: option.key == 'endAfter' ? false : true,
disableEndDate: option.key == 'endDate' ? false : true,
});
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.SyntheticEvent<HTMLElement>} ev
* @param {IChoiceGroupOption} option
* @memberof EventRecurrenceInfoYearly
*/
private onPaternChange(ev: React.SyntheticEvent<HTMLElement>, option: IChoiceGroupOption): void {
ev.preventDefault();
this.setState({
selectPatern: option.key,
disableDayOfMonth: option.key == 'yearly' ? false : true,
});
this.applyRecurrence();
}
public async componentDidMount() {
}
public async componentWillMount() {
await this.load();
}
/**
*
*
* @private
* @param {React.FormEvent<HTMLDivElement>} ev
* @param {IDropdownOption} item
* @memberof EventRecurrenceInfoYearly
*/
private onWeekOrderMonthChange(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
this.setState({ selectedWeekOrderMonth: item.text });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.FormEvent<HTMLDivElement>} ev
* @param {IDropdownOption} item
* @memberof EventRecurrenceInfoYearly
*/
private onYearlyByDayMonthChange(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
this.setState({ selectedYearlyByDayMonth: item.key });
this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.FormEvent<HTMLDivElement>} ev
* @param {IDropdownOption} item
* @memberof EventRecurrenceInfoYearly
*/
private onSelectedWeekDayChange(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
this.setState({ selectedWeekDay: item.text });
this.applyRecurrence();
}
public async componentDidUpdate(prevProps: IEventRecurrenceInfoYearlyProps, prevState: IEventRecurrenceInfoYearlyState) {
}
/**
*
*
* @private
* @memberof EventRecurrenceInfoYearly
*/
private async load() {
let patern: any = {};
let dateRange: { repeatForever?: string, repeatInstances?: string, windowEnd?: Date } = {};
let yearlyPatern: { yearFrequency?: string, day?: string, month?: string } = {};
let yearlyByDayPatern: { yearFrequency?: string, weekdayOfMonth?: string, weekDay?: string, month?: string } = {};
let recurrenceRule: string;
if (this.props.recurrenceData) {
parseString(this.props.recurrenceData, { explicitArray: false }, (error, result) => {
if (result.recurrence.rule.repeat) {
patern = result.recurrence.rule.repeat;
}
//
if (result.recurrence.rule.repeatForever) {
dateRange = { repeatForever: result.recurrence.rule.repeatForever };
}
if (result.recurrence.rule.repeatInstances) {
dateRange = { repeatInstances: result.recurrence.rule.repeatInstances };
}
if (result.recurrence.rule.windowEnd) {
dateRange = { windowEnd: result.recurrence.rule.windowEnd };
}
});
// yearly Patern
if (patern.yearly) {
recurrenceRule = 'yearly';
if (patern.yearly.$.yearFrequency && patern.yearly.$.day) {
yearlyPatern = { yearFrequency: patern.yearly.$.yearFrequency, day: patern.yearly.$.day, month: patern.yearly.$.month };
}
}
// yearlyByDay Patern
if (patern.yearlyByDay) {
recurrenceRule = 'yearly';
let weekDay = 'day';
if (patern.yearlyByDay.$.su) weekDay = 'sunday';
if (patern.yearlyByDay.$.mo) weekDay = 'monday';
if (patern.yearlyByDay.$.tu) weekDay = 'tuesday';
if (patern.yearlyByDay.$.we) weekDay = 'wednesday';
if (patern.yearlyByDay.$.th) weekDay = 'thursday';
if (patern.yearlyByDay.$.fr) weekDay = 'friday';
if (patern.yearlyByDay.$.sa) weekDay = 'saturday';
if (patern.yearlyByDay.$.day) weekDay = 'day';
if (patern.yearlyByDay.$.weekday) weekDay = 'weekday';
if (patern.yearlyByDay.$.weekend_day) weekDay = 'weekdendday';
yearlyByDayPatern = {
yearFrequency: patern.yearlyByDay.$.yearFrequency,
weekdayOfMonth: patern.yearlyByDay.$.weekdayOfMonth,
weekDay: weekDay,
month: patern.yearlyByDay.$.month,
};
}
let selectDateRangeOption: string = 'noDate';
if (dateRange.repeatForever) {
selectDateRangeOption = 'noDate';
} else if (dateRange.repeatInstances) {
selectDateRangeOption = 'endAfter';
} else if (dateRange.windowEnd) {
selectDateRangeOption = 'endDate';
}
// weekday patern
this.setState({
selectedRecurrenceRule: recurrenceRule,
selectPatern: patern.yearly ? 'yearly' : 'yearlyByDay',
dayOfMonth: yearlyPatern.day ? yearlyPatern.day : '1',
selectedMonth: yearlyPatern.month ? yearlyPatern.month : moment().month(),
selectedYearlyByDayMonth: yearlyByDayPatern.month ? yearlyByDayPatern.month : moment().format('M'),
selectedWeekOrderMonth: yearlyByDayPatern.weekdayOfMonth ? yearlyByDayPatern.weekdayOfMonth : 'first',
selectedWeekDay: yearlyByDayPatern.weekDay ? yearlyByDayPatern.weekDay : 'day',
disableDayOfMonth: patern.yearly ? false : true,
selectdateRangeOption: selectDateRangeOption,
numberOcurrences: dateRange.repeatInstances ? dateRange.repeatInstances : '10',
disableNumberOcurrences: dateRange.repeatInstances ? false : true,
endDate: dateRange.windowEnd ? new Date(moment(dateRange.windowEnd).format('YYYY/MM/DD')) : this.state.endDate,
disableEndDate: dateRange.windowEnd ? false : true,
isLoading: false,
});
}
await this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof EventRecurrenceInfoYearly
*/
private async onApplyRecurrence(ev: React.MouseEvent<HTMLButtonElement>) {
await this.applyRecurrence();
}
/**
*
*
* @private
* @param {React.MouseEvent<HTMLButtonElement>} ev
* @memberof EventRecurrenceInfoDaily
*/
private async applyRecurrence() {
const siteTimeZoneHours: number = await this.spService.getSiteTimeZoneHours(this.props.siteUrl);
const eventDate = new Date(moment(this.state.startDate).add(siteTimeZoneHours, 'hours').toISOString());
const endDate = moment(this.state.endDate).add(siteTimeZoneHours, 'hours').toISOString();
let selectDateRangeOption;
switch (this.state.selectdateRangeOption) {
case 'noDate':
selectDateRangeOption = `<repeatForever>FALSE</repeatForever>`;
break;
case 'endAfter':
selectDateRangeOption = `<repeatInstances>${this.state.numberOcurrences}</repeatInstances>`;
break;
case 'endDate':
selectDateRangeOption = `<windowEnd>${endDate}</windowEnd>`;
break;
default:
break;
}
let recurrencePatern: string = '';
if (this.state.selectPatern == 'yearly') {
recurrencePatern = `<yearly yearFrequency="1" day="${this.state.dayOfMonth}" month="${this.state.selectedMonth}" /></repeat>${selectDateRangeOption}</rule></recurrence>`;
}
if (this.state.selectPatern == 'yearlyByDay') {
recurrencePatern = `<yearlyByDay weekdayOfMonth="${this.state.selectedWeekOrderMonth}" month="${this.state.selectedYearlyByDayMonth}"`;
switch (this.state.selectedWeekDay) {
case 'day':
recurrencePatern = recurrencePatern + `day="TRUE"`;
break;
case 'weekday':
recurrencePatern = recurrencePatern + `weekday="TRUE"`;
break;
case 'weekendday':
recurrencePatern = recurrencePatern + `weekend_day="TRUE"`;
break;
case 'sunday':
recurrencePatern = recurrencePatern + `su="TRUE"`;
break;
case 'monday':
recurrencePatern = recurrencePatern + `mo="TRUE"`;
break;
case 'tuesday':
recurrencePatern = recurrencePatern + `tu="TRUE"`;
break;
case 'wednesday':
recurrencePatern = recurrencePatern + `we="TRUE"`;
break;
case 'thursday':
recurrencePatern = recurrencePatern + `th="TRUE"`;
break;
case 'friday':
recurrencePatern = recurrencePatern + `fr="TRUE"`;
break;
case 'saturday':
recurrencePatern = recurrencePatern + `sa="TRUE"`;
break;
default:
break;
}
recurrencePatern = recurrencePatern + ` yearFrequency="1" /></repeat>${selectDateRangeOption}</rule></recurrence>`;
}
const recurrenceXML = `<recurrence><rule><firstDayOfWeek>su</firstDayOfWeek><repeat>` + recurrencePatern;
this.props.returnRecurrenceData(this.state.startDate, recurrenceXML);
}
/**
*
*
* @returns {React.ReactElement<IEventRecurrenceInfoDailyProps>}
* @memberof EventRecurrenceInfoDaily
*/
public render(): React.ReactElement<IEventRecurrenceInfoYearlyProps> {
return (
<div >
{
<div>
<div style={{ display: 'inline-block', float: 'right', paddingTop: '10px', height: '40px' }}>
</div>
<div style={{ width: '100%', paddingTop: '10px' }}>
<Label>{strings.PaternLabel}</Label>
<ChoiceGroup
selectedKey={this.state.selectPatern}
options={[
{
key: 'yearly',
text: strings.every,
ariaLabel: strings.every,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' }}>
<Dropdown
selectedKey={this.state.selectedMonth}
onChange={this.onMonthChange}
disabled={this.state.disableDayOfMonth}
options={[
{ key: '1', text: strings.January },
{ key: '2', text: strings.February },
{ key: '3', text: strings.March },
{ key: '4', text: strings.April },
{ key: '5', text: strings.May },
{ key: '6', text: strings.June },
{ key: '7', text: strings.July },
{ key: '8', text: strings.August },
{ key: '9', text: strings.September },
{ key: '10', text: strings.October },
{ key: '11', text: strings.November },
{ key: '12', text: strings.December },
]}
/>
</div>
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="99"
maskChar=' '
disabled={this.state.disableDayOfMonth}
value={this.state.dayOfMonth}
errorMessage={this.state.errorMessageDayOfMonth}
onChange={this.onDayOfMonthChange} />
</div>
);
}
},
{
key: 'yearlyByDay',
text: strings.theLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '80px', paddingLeft: '10px' }}>
<Dropdown
selectedKey={this.state.selectedWeekOrderMonth}
onChange={this.onWeekOrderMonthChange}
disabled={!this.state.disableDayOfMonth}
options={[
{ key: 'first', text: strings.firstLabel },
{ key: 'second', text: strings.secondLabel },
{ key: 'third', text: strings.thirdLabel },
{ key: 'fourth', text: strings.fourthLabel },
{ key: 'last', text: strings.lastLabel },
]}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '5px' }}>
<Dropdown
selectedKey={this.state.selectedWeekDay}
disabled={!this.state.disableDayOfMonth}
onChange={this.onSelectedWeekDayChange}
options={[
{ key: 'day', text: strings.dayLable },
{ key: 'weekday', text: strings.weekDayLabel },
{ key: 'weekendday', text: strings.weekEndDay },
{ key: 'sunday', text: strings.Sunday },
{ key: 'monday', text: strings.Monday },
{ key: 'tuesday', text: strings.Tuesday },
{ key: 'wednesday', text: strings.Wednesday },
{ key: 'thursday', text: strings.Thursday },
{ key: 'friday', text: strings.Friday },
{ key: 'saturday', text: strings.Saturday },
]}
/>
</div>
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '30px', paddingLeft: '10px' } }}>of</Label>
<div style={{ display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '5px' }}>
<Dropdown
selectedKey={this.state.selectedYearlyByDayMonth}
onChange={this.onYearlyByDayMonthChange}
disabled={!this.state.disableDayOfMonth}
options={[
{ key: '1', text: strings.January },
{ key: '2', text: strings.February },
{ key: '3', text: strings.March },
{ key: '4', text: strings.April },
{ key: '5', text: strings.May },
{ key: '6', text: strings.June },
{ key: '7', text: strings.July },
{ key: '8', text: strings.August },
{ key: '9', text: strings.September },
{ key: '10', text: strings.October },
{ key: '11', text: strings.November },
{ key: '12', text: strings.December },
]}
/>
</div>
</div>
);
}
}
]}
onChange={this.onPaternChange}
required={true}
/>
</div>
<div style={{ paddingTop: '22px' }}>
<Label>Date Range</Label>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: '35px', paddingTop: '10px' }}>
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
label={strings.StartDateLabel}
value={this.state.startDate}
onSelectDate={this.onStartDateChange}
/>
</div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingTop: '10px' }}>
<ChoiceGroup
selectedKey={this.state.selectdateRangeOption}
onChange={this.onDataRangeOptionChange}
options={[
{
key: 'noDate',
text: strings.noEndDate,
},
{
key: 'endDate',
text: strings.EndByLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<DatePicker
firstDayOfWeek={DayOfWeek.Sunday}
strings={DayPickerStrings}
placeholder={strings.StartDatePlaceHolder}
ariaLabel={strings.StartDatePlaceHolder}
style={{ display: 'inline-block', verticalAlign: 'top', paddingLeft: '22px', }}
onSelectDate={this.onEndDateChange}
value={this.state.endDate}
disabled={this.state.disableEndDate}
/>
</div>
);
}
},
{
key: 'endAfter',
text: strings.EndAfterLabel,
onRenderField: (props, render) => {
return (
<div >
{render!(props)}
<MaskedTextField
styles={{ root: { display: 'inline-block', verticalAlign: 'top', width: '100px', paddingLeft: '10px' } }}
mask="999"
maskChar=' '
value={this.state.numberOcurrences}
disabled={this.state.disableNumberOcurrences}
onChange={this.onNumberOfOcurrencesChange} />
<Label styles={{ root: { display: 'inline-block', verticalAlign: 'top', paddingLeft: '10px' } }}>{strings.OcurrencesLabel}</Label>
</div>
);
}
},
]}
required={true}
/>
</div>
</div>
</div>
}
</div>
);
}
}

View File

@ -0,0 +1,9 @@
import { WebPartContext } from "@microsoft/sp-webpart-base";
export interface IEventRecurrenceInfoYearlyProps {
display:boolean;
recurrenceData: string;
startDate:Date;
context: WebPartContext;
siteUrl:string;
returnRecurrenceData: (startDate:Date,recurrenceData:string) => void;
}

View File

@ -0,0 +1,20 @@
export interface IEventRecurrenceInfoYearlyState {
selectedKey:string;
selectPatern:string;
startDate: Date;
endDate:Date;
numberOcurrences:string;
dayOfMonth:string;
disableDayOfMonth: boolean;
disableNumberOcurrences: boolean;
selectdateRangeOption:string;
disableEndDate:boolean;
selectedRecurrenceRule:string;
isLoading:boolean;
errorMessageDayOfMonth:string;
selectedWeekOrderMonth:string;
selectedWeekDay:string;
selectedMonth:string | number;
selectedYearlyByDayMonth: string | number;
}

View File

@ -1,17 +1,25 @@
export interface IEventData {
id?:number;
Id?:number;
ID?:number;
title: string;
Description?: any;
location?:string;
start: Date;
end: Date;
EventDate: Date;
EndDate: Date;
color?:string;
ownerInitial?: string;
ownerPhoto?:string;
ownerEmail?:string;
ownerName?:string;
allDayEvent?: boolean;
fAllDayEvent?: boolean;
attendes?: number[];
geolocation?: {Longitude:number, Latitude: number};
Category?: string;
Duration?: number;
RecurrenceData?:string;
fRecurrence?:string | boolean;
EventType?:string;
UID?:string;
RecurrenceID?: string;
MasterSeriesItemID?: string;
}

View File

@ -0,0 +1,8 @@
export default class parseRecurrentEvent {
constructor();
parseEvents(events: any, start: any, end: any): any[];
formatString(str: any): any;
parseDate(date: any, allDay: any): any;
parseEvent(e: any, start: any, end: any): any[];
cloneObj(obj: any): any;
}

View File

@ -0,0 +1,401 @@
import * as moment from 'moment';
export default class parseRecurrentEvent {
private wEvents: any[] = [];
private full: any[] = [] ;
public parseEvents(events: any[], start: any, end: any) {
this.wEvents = events;
for (var i = 0; i < events.length; i++) {
end = null;
if (events[i].RecurrenceData.indexOf('<windowEnd>') != -1) {
let wDtEnd = events[i].RecurrenceData.substring(events[i].RecurrenceData.indexOf("<windowEnd>") + 11);
wDtEnd = wDtEnd.substring(0, wDtEnd.indexOf('<'));
end = moment(wDtEnd).toDate();
}
this.full = this.full.concat(this.parseEvent(events[i], start, end));
}
// remove deleted recurrences EventType = 3
this.full = this.full.filter( (el,j)=>{
if (el.EventType != '3'){
return el;
}
});
return this.full;
}
public RecurrenceExceptionExists(masterSeriesItemId, date) {
const found = this.wEvents.filter((el,i) => {
if (moment(el.RecurrenceID).isSame(moment(date)) && el.MasterSeriesItemID == masterSeriesItemId ) {
return el;
}
});
return found.length > 0 ? true : false;
}
//
public formatString(str: string) {
var arr = str.split("'");
str = arr.join('');
arr = str.split('"');
str = arr.join('');
arr = str.split('=');
str = arr.join(' ');
str.trim();
return str.split(' ');
}
public parseDate(date: any, allDay: any) {
if (typeof date == 'string') {
if (allDay) {
if (date.lastIndexOf('Z') == date.length - 1) {
var dt = date.substring(0, date.length - 1);
return new Date(dt);
}
}
else {
return new Date(date);
}
}
return date;
}
public parseEvent(e: any, start: any, end: any) {
if (e.fRecurrence == '0' || e.fRecurrence == '4') {
e.EventDate = new Date(this.parseDate(e.EventDate, e.fAllDayEvent));
e.EndDate = new Date(this.parseDate(e.EndDate, e.fAllDayEvent));
return [e];
}
else {
start = start || this.parseDate(e.EventDate, e.fAllDayEvent);
end = end || this.parseDate(e.EndDate, e.fAllDayEvent);
var er = [];
var wd = ['su', 'mo', 'tu', 'we', 'th', 'fr', 'sa'];
var wom = ['first', 'second', 'third', 'fourth'];
var rTotal: any = 0;
var total: any = 0;
if (e.RecurrenceData.indexOf('<repeatInstances>') != -1) {
rTotal = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<repeatInstances>") + 17);
rTotal = parseInt(rTotal.substring(0, rTotal.indexOf('<')));
}
if (e.RecurrenceData.indexOf("<daily ") != -1) {
var str = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<daily "));
str = str.substring(7, str.indexOf('/>') - 1);
var arr = this.formatString(str);
if (arr.indexOf("dayFrequency") != -1) {
var frequency = parseInt(arr[arr.indexOf("dayFrequency") + 1]);
var loop = true;
var init = this.parseDate(e.EventDate, e.fAllDayEvent);
while (loop) {
total++;
if ((new Date(init)).getTime() >= start.getTime()) {
var ed = new Date(init);
ed.setSeconds(ed.getSeconds() + e.Duration);
var ni = this.cloneObj(e);
ni.EventDate = new Date(init);
if (!this.RecurrenceExceptionExists(e.Id, ni.EventDate)) {
ni.EndDate = ed;
ni.fRecurrence = false;
ni.Id = e.Id;
ni.ID = e.Id;
er.push(ni);
}
}
init.setDate(init.getDate() + frequency);
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
}
}
else if (arr.indexOf("weekday") != -1) {
e.RecurrenceData = e.RecurrenceData + "<weekly mo='TRUE' tu='TRUE' we='TRUE' th='TRUE' fr='TRUE' weekFrequency='1' />";//change from daily on every weekday to weekly on every weekday
}
}
if (e.RecurrenceData.indexOf("<weekly ") != -1) {
var str = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<weekly "));
str = str.substring(8, str.indexOf('/>') - 1);
var arr = this.formatString(str);
var frequency = parseInt(arr[arr.indexOf("weekFrequency") + 1]);
var loop = true;
var init = this.parseDate(e.EventDate, e.fAllDayEvent);
var initDay = init.getDay();
while (loop) {
for (var i = initDay; i < 7; i++) {
if (arr.indexOf(wd[i]) != -1 && (rTotal > total || rTotal == 0)) {
total++;
if ((new Date(init)).getTime() >= start.getTime()) {
var nd: any = new Date(init);
nd.setDate(nd.getDate() + (i - initDay));
var ed = new Date(nd);
ed.setSeconds(ed.getSeconds() + e.Duration);
var ni = this.cloneObj(e);
ni.EventDate = new Date(nd);
if (!this.RecurrenceExceptionExists(e.Id, ni.EventDate)) {
ni.EndDate = ed;
ni.fRecurrence = false;
ni.Id = e.Id;
ni.ID = e.Id;
er.push(ni);
}
}
}
}
init.setDate(init.getDate() + ((7 * frequency) - initDay));
initDay = 0;
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
}
}
if (e.RecurrenceData.indexOf("<monthly ") != -1) {
var str = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<monthly "));
str = str.substring(9, str.indexOf('/>') - 1);
var arr = this.formatString(str);
var frequency = parseInt(arr[arr.indexOf("monthFrequency") + 1]);
var loop = true;
var init = this.parseDate(e.EventDate, e.fAllDayEvent);
var day = parseInt(arr[arr.indexOf("day") + 1]);
while (loop) {
total++;
if ((new Date(init)).getTime() >= start.getTime()) {
var nd: any = new Date(init);
nd.setDate(day);
if (nd.getMonth() == init.getMonth()) {
var ed = new Date(nd);
ed.setSeconds(ed.getSeconds() + e.Duration);
var ni = this.cloneObj(e);
ni.EventDate = new Date(nd);
if (!this.RecurrenceExceptionExists(e.Id, ni.EventDate)) {
ni.EndDate = ed;
ni.fRecurrence = false;
ni.Id = e.Id;
ni.ID = e.Id;
er.push(ni);
}
}
}
init.setMonth(init.getMonth() + frequency);
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
}
}
if (e.RecurrenceData.indexOf("<monthlyByDay ") != -1) {
var str = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<monthlyByDay "));
str = str.substring(14, str.indexOf('/>') - 1);
var arr = this.formatString(str);
var frequency = parseInt(arr[arr.indexOf("monthFrequency") + 1]);
var loop = true;
var init = this.parseDate(e.EventDate, e.fAllDayEvent);
var weekdayOfMonth = arr[arr.indexOf("weekdayOfMonth") + 1];
var temp: any = new Date();
while (loop) {
total++;
if ((new Date(init)).getTime() >= start.getTime()) {
var nd: any = new Date(init);
nd.setDate(1); //set to first day of month
if (arr.indexOf("weekday") != -1) { //find first weekday - if not saturday or sunday, then current date is a weekday
if (nd.getDay() == 0) nd.setDate(nd.getDate() + 1);// add one day to sunday
else if (nd.getDay() == 6) nd.setDate(nd.getDate() + 2); //add two days to saturday
if (weekdayOfMonth == 'last') {
while (nd.getMonth() == init.getMonth()) {
temp = new Date(nd);
if (nd.getDay() == 5) nd.setDate(nd.getDate() + 3); //if the current date is friday, add three days to get to monday
else nd.setDate(nd.getDate() + 1); //otherwise, just add one day
}
nd = new Date(temp);
}
else {
for (var i: any = 0; i < wom.indexOf(weekdayOfMonth); i++) {
if (nd.getDay() == 5) nd.setDate(nd.getDate() + 3); //if the current date is friday, add three days to get to monday
else nd.setDate(nd.getDate() + 1); //otherwise, just add one day
}
}
}
else if (arr.indexOf("weekend_day") != -1) { //find first weekend day
if (nd.getDay() != 0 && nd.getDay() != 6) nd.setDate(nd.getDate() + (6 - nd.getDay())); //if not saturday or sunday, then add days to get to saturday
if (weekdayOfMonth == 'last') {
while (nd.getMonth() == init.getMonth()) {
temp = new Date(nd);
if (nd.getDay() == 0) nd.setDate(nd.getDate() + 6); //if the current date is sunday, add six days to get to saturday
else nd.setDate(nd.getDate() + 1); //otherwise, just add one day
}
nd = new Date(temp);
}
else {
for (var i: any = 0; i < wom.indexOf(weekdayOfMonth); i++) {
if (nd.getDay() == 0) nd.setDate(nd.getDate() + 6); //if the current date is sunday, add six days to get to saturday
else nd.setDate(nd.getDate() + 1); //otherwise, just add one day
}
}
}
else if (arr.indexOf("day") != -1) {//just looking for the Nth day in the month...
if (weekdayOfMonth == 'last') {
nd.setMonth(nd.getMonth() + 1);
nd.setDate(0);
}
else nd.setDate(nd.getDate() + (wom.indexOf(weekdayOfMonth))); //now add days to get to the Nth instance of this day
}
else {
for (var i: any = 0; i < wd.length; i++) { //get first instance of the specified day
if (arr.indexOf(wd[i]) != -1) {
if (nd.getDay() > i) nd.setDate(nd.getDate() + (7 - (nd.getDay() - i)));
else nd.setDate(nd.getDate() + (i - nd.getDay() ));
}
}
if (weekdayOfMonth == 'last') {
while (nd.getMonth() == init.getMonth()) {
temp = new Date(nd);
nd.setDate(nd.getDate() + 7); //add a week to each instance to get the Nth instance
}
nd = new Date(temp);
}
else {
for (var i: any = 0; i < wom.indexOf(weekdayOfMonth); i++) {
nd.setDate(nd.getDate() + 7); //add a week to each instance to get the Nth instance
console.log(nd);
}
}
}
if (nd.getMonth() == init.getMonth()) { //make sure the new date calculated actually falls within the current month (sometimes there may be no 4th instance of a day)
var ed = new Date(nd);
ed.setSeconds(ed.getSeconds() + e.Duration);
var ni = this.cloneObj(e);
ni.EventDate = new Date(nd);
if (!this.RecurrenceExceptionExists(e.Id, ni.EventDate)) {
ni.EndDate = ed;
ni.fRecurrence = false;
ni.Id = e.Id;
ni.ID = e.Id;
er.push(ni);
}
}
}
init.setMonth(init.getMonth() + frequency);
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
}
}
if (e.RecurrenceData.indexOf("<yearly ") != -1) {
var str = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<yearly "));
str = str.substring(8, str.indexOf('/>') - 1);
var arr = this.formatString(str);
var frequency = parseInt(arr[arr.indexOf("yearFrequency") + 1]);
var loop = true;
var init = this.parseDate(e.EventDate, e.fAllDayEvent);
var month = (parseInt(arr[arr.indexOf("month") + 1]) - 1);
var day = parseInt(arr[arr.indexOf("day") + 1]);
while (loop) {
var nd: any = new Date(init);
nd.setMonth(month);
nd.setDate(day);
if ((new Date(init)).getTime() <= nd.getTime()) {
total++;
if ((new Date(init)).getTime() >= start.getTime()) {
var ed = new Date(nd);
ed.setSeconds(ed.getSeconds() + e.Duration);
var ni = this.cloneObj(e);
ni.EventDate = new Date(nd);
if (!this.RecurrenceExceptionExists(e.Id, ni.EventDate)) {
ni.EndDate = ed;
ni.fRecurrence = false;
ni.Id = e.Id;
ni.ID = e.Id;
er.push(ni);
}
}
}
init.setFullYear(init.getFullYear() + frequency);
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
}
}
if (e.RecurrenceData.indexOf("<yearlyByDay ") != -1) {
var str = e.RecurrenceData.substring(e.RecurrenceData.indexOf("<yearlyByDay "));
str = str.substring(13, str.indexOf('/>') - 1);
var arr = this.formatString(str);
var frequency = parseInt(arr[arr.indexOf("yearFrequency") + 1]);
var loop = true;
var init = this.parseDate(e.EventDate, e.fAllDayEvent);
var month = (parseInt(arr[arr.indexOf("month") + 1]) - 1);
var weekdayOfMonth = arr[arr.indexOf("weekdayOfMonth") + 1];
var day = 0;
for (var i: any = 0; i < wd.length; i++) {
if (arr.indexOf(wd[i]) != -1) {
if (arr[arr.indexOf(wd[i]) + 1].toLowerCase() == 'true') day = i;
}
}
while (loop) {
var nd: any = new Date(init);
nd.setMonth(month);
if ((new Date(init)).getTime() <= nd.getTime()) {
total++;
if ((new Date(init)).getTime() >= start.getTime()) {
nd.setDate(1);
var dayOfMonth = nd.getDay();
if (day < dayOfMonth) nd.setDate(nd.getDate() + ((7 - dayOfMonth) + day)); //first instance of this day in the selected month
else nd.setDate(nd.getDate() + (day - dayOfMonth));
if (weekdayOfMonth == 'last') {
var temp: any = new Date(nd);
while (temp.getMonth() == month) {
nd = new Date(temp);
temp.setDate(temp.getDate() + 7); //loop from first instance of month to last instance of month
}
}
else {
nd.setDate(nd.getDate() + (7 * (wom.indexOf(weekdayOfMonth))));
}
if (nd.getMonth() == month) {
var ed = new Date(nd);
ed.setSeconds(ed.getSeconds() + e.Duration);
var ni = this.cloneObj(e);
ni.EventDate = new Date(nd);
if (!this.RecurrenceExceptionExists(e.Id, ni.EventDate)) {
ni.EndDate = ed;
ni.fRecurrence = false;
ni.Id = e.Id;
ni.ID = e.Id;
er.push(ni);
}
}
}
}
init.setFullYear(init.getFullYear() + frequency);
init.setMonth(month);
init.setDate(1);
if ((new Date(init) > end) || (rTotal > 0 && rTotal <= total)) loop = false;
}
}
return er;
} //end recurrence check
}
public cloneObj(obj: any): any {
var copy: any;
if (null == obj || "object" != typeof obj) return obj;
if (obj instanceof Date) {
copy = new Date();
copy.setTime(obj.getTime());
return copy;
}
if (obj instanceof Array) {
copy = [];
for (var i = 0, len = obj.length; i < len; i++) {
copy[i] = this.cloneObj(obj[i]);
}
return copy;
}
if (obj instanceof Object) {
copy = {};
for (var attr in obj) {
if (obj.hasOwnProperty(attr)) copy[attr] = this.cloneObj(obj[attr]);
}
return copy;
}
throw new Error("Unable to copy obj! Its type isn't supported.");
}
}

View File

@ -8,14 +8,13 @@ import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions, HttpClient, M
import * as $ from 'jquery';
import { IEventData } from './IEventData';
import { registerDefaultFontFaces } from "@uifabric/styling";
import { EventArgs } from "@microsoft/sp-core-library";
import * as moment from 'moment';
import { SiteUser } from "@pnp/sp/src/siteusers";
import { IUserPermissions } from './IUserPermissions';
import { dateAdd } from "@pnp/common";
import { escape } from '@microsoft/sp-lodash-subset';
import { escape, update } from '@microsoft/sp-lodash-subset';
import parseRecurrentEvent from './parseRecurrentEvent';
const ADMIN_ROLETEMPLATE_ID = "62e90394-69f5-4237-9190-012177145e10"; // Global Admin TemplateRoleId
// Class Services
export default class spservices {
@ -35,8 +34,6 @@ export default class spservices {
}
// OnInit Function
private async onInit() {
//this.appCatalogUrl = await this.getAppCatalogUrl();
}
/**
@ -46,9 +43,9 @@ export default class spservices {
* @returns {Promise<number>}
* @memberof spservices
*/
private async getSiteTimeZoneHoursToUtc(siteUrl: string): Promise<number> {
public async getSiteTimeZoneHours(siteUrl: string): Promise<number> {
let numberHours: number = 0;
let siteTimeZoneHoursToUTC: any;
let siteTimeZoneHours: any;
let siteTimeZoneBias: number;
let siteTimeZoneDaylightBias: number;
let currentDateTimeOffSet: number = new Date().getTimezoneOffset() / 60;
@ -61,10 +58,10 @@ export default class spservices {
// Formula to calculate the number of hours need to get UTC Date.
// numberHours = (siteTimeZoneBias / 60) + (siteTimeZoneDaylightBias / 60) - currentDateTimeOffSet;
if ( siteTimeZoneBias >= 0 ){
numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet) + siteTimeZoneDaylightBias/60 ;
}else {
numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet) ;
if (siteTimeZoneBias >= 0) {
numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet) + siteTimeZoneDaylightBias / 60;
} else {
numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet);
}
}
catch (error) {
@ -86,26 +83,85 @@ export default class spservices {
try {
const web = new Web(siteUrl);
const siteTimeZoneHoursToUTC: number = await this.getSiteTimeZoneHoursToUtc(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
results = await web.lists.getById(listId).items.add({
Title: newEvent.title,
Description: newEvent.Description,
Geolocation: newEvent.geolocation,
ParticipantsPickerId: { results: newEvent.attendes },
EventDate: new Date(moment(newEvent.start).add(siteTimeZoneHoursToUTC, 'hours').toISOString()),
EndDate: new Date(moment(newEvent.end).add(siteTimeZoneHoursToUTC, 'hours').toISOString()),
EventDate: new Date(moment(newEvent.EventDate).add(siteTimeZoneHours, 'hours').toISOString()),
EndDate: new Date(moment(newEvent.EndDate).add(siteTimeZoneHours, 'hours').toISOString()),
Location: newEvent.location,
fAllDayEvent: false,
fRecurrence: false,
fRecurrence: newEvent.fRecurrence,
Category: newEvent.Category,
EventType: newEvent.EventType,
UID: newEvent.UID,
RecurrenceData: newEvent.RecurrenceData ? await this.deCodeHtmlEntities(newEvent.RecurrenceData) : "",
MasterSeriesItemID: newEvent.MasterSeriesItemID,
RecurrenceID: newEvent.RecurrenceID ? moment(newEvent.RecurrenceID).add(siteTimeZoneHours, 'hours').toISOString() : undefined,
});
} catch (error) {
return Promise.reject(error);
}
return results;
}
/**
*
*
* @param {string} siteUrl
* @param {string} listId
* @param {number} eventId
* @returns {Promise<IEventData>}
* @memberof spservices
*/
public async getEvent(siteUrl: string, listId: string, eventId: number): Promise<IEventData> {
let returnEvent: IEventData = undefined;
try {
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
const web = new Web(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
const event = await web.lists.getById(listId).items.usingCaching().getById(eventId)
.select("RecurrenceID", "MasterSeriesItemID", "Id", "ID", "ParticipantsPickerId", "EventType", "Title", "Description", "EventDate", "EndDate", "Location", "Author/SipAddress", "Author/Title", "Geolocation", "fAllDayEvent", "fRecurrence", "RecurrenceData", "RecurrenceData", "Duration", "Category", "UID")
.expand("Author")
.get();
returnEvent = {
Id: event.ID,
ID: event.ID,
EventType: event.EventType,
title: await this.deCodeHtmlEntities(event.Title),
Description: event.Description ? event.Description : '',
EventDate: new Date(moment(event.EventDate).subtract((siteTimeZoneHours), 'hour').toISOString()),
EndDate: new Date(moment(event.EndDate).subtract(siteTimeZoneHours, 'hour').toISOString()),
location: event.Location,
ownerEmail: event.Author.SipAddress,
ownerPhoto: "",
ownerInitial: '',
color: '',
ownerName: event.Author.Title,
attendes: event.ParticipantsPickerId,
fAllDayEvent: false,
geolocation: { Longitude: event.Geolocation ? event.Geolocation.Longitude : 0, Latitude: event.Geolocation ? event.Geolocation.Latitude : 0 },
Category: event.Category,
Duration: event.Duration,
UID: event.UID,
RecurrenceData: event.RecurrenceData ? await this.deCodeHtmlEntities(event.RecurrenceData) : "",
fRecurrence: event.fRecurrence,
RecurrenceID: event.RecurrenceID,
MasterSeriesItemID: event.MasterSeriesItemID,
};
} catch (error) {
return Promise.reject(error);
}
return returnEvent;
}
/**
*
* @param {IEventData} newEvent
@ -118,21 +174,28 @@ export default class spservices {
let results = null;
try {
const siteTimeZoneHoursToUTC: number = await this.getSiteTimeZoneHoursToUtc(siteUrl);
// delete all recursive extentions before update recurrence event
if (updateEvent.EventType.toString() == "1") await this.deleteRecurrenceExceptions(updateEvent, siteUrl, listId);
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
const web = new Web(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
results = await web.lists.getById(listId).items.getById(updateEvent.id).update({
results = await web.lists.getById(listId).items.getById(updateEvent.Id).update({
Title: updateEvent.title,
Description: updateEvent.Description,
Geolocation: updateEvent.geolocation,
ParticipantsPickerId: { results: updateEvent.attendes },
EventDate: new Date(moment(updateEvent.start).add(siteTimeZoneHoursToUTC, 'hours').toISOString()),
EndDate: new Date(moment(updateEvent.end).add(siteTimeZoneHoursToUTC, 'hours').toISOString()),
EventDate: new Date(moment(updateEvent.EventDate).add(siteTimeZoneHours, 'hours').toISOString()),
EndDate: new Date(moment(updateEvent.EndDate).add(siteTimeZoneHours, 'hours').toISOString()),
Location: updateEvent.location,
fAllDayEvent: false,
fRecurrence: false,
fRecurrence: updateEvent.fRecurrence,
Category: updateEvent.Category,
UID: updateEvent.UID,
RecurrenceData: updateEvent.RecurrenceData ? await this.deCodeHtmlEntities(updateEvent.RecurrenceData) : "",
EventType: updateEvent.EventType,
MasterSeriesItemID: updateEvent.MasterSeriesItemID,
});
} catch (error) {
return Promise.reject(error);
@ -140,6 +203,25 @@ export default class spservices {
return results;
}
public async deleteRecurrenceExceptions(event: IEventData, siteUrl: string, listId: string) {
let results = null;
try {
const web = new Web(siteUrl);
results = await web.lists.getById(listId).items
.select('Id')
.filter(`EventType eq '3' or EventType eq '4' and MasterSeriesItemID eq '${event.Id}' `)
.get();
if (results && results.length > 0) {
for (const recurrenceException of results) {
await web.lists.getById(listId).items.getById(recurrenceException.Id).delete();
}
}
} catch (error) {
return Promise.reject(error);
}
return;
}
/**
*
* @param {IEventData} event
@ -148,17 +230,44 @@ export default class spservices {
* @returns
* @memberof spservices
*/
public async deleteEvent(event: IEventData, siteUrl: string, listId: string) {
public async deleteEvent(event: IEventData, siteUrl: string, listId: string, recurrenceSeriesEdited: boolean) {
let results = null;
try {
const web = new Web(siteUrl);
// Exception Recurrence eventtype = 4 ? update to deleted Recurrence eventtype=3
switch (event.EventType.toString()) {
case '4': // Exception Recurrence Event
results = await web.lists.getById(listId).items.getById(event.Id).update({
Title: `Delete: ${event.title}`,
EventType: '3',
});
break;
case '1': // recurrence Event
// if delete is a main recrrence delete all recurrences and main recurrence
if (recurrenceSeriesEdited) {
// delete execptions if exists before delete recurrence event
await this.deleteRecurrenceExceptions(event, siteUrl, listId);
await web.lists.getById(listId).items.getById(event.Id).delete();
} else {
// delete a single recurrence Exception. add new entry with eventtype 3
event.RecurrenceID = event.EventDate.toString();
event.MasterSeriesItemID = event.ID.toString();
event.fRecurrence = true;
event.EventType = '3';
await this.addEvent(event, siteUrl, listId);
}
break;
case '0': // normal Event
await web.lists.getById(listId).items.getById(event.Id).delete();
break;
}
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
results = await web.lists.getById(listId).items.getById(event.id).delete();
} catch (error) {
return Promise.reject(error);
}
return results;
return;
}
/**
*
@ -218,7 +327,7 @@ export default class spservices {
public async getUserProfilePictureUrl(loginName: string) {
let results: any = null;
try {
results = await sp.profiles.getPropertiesFor(loginName);
results = await sp.profiles.usingCaching().getPropertiesFor(loginName);
} catch (error) {
results = null;
}
@ -241,12 +350,13 @@ export default class spservices {
try {
const web = new Web(siteUrl);
const userEffectivePermissions = await web.lists.getById(listId).effectiveBasePermissions.get();
// chaeck user permissions
// ...
hasPermissionAdd = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.AddListItems);
hasPermissionEdit =sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.EditListItems);
hasPermissionDelete =sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.DeleteListItems);
hasPermissionDelete = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.DeleteListItems);
hasPermissionEdit = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.EditListItems);
hasPermissionView = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.ViewListItems);
userPermissions = { hasPermissionAdd: hasPermissionAdd, hasPermissionEdit: hasPermissionEdit, hasPermissionDelete: hasPermissionDelete, hasPermissionView: hasPermissionView };
} catch (error) {
return Promise.reject(error);
}
@ -289,6 +399,7 @@ export default class spservices {
for (var i = 0; i < 6; i++) {
var x = Math.round(Math.random() * 14);
var y = hexValues[x];
newColor += y;
}
@ -344,7 +455,7 @@ export default class spservices {
}
try {
// Get Regional Settings TimeZone Hours to UTC
const siteTimeZoneHoursToUTC: number = await this.getSiteTimeZoneHoursToUtc(siteUrl);
const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
// Get Category Field Choices
const categoryDropdownOption = await this.getChoiceFieldOptions(siteUrl, listId, 'Category');
let categoryColor: { category: string, color: string }[] = [];
@ -353,13 +464,12 @@ export default class spservices {
}
const web = new Web(siteUrl);
const results = await web.lists.getById(listId).renderListDataAsStream(
const results = await web.lists.getById(listId).usingCaching().renderListDataAsStream(
{
DatesInUtc: true,
ViewXml: `<View><ViewFields><FieldRef Name='Author'/><FieldRef Name='Category'/><FieldRef Name='Description'/><FieldRef Name='ParticipantsPicker'/><FieldRef Name='Geolocation'/><FieldRef Name='ID'/><FieldRef Name='EndDate'/><FieldRef Name='EventDate'/><FieldRef Name='ID'/><FieldRef Name='Location'/><FieldRef Name='Title'/><FieldRef Name='fAllDayEvent'/></ViewFields>
ViewXml: `<View><ViewFields><FieldRef Name='RecurrenceData'/><FieldRef Name='Duration'/><FieldRef Name='Author'/><FieldRef Name='Category'/><FieldRef Name='Description'/><FieldRef Name='ParticipantsPicker'/><FieldRef Name='Geolocation'/><FieldRef Name='ID'/><FieldRef Name='EndDate'/><FieldRef Name='EventDate'/><FieldRef Name='ID'/><FieldRef Name='Location'/><FieldRef Name='Title'/><FieldRef Name='fAllDayEvent'/><FieldRef Name='EventType'/><FieldRef Name='UID' /><FieldRef Name='fRecurrence' /></ViewFields>
<Query>
<Where>
<And>
<And>
<Geq>
<FieldRef Name='EventDate' />
@ -370,11 +480,6 @@ export default class spservices {
<Value IncludeTimeValue='false' Type='DateTime'>${moment(eventEndDate).format('YYYY-MM-DD')}</Value>
</Leq>
</And>
<Eq>
<FieldRef Name='fRecurrence' />
<Value Type='Recurrence'>0</Value>
</Eq>
</And>
</Where>
</Query>
<RowLimit Paged=\"FALSE\">2000</RowLimit>
@ -383,7 +488,8 @@ export default class spservices {
);
if (results && results.Row.length > 0) {
for (const event of results.Row) {
let event: any = '';
for (event of results.Row) {
const initialsArray: string[] = event.Author[0].title.split(' ');
const initials: string = initialsArray[0].charAt(0) + initialsArray[initialsArray.length - 1].charAt(0);
const userPictureUrl = await this.getUserProfilePictureUrl(`i:0#.f|membership|${event.Author[0].email}`);
@ -399,28 +505,40 @@ export default class spservices {
attendees.push(parseInt(attendee.id));
}
events.push({
id: event.ID,
Id: event.ID,
ID: event.ID,
EventType: event.EventType,
title: await this.deCodeHtmlEntities(event.Title),
Description: event.Description,
// start: moment(event.EventDate).utc().toDate().setUTCMinutes(this.siteTimeZoneOffSet),
start: new Date(moment(event.EventDate).subtract((siteTimeZoneHoursToUTC), 'hour').toISOString()),
// end: new Date(moment(event.EndDate).toLocaleString()),
end: new Date(moment(event.EndDate).subtract(siteTimeZoneHoursToUTC, 'hour').toISOString()),
EventDate: new Date(moment(event.EventDate).subtract((siteTimeZoneHours), 'hour').toISOString()),
EndDate: new Date(moment(event.EndDate).subtract(siteTimeZoneHours, 'hour').toISOString()),
location: event.Location,
ownerEmail: event.Author[0].email,
ownerPhoto: userPictureUrl ?
`https://outlook.office365.com/owa/service.svc/s/GetPersonaPhoto?email=${event.Author[0].email}&UA=0&size=HR96x96` : '',
ownerInitial: initials,
// color: await this.colorGenerate(),
color: CategoryColorValue.length > 0 ? CategoryColorValue[0].color : await this.colorGenerate,
color: CategoryColorValue.length > 0 ? CategoryColorValue[0].color : '#1a75ff', // blue default
ownerName: event.Author[0].title,
attendes: attendees,
allDayEvent: false,
fAllDayEvent: false,
geolocation: { Longitude: parseFloat(geolocation[0]), Latitude: parseFloat(geolocation[1]) },
Category: event.Category
Category: event.Category,
Duration: event.Duration,
RecurrenceData: event.RecurrenceData ? await this.deCodeHtmlEntities(event.RecurrenceData) : "",
fRecurrence: event.fRecurrence,
RecurrenceID: event.RecurrenceID ? moment(event.RecurrenceID).subtract(siteTimeZoneHours, 'hour').toISOString() : undefined,
MasterSeriesItemID: event.MasterSeriesItemID,
UID: event.UID.replace("{", "").replace("}", ""),
});
}
let parseEvt: parseRecurrentEvent = new parseRecurrentEvent();
events = parseEvt.parseEvents(events, null, null);
}
// Return Data
return events;
@ -441,7 +559,7 @@ export default class spservices {
let regionalSettings: RegionalSettings;
try {
const web = new Web(siteUrl);
regionalSettings = await web.regionalSettings.timeZone.get();
regionalSettings = await web.regionalSettings.timeZone.usingCaching().get();
} catch (error) {
return Promise.reject(error);

View File

@ -12,7 +12,8 @@
"requiresCustomScript": false,
"supportedHosts": [
"SharePointWebPart",
"TeamsTab"
"TeamsTab",
"SharePointFullPage"
],
"preconfiguredEntries": [
{

View File

@ -1,7 +1,6 @@
import * as React from 'react';
import * as ReactDom from 'react-dom';
import { Version } from '@microsoft/sp-core-library';
import { BaseClientSideWebPart, PropertyPaneHorizontalRule } from '@microsoft/sp-webpart-base';
import {
IPropertyPaneConfiguration,
@ -66,7 +65,7 @@ export default class CalendarWebPart extends BaseClientSideWebPart<ICalendarWebP
public async onInit(): Promise<void> {
this.spService = new spservices(this.context);
this.properties.siteUrl = this.context.pageContext.site.absoluteUrl;
this.properties.siteUrl = this.properties.siteUrl ? this.properties.siteUrl : this.context.pageContext.site.absoluteUrl;
if (!this.properties.eventStartDate){
this.properties.eventStartDate = { value: moment().subtract(2,'years').startOf('month').toDate(), displayValue: moment().format('ddd MMM MM YYYY')};
}
@ -75,9 +74,12 @@ export default class CalendarWebPart extends BaseClientSideWebPart<ICalendarWebP
}
if (this.properties.siteUrl && !this.properties.list) {
const _lists = await this.loadLists();
if ( _lists.length > 0 ){
this.lists = _lists;
this.properties.list = this.lists.length > 0 ? this.lists[0].key.toString() : '';
this.properties.list = this.lists[0].key.toString();
}
}
return Promise.resolve();
}
@ -130,14 +132,16 @@ export default class CalendarWebPart extends BaseClientSideWebPart<ICalendarWebP
for (const list of results) {
_lists.push({ key: list.Id, text: list.Title });
}
// push new item value
} catch (error) {
this.errorMessage = `${error.message} - ${strings.PropPanelSiteUrlErrorMessage}` ;
this.errorMessage = `${error.message} - please check if site url if valid.` ;
this.context.propertyPane.refresh();
}
return _lists;
}
/**
*
*
* @private
* @param {string} date
@ -226,7 +230,7 @@ export default class CalendarWebPart extends BaseClientSideWebPart<ICalendarWebP
super.onPropertyPaneFieldChanged(propertyPath, oldValue, newValue);
}
} catch (error) {
this.errorMessage = `${error.message} - ${strings.PropPanelSiteUrlErrorMessage}` ;
this.errorMessage = `${error.message} - please check if site url if valid.` ;
this.context.propertyPane.refresh();
}
}
@ -237,6 +241,8 @@ export default class CalendarWebPart extends BaseClientSideWebPart<ICalendarWebP
* @memberof CalendarWebPart
*/
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
// EndDate and Start Date defualt values
return {
pages: [
{

View File

@ -1,4 +1,21 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss';
@import './node_modules/spfx-uifabric-themes/office.theme.vars';
:export {
themeDark: $ms-color-themePrimary;
}
.eventStyleSetter {
background-color: white;
border-radius: 0px;
color: $ms-color-themePrimary;
opacity: 1;
border-width: '1.1px';
border-style: 'solid';
border-color: $ms-color-themePrimary;
border-left-width: '6px';
display: 'block';
}
.Documentcard {

View File

@ -8,8 +8,10 @@ import * as moment from 'moment';
import * as strings from 'CalendarWebPartStrings';
import 'react-big-calendar/lib/css/react-big-calendar.css';
require('./calendar.css');
import {
import { CommunicationColors , FluentCustomizations, FluentTheme } from '@uifabric/fluent-theme';
import {
Customizer,
IPersonaSharedProps,
Persona,
PersonaSize,
@ -132,7 +134,6 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
}
/**
*
*
* @param {*} error
* @param {*} errorInfo
@ -170,7 +171,7 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
previewImages: [
{
// previewImageSrc: event.ownerPhoto,
previewIconProps: { iconName: 'Calendar', styles: { root: { color: event.color } }, className: styles.previewEventIcon },
previewIconProps: { iconName: event.fRecurrence === '0' ? 'Calendar': 'RecurringEvent', styles: { root: { color: event.color } }, className: styles.previewEventIcon },
height: 43,
}
]
@ -195,14 +196,13 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
<div className={styles.DocumentCardDetails}>
<DocumentCardTitle title={event.title} shouldTruncate={true} className={styles.DocumentCardTitle} styles={{ root: { color: event.color} }} />
</div>
{
moment(event.start).format('YYYY/MM/DD') !== moment(event.end).format('YYYY/MM/DD') ?
<span className={styles.DocumentCardTitleTime}>{moment(event.start).format('dddd')} - {moment(event.end).format('dddd')} </span>
moment(event.EventDate).format('YYYY/MM/DD') !== moment(event.EndDate).format('YYYY/MM/DD') ?
<span className={styles.DocumentCardTitleTime}>{moment(event.EventDate).format('dddd')} - {moment(event.EndDate).format('dddd')} </span>
:
<span className={styles.DocumentCardTitleTime}>{moment(event.start).format('dddd')} </span>
<span className={styles.DocumentCardTitleTime}>{moment(event.EventDate).format('dddd')} </span>
}
<span className={styles.DocumentCardTitleTime}>{moment(event.start).format('HH:mm')}H - {moment(event.end).format('HH:mm')}H</span>
<span className={styles.DocumentCardTitleTime}>{moment(event.EventDate).format('HH:mm')}H - {moment(event.EndDate).format('HH:mm')}H</span>
<Icon iconName='MapPin' className={styles.locationIcon} style={{ color: event.color }} />
<DocumentCardTitle
title={`${event.location}`}
@ -213,7 +213,7 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
<div style={{ marginTop: 20 }}>
<DocumentCardActivity
activity={strings.EventOwnerLabel}
people={[{ name: event.ownerName, profileImageSrc: event.ownerPhoto, initialsColor: event.color }]}
people={[{ name: event.ownerName, profileImageSrc: event.ownerPhoto, initialsColor:event.color}]}
/>
</div>
</DocumentCardDetails>
@ -223,7 +223,6 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
};
return (
<div style={{ height: 22 }}>
<HoverCard
cardDismissDelay={1000}
@ -273,15 +272,16 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
* @memberof Calendar
*/
public eventStyleGetter(event, start, end, isSelected): any {
let style: any = {
backgroundColor: 'white',
borderRadius: '0px',
opacity: 1,
color: 'black',
color: event.color,
borderWidth: '1.1px',
borderStyle: 'solid',
borderColor: event.color,
borderLeftWidth: '5px',
borderLeftWidth: '6px',
display: 'block'
};
@ -298,7 +298,10 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
public render(): React.ReactElement<ICalendarProps> {
return (
<div className={styles.calendar}>
<Customizer {...FluentCustomizations}>
<div className={styles.calendar} style={{backgroundColor: 'white', padding: '20px'}}>
<WebPartTitle displayMode={this.props.displayMode}
title={this.props.title}
updateProperty={this.props.updateProperty} />
@ -326,8 +329,8 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
localizer={localizer}
selectable
events={this.state.eventData}
startAccessor="start"
endAccessor="end"
startAccessor="EventDate"
endAccessor="EndDate"
eventPropGetter={this.eventStyleGetter}
onSelectSlot={this.onSelectSlot}
components={{
@ -367,6 +370,7 @@ export default class Calendar extends React.Component<ICalendarProps, ICalendarS
/>
}
</div>
</Customizer>
);
}
}

View File

@ -1,18 +1,38 @@
define([], function () {
return {
PropPanelSiteUrlErrorMessage:'Please verify if site url is valid',
WeeksOnLabel: "week(s) on",
PaternLabel: "Patern",
OcurrencesLabel: "Ocurrences",
dateRangeLabel: "Date Range",
weekEndDay: "Weekend Day",
weekDayLabel: "Weekday",
lastLabel: "last",
fourthLabel: "fourth",
thirdLabel: "third",
secondLabel: "second",
firstLabel: "first",
theLabel: "the",
MonthsLabel: "month(s)",
ofEveryLabel: "of every ",
AllowedValues1to12Label: "Allowed values 1 to 12",
noEndDate: "no end date",
everyweekdays: "every weekdays",
days: "days",
every: "every",
EndByLabel: "end by",
EndAfterLabel: "end after",
HttpErrorMessage: "Error reading calendar events:",
CategoryPlaceHolder: "Please select category",
CategoryLabel: "Category",
EnDateValidationMessage: "start date is greater than end date",
SartDateValidationMessage: "start date is greater than end date",
eventSelectDatesLabel: "Show only the events within the following dates",
ConfirmeDeleteMessage: "Confirm delete event ?",
DialogConfirmDeleteTitle: " 'Delete Event'",
ConfirmeDeleteMessage: "Confirm delete event ? If the event is a recurrence event all entries will be deleted ",
DialogConfirmDeleteTitle: "Delete Event",
SpinnerDeletingLabel: "Deleting...",
DialogCloseButtonLabel: "Cancel",
DialogConfirmDeleteLabel: "Delete",
SaveButtonLabel: " Save",
SaveButtonLabel: "Save",
DeleteButtonLabel: "Delete",
CancelButtonLabel: "Cancel",
LoadingEventsLabel: "Loading events...",
@ -54,7 +74,7 @@ define([], function () {
Oct:'Oct',
Nov:'Nov',
Dez:'Dez',
Dezember: "December",
December: "December",
November: " 'November'",
October: "October",
September: "September",

View File

@ -1,4 +1,25 @@
declare interface ICalendarWebPartStrings {
WeeksOnLabel: string;
PaternLabel: string;
OcurrencesLabel: string;
dateRangeLabel: string;
weekEndDay: string;
weekDayLabel: string;
lastLabel: string;
fourthLabel: string;
thirdLabel: string;
secondLabel: string;
firstLabel: string;
theLabel: string;
MonthsLabel: string;
ofEveryLabel: string;
AllowedValues1to12Label: string;
noEndDate: string;
everyweekdays: string;
days: string;
every: string;
EndByLabel: string;
EndAfterLabel: string;
HttpErrorMessage: string;
CategoryPlaceHolder: string;
CategoryLabel: string;
@ -52,7 +73,7 @@ declare interface ICalendarWebPartStrings {
Oct:string;
Nov:string;
Dez:string;
Dezember: string;
December: string;
November: string;
October: string;
September: string;
@ -91,7 +112,6 @@ declare interface ICalendarWebPartStrings {
previousLabel: string;
nextLabel: string;
showMore: string;
PropPanelSiteUrlErrorMessage: string;
}
declare module 'CalendarWebPartStrings' {

View File

@ -1,6 +1,26 @@
define([], function() {
return {
PropPanelSiteUrlErrorMessage:'Por favor verifique se site url é valido.',
WeeksOnLabel: "week(s) on",
PaternLabel: "Patern",
OcurrencesLabel: "Ocurrences",
dateRangeLabel: "Date Range",
weekEndDay: " 'weekend day'",
weekDayLabel: "weekday",
lastLabel: "last",
fourthLabel: " 'fourth'",
thirdLabel: "third",
secondLabel: " 'Second' ",
firstLabel: "first",
theLabel: "the",
MonthsLabel: "month(s)",
ofEveryLabel: "of every ",
AllowedValues1to12Label: "Allowed values 1 to 12",
noEndDate: "no end date",
everyweekdays: "every weekdays",
days: "days",
every: "every",
EndByLabel: "end by",
EndAfterLabel: "end after",
HttpErrorMessage: "Error reading calendar events:",
CategoryPlaceHolder: "Please select category",
CategoryLabel: "Category",