Merge pull request #882 from joaojmendes/dev
Modern Calendar Web Part - Added Recurrence Events
This commit is contained in:
commit
a8f951d7a5
|
@ -11,7 +11,7 @@ dist
|
|||
lib
|
||||
solution
|
||||
temp
|
||||
*.sppkg
|
||||
#*.sppkg
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
|
|
@ -22,11 +22,11 @@
|
|||
"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",
|
||||
"isDomainIsolated": false,
|
||||
"componentType": "webpart"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +1,43 @@
|
|||
# React Calendar
|
||||
|
||||
## Summary
|
||||
This Web Part allows you to manage events in a calendar.
|
||||
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.
|
||||
|
||||
|
||||
|
||||
##
|
||||
![callendar](/samples/react-calendar/assets/animatevideo.gif)
|
||||
![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 |
|
@ -8,6 +8,7 @@
|
|||
"entrypoint": "./lib/webparts/calendar/CalendarWebPart.js",
|
||||
"manifest": "./src/webparts/calendar/CalendarWebPart.manifest.json"
|
||||
}
|
||||
|
||||
]
|
||||
}
|
||||
},
|
||||
|
@ -17,4 +18,4 @@
|
|||
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
|
||||
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -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",
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,4 +25,8 @@ export interface IEventState {
|
|||
userPermissions?: IUserPermissions;
|
||||
isloading:boolean;
|
||||
siteRegionalSettings: any;
|
||||
recurrenceSeriesEdited?:boolean;
|
||||
showRecurrenceSeriesInfo:boolean;
|
||||
newRecurrenceEvent:boolean;
|
||||
recurrenceAction:string;
|
||||
}
|
||||
|
|
|
@ -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,16 +505,22 @@ 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' }}>
|
||||
{strings.SaveButtonLabel}
|
||||
</PrimaryButton>
|
||||
<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>
|
||||
<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>
|
||||
);
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
.divWrraper {
|
||||
border-width:1px;
|
||||
border-color:#adadad;
|
||||
padding: 20px;
|
||||
border-style: solid;
|
||||
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
.divWrraper {
|
||||
border-width:1px;
|
||||
border-color:#adadad;
|
||||
padding: 20px;
|
||||
border-style: solid;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
.divWrraper {
|
||||
border-width:1px;
|
||||
border-color:#adadad;
|
||||
padding: 20px;
|
||||
border-style: solid;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
.divWrraper {
|
||||
border-width:1px;
|
||||
border-color:#adadad;
|
||||
padding: 20px;
|
||||
border-style: solid;
|
||||
}
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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.");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
@ -240,13 +349,14 @@ export default class spservices {
|
|||
let userPermissions: IUserPermissions = undefined;
|
||||
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);
|
||||
hasPermissionView = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.ViewListItems);
|
||||
userPermissions = { hasPermissionAdd: hasPermissionAdd, hasPermissionEdit: hasPermissionEdit, hasPermissionDelete: hasPermissionDelete, hasPermissionView: hasPermissionView };
|
||||
const userEffectivePermissions = await web.lists.getById(listId).effectiveBasePermissions.get();
|
||||
// ...
|
||||
hasPermissionAdd = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.AddListItems);
|
||||
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,14 +464,13 @@ 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' />
|
||||
<Value IncludeTimeValue='false' Type='DateTime'>${moment(eventStartDate).format('YYYY-MM-DD')}</Value>
|
||||
|
@ -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);
|
||||
|
@ -723,15 +841,15 @@ export default class spservices {
|
|||
"♦": "♦"
|
||||
};
|
||||
|
||||
var entityMap = HtmlEntitiesMap;
|
||||
string = string.replace(/&/g, '&');
|
||||
string = string.replace(/"/g, '"');
|
||||
for (var key in entityMap) {
|
||||
var entity = entityMap[key];
|
||||
var regex = new RegExp(key, 'g');
|
||||
string = string.replace(regex, entity);
|
||||
}
|
||||
return string;
|
||||
var entityMap = HtmlEntitiesMap;
|
||||
string = string.replace(/&/g, '&');
|
||||
string = string.replace(/"/g, '"');
|
||||
for (var key in entityMap) {
|
||||
var entity = entityMap[key];
|
||||
var regex = new RegExp(key, 'g');
|
||||
string = string.replace(regex, entity);
|
||||
}
|
||||
return string;
|
||||
}
|
||||
|
||||
public async deCodeHtmlEntities(string: string) {
|
||||
|
|
|
@ -12,7 +12,8 @@
|
|||
"requiresCustomScript": false,
|
||||
"supportedHosts": [
|
||||
"SharePointWebPart",
|
||||
"TeamsTab"
|
||||
"TeamsTab",
|
||||
"SharePointFullPage"
|
||||
],
|
||||
"preconfiguredEntries": [
|
||||
{
|
||||
|
|
|
@ -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();
|
||||
this.lists = _lists;
|
||||
this.properties.list = this.lists.length > 0 ? this.lists[0].key.toString() : '';
|
||||
if ( _lists.length > 0 ){
|
||||
this.lists = _lists;
|
||||
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: [
|
||||
{
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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' {
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue