update docs

This commit is contained in:
joaojmendes 2019-06-11 17:10:09 +01:00
parent 1ba602962a
commit 13ecb88458
46 changed files with 5178 additions and 1292 deletions

View File

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

View File

@ -22,11 +22,11 @@
"framework": "react", "framework": "react",
"plusBeta": true, "plusBeta": true,
"isCreatingSolution": true, "isCreatingSolution": true,
"version": "1.8.0", "version": "1.8.2",
"libraryName": "react-calendar", "libraryName": "react-calendar",
"libraryId": "3a13208b-3874-4036-9262-4edd22e88187", "libraryId": "3a13208b-3874-4036-9262-4edd22e88187",
"packageManager": "npm", "packageManager": "npm",
"isDomainIsolated": false, "isDomainIsolated": false,
"componentType": "webpart" "componentType": "webpart"
} }
} }

View File

@ -5,8 +5,6 @@ This Web Part allows you to manage events in a calendar.
Uses a list of existing calendars on any website. 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 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. 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 checks the user's permissions for the View, Add, Edit, and Delete events.
@ -14,56 +12,72 @@ The Web Part checks the user's permissions for the View, Add, Edit, and Delete e
The Web Part does not show recurring events, I will work on it soon. The Web Part does not show recurring events, I will work on it soon.
##
![callendar](/assets/animatevideo.gif)
## ##
![callendar](/samples/react-calendar/assets/animatevideo.gif) ![callendar](/assets/weekly_moderncalendar.gif)
##
![callendar](/assets/modercalendar_monthly.gif)
##
![callendar](/assets/moderncalendar_yearly.gif)
## Web Part - Screenshots ## Web Part - Screenshots
![callendar](/samples/react-calendar//assets/screen1.png)
![callendar](/assets/calendar_teams.jpg)
![callendar](/assets/calendar_teams2.jpg)
![callendar](/assets/screen1.png)
![callendar](/samples/react-calendar/assets/screen1.0.jpg) ![callendar](/assets/screen1.0.png)
![callendar](/samples/react-calendar/assets/screen1.1.png) ![callendar](/assets/screen1.1.png)
![callendar](/samples/react-calendar/assets/screen1.2.png) ![callendar](/assets/screen1.2.png)
![callendar](/samples/react-calendar/assets/screen1.3.png) ![callendar](/assets/screen1.3.png)
![callendar](/samples/react-calendar//assets/screen1.4.png) ![callendar](/assets/screen1.4.png)
![callendar](/samples/react-calendar//assets/screen2.png) ![callendar](/assets/screen2.png)
![callendar](/samples/react-calendar/assets/screen3.png) ![callendar](/assets/screen3.png)
![callendar](/samples/react-calendar//assets/screen4.png) ![callendar](/assets/screen4.png)
![callendar](/samples/react-calendar/assets/screen5.png) ![callendar](/assets/screen5.png)
![callendar](/samples/react-calendar//assets/screen6.png) ![callendar](/assets/screen6.png)
![callendar](/samples/react-calendar//assets/screen7.png) ![callendar](/assets/screen7.png)
![callendar](/samples/react-calendar/assets/screen8.png) ![callendar](/assets/screen8.png)
![callendar](/samples/react-calendar//assets/screen9.png) ![callendar](/assets/screen9.png)
## ##
@ -103,6 +117,7 @@ Calendar Web Part|João Mendes
Version|Date|Comments Version|Date|Comments
-------|----|-------- -------|----|--------
1.0.0|April 25, 2019|Initial release 1.0.0|April 25, 2019|Initial release
1.0.1|June 10, 2019|update add recurrence events
## Disclaimer ## 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.** **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.**

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 MiB

View File

@ -8,6 +8,7 @@
"entrypoint": "./lib/webparts/calendar/CalendarWebPart.js", "entrypoint": "./lib/webparts/calendar/CalendarWebPart.js",
"manifest": "./src/webparts/calendar/CalendarWebPart.manifest.json" "manifest": "./src/webparts/calendar/CalendarWebPart.manifest.json"
} }
] ]
} }
}, },
@ -17,4 +18,4 @@
"ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js", "ControlStrings": "node_modules/@pnp/spfx-controls-react/lib/loc/{locale}.js",
"PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js" "PropertyControlStrings": "node_modules/@pnp/spfx-property-controls/lib/loc/{locale}.js"
} }
} }

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

@ -37,12 +37,14 @@ import {
Dialog, Dialog,
DialogType, DialogType,
DialogFooter, DialogFooter,
Toggle Toggle,
ActionButton,
IButtonProps
} }
from 'office-ui-fabric-react'; from 'office-ui-fabric-react';
import { addMonths, addYears } from 'office-ui-fabric-react/lib/utilities/dateMath/DateMath'; import { addMonths, addYears } from 'office-ui-fabric-react/lib/utilities/dateMath/DateMath';
import { _ComponentBaseKillSwitches } from '@microsoft/sp-component-base';
import { IPanelModelEnum } from './IPanelModeEnum'; import { IPanelModelEnum } from './IPanelModeEnum';
import { EditorState, convertToRaw, ContentState } from 'draft-js'; import { EditorState, convertToRaw, ContentState } from 'draft-js';
import { Editor } from 'react-draft-wysiwyg'; 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 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css';
import spservices from '../../services/spservices'; import spservices from '../../services/spservices';
import { Map, ICoordinates, MapType } from "@pnp/spfx-controls-react/lib/Map"; 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 today: Date = new Date(Date.now());
const DayPickerStrings: IDatePickerStrings = { 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], 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], 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], 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 attendees: IPersonaProps[] = [];
private latitude: number = 41.1931819; private latitude: number = 41.1931819;
private longitude: number = -8.4897452; private longitude: number = -8.4897452;
private returnedRecurrenceInfo: { recurrenceData: string, eventDate: Date, endDate: Date } = undefined;
private categoryDropdownOption: IDropdownOption[] = []; private categoryDropdownOption: IDropdownOption[] = [];
@ -93,7 +98,6 @@ export class Event extends React.Component<IEventProps, IEventState> {
} }
// Initialize Map coordinates // Initialize Map coordinates
console.log('ini', this.latitude, this.longitude);
this.state = { this.state = {
showPanel: false, showPanel: false,
eventData: this.props.event, eventData: this.props.event,
@ -112,6 +116,10 @@ export class Event extends React.Component<IEventProps, IEventState> {
displayDialog: false, displayDialog: false,
isloading: false, isloading: false,
siteRegionalSettings: undefined, siteRegionalSettings: undefined,
recurrenceSeriesEdited: false,
showRecurrenceSeriesInfo:false,
newRecurrenceEvent:false,
recurrenceAction: 'display',
userPermissions: { hasPermissionAdd: false, hasPermissionDelete: false, hasPermissionEdit: false, hasPermissionView: false }, userPermissions: { hasPermissionAdd: false, hasPermissionDelete: false, hasPermissionEdit: false, hasPermissionView: false },
}; };
// local copia of props // local copia of props
@ -131,8 +139,9 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.onDelete = this.onDelete.bind(this); this.onDelete = this.onDelete.bind(this);
this.closeDialog = this.closeDialog.bind(this); this.closeDialog = this.closeDialog.bind(this);
this.confirmDelete = this.confirmDelete.bind(this); this.confirmDelete = this.confirmDelete.bind(this);
this.onAllDayEventChange = this.onAllDayEventChange.bind(this);
this.onCategoryChanged = this.onCategoryChanged.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); this.spService = new spservices(this.props.context);
} }
/** /**
@ -152,27 +161,58 @@ export class Event extends React.Component<IEventProps, IEventState> {
private async onSave() { private async onSave() {
let eventData: IEventData = this.state.eventData; 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 startTime = `${this.state.startSelectedHour.key}:${this.state.startSelectedMin.key}`;
const startDateTime = `${startDate} ${startTime}`; const startDateTime = `${startDate} ${startTime}`;
const start = moment(startDateTime, 'YYYY/MM/DD HH:mm').toLocaleString(); const start = moment(startDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.start = new Date(start); eventData.EventDate = new Date(start);
// End Date // End Date
const endDate = `${moment(this.state.endDate).format('YYYY/MM/DD')}`;
const endTime = `${this.state.endSelectedHour.key}:${this.state.endSelectedMin.key}`; const endTime = `${this.state.endSelectedHour.key}:${this.state.endSelectedMin.key}`;
const endDateTime = `${endDate} ${endTime}`; const endDateTime = `${endDate} ${endTime}`;
const end = moment(endDateTime, 'YYYY/MM/DD HH:mm').toLocaleString(); const end = moment(endDateTime, 'YYYY/MM/DD HH:mm').toLocaleString();
eventData.end = new Date(end); eventData.EndDate = new Date(end);
// get Geolocation // get Geolocation
eventData.geolocation = { Latitude: this.latitude, Longitude: this.longitude }; eventData.geolocation = { Latitude: this.latitude, Longitude: this.longitude };
const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude); const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude);
eventData.location = locationInfo ? locationInfo.display_name : 'N/A'; eventData.location = locationInfo ? locationInfo.display_name : 'N/A';
console.log('beforeupd',eventData.geolocation);
// get Attendees // get Attendees
if (!eventData.attendes) { //vinitialize if no attendees if (!eventData.attendes) { //vinitialize if no attendees
eventData.attendes = []; eventData.attendes = [];
@ -184,13 +224,13 @@ export class Event extends React.Component<IEventProps, IEventState> {
try { try {
for (const user of this.attendees) { for (const user of this.attendees) {
const userInfo: any= await this.spService.getUserByLoginName(user.id, this.props.siteUrl); const userInfo: any = await this.spService.getUserByLoginName(user.id, this.props.siteUrl);
eventData.attendes.push(parseInt(userInfo.Id)); eventData.attendes.push(Number(userInfo.Id));
} }
this.setState({ isSaving: true }); this.setState({ isSaving: true });
switch (this.props.panelMode) { switch (panelMode) {
case IPanelModelEnum.edit: case IPanelModelEnum.edit:
await this.spService.updateEvent(eventData, this.props.siteUrl, this.props.listId); await this.spService.updateEvent(eventData, this.props.siteUrl, this.props.listId);
break; break;
@ -215,16 +255,22 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event * @memberof Event
*/ */
public componentDidCatch(error: any, errorInfo: any) { 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 * @memberof Event
*/ */
public async componentDidMount() { private async renderEventData(eventId?: number) {
this.setState({ isloading: true }); 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 // Load Regional Settings
const siteRegionalSettigns = await this.spService.getSiteRegionalSettingsTimeZone(this.props.siteUrl); const siteRegionalSettigns = await this.spService.getSiteRegionalSettingsTimeZone(this.props.siteUrl);
// chaeck User list Permissions // chaeck User list Permissions
@ -232,16 +278,16 @@ export class Event extends React.Component<IEventProps, IEventState> {
// Load Categories // Load Categories
this.categoryDropdownOption = await this.spService.getChoiceFieldOptions(this.props.siteUrl, this.props.listId, 'Category'); this.categoryDropdownOption = await this.spService.getChoiceFieldOptions(this.props.siteUrl, this.props.listId, 'Category');
// Edit Mode ? // Edit Mode ?
if (this.props.panelMode == IPanelModelEnum.edit && this.props.event) { if (this.props.panelMode == IPanelModelEnum.edit && event) {
// Get hours of event // Get hours of event
const startHour = moment(this.props.event.start).format('HH').toString(); const startHour = moment(event.EventDate).format('HH').toString();
const startMin = moment(this.props.event.start).format('mm').toString(); const startMin = moment(event.EventDate).format('mm').toString();
const endHour = moment(this.props.event.end).format('HH').toString(); const endHour = moment(event.EndDate).format('HH').toString();
const endMin = moment(this.props.event.end).format('mm').toString(); const endMin = moment(event.EndDate).format('mm').toString();
// Get Descript and covert to RichText Control // Get Descript and covert to RichText Control
const html = this.props.event.Description; const html = event.Description;
const contentBlock = htmlToDraft(html); const contentBlock = htmlToDraft(html);
if (contentBlock) { if (contentBlock) {
@ -250,7 +296,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
} }
// testa attendees // testa attendees
const attendees = this.props.event.attendes; const attendees = event.attendes;
let selectedUsers: string[] = []; let selectedUsers: string[] = [];
if (attendees && attendees.length > 0) { if (attendees && attendees.length > 0) {
for (const userId of attendees) { for (const userId of attendees) {
@ -261,14 +307,16 @@ export class Event extends React.Component<IEventProps, IEventState> {
} }
} }
// Has geolocation ? // Has geolocation ?
this.latitude = this.props.event.geolocation && this.props.event.geolocation.Latitude ? this.props.event.geolocation.Latitude : this.latitude; this.latitude = event.geolocation && event.geolocation.Latitude ? event.geolocation.Latitude : this.latitude;
this.longitude = this.props.event.geolocation && this.props.event.geolocation.Longitude ? this.props.event.geolocation.Longitude : this.longitude; 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 // Update Component Data
this.setState({ this.setState({
eventData: this.props.event, eventData: event,
startDate: this.props.event.start, startDate: event.EventDate,
endDate: this.props.event.end, endDate: event.EndDate,
startSelectedHour: { key: startHour, text: startHour }, startSelectedHour: { key: startHour, text: startHour },
startSelectedMin: { key: startMin, text: startMin }, startSelectedMin: { key: startMin, text: startMin },
endSelectedHour: { key: endHour, text: endHour }, endSelectedHour: { key: endHour, text: endHour },
@ -290,24 +338,30 @@ export class Event extends React.Component<IEventProps, IEventState> {
userPermissions: userListPermissions, userPermissions: userListPermissions,
isloading: false, isloading: false,
siteRegionalSettings: siteRegionalSettigns, siteRegionalSettings: siteRegionalSettigns,
eventData: { ...event, EventType: "0" },
}); });
} }
} }
/** /**
*
* *
* @memberof Event * @memberof Event
*/ */
public componentWillMount() { public async componentDidMount() {
await this.renderEventData();
} }
/** /**
* @private * @private
* @memberof Event * @memberof Event
*/ */
private onStartChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => { private onStartChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
ev.preventDefault();
this.setState({ startSelectedHour: item }); this.setState({ startSelectedHour: item });
} }
@ -316,7 +370,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event * @memberof Event
*/ */
private onEndChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => { private onEndChangeHour = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
ev.preventDefault();
this.setState({ endSelectedHour: item }); this.setState({ endSelectedHour: item });
} }
@ -325,7 +379,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event * @memberof Event
*/ */
private onStartChangeMin = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => { private onStartChangeMin = (ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void => {
ev.preventDefault();
this.setState({ startSelectedMin: item }); this.setState({ startSelectedMin: item });
} }
@ -374,12 +428,11 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event * @memberof Event
*/ */
private onEndChangeMin(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void { private onEndChangeMin(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
ev.preventDefault();
this.setState({ endSelectedMin: item }); this.setState({ endSelectedMin: item });
} }
/** /**
*
* *
* @private * @private
* @param {React.FormEvent<HTMLDivElement>} ev * @param {React.FormEvent<HTMLDivElement>} ev
@ -387,7 +440,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
* @memberof Event * @memberof Event
*/ */
private onCategoryChanged(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void { private onCategoryChanged(ev: React.FormEvent<HTMLDivElement>, item: IDropdownOption): void {
ev.preventDefault();
this.setState({ eventData: { ...this.state.eventData, Category: item.text } }); 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 }); this.setState({ displayDialog: false });
} }
/**
*
*
* @private
* @param {React.MouseEvent<HTMLDivElement>} ev
* @memberof Event
*/
private async confirmDelete(ev: React.MouseEvent<HTMLDivElement>) { private async confirmDelete(ev: React.MouseEvent<HTMLDivElement>) {
ev.preventDefault(); ev.preventDefault();
try { try {
@ -420,7 +480,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
switch (this.props.panelMode) { switch (this.props.panelMode) {
case IPanelModelEnum.edit: 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; break;
default: default:
break; break;
@ -428,7 +488,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
this.setState({ isDeleting: false }); this.setState({ isDeleting: false });
this.props.onDissmissPanel(true); this.props.onDissmissPanel(true);
} catch (error) { } 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> </DefaultButton>
{ {
this.props.panelMode == IPanelModelEnum.edit && this.state.userPermissions.hasPermissionDelete && ( 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} {strings.DeleteButtonLabel}
</DefaultButton> </DefaultButton>
) )
} }
{ {
(this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit) && (this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit) &&
<PrimaryButton disabled={this.state.disableButton} onClick={this.onSave} style={{ marginBottom: '15px', marginRight: '8px', float: 'right' }}> <PrimaryButton
{strings.SaveButtonLabel} disabled={this.state.disableButton}
</PrimaryButton> 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 * @private
@ -498,13 +561,45 @@ export class Event extends React.Component<IEventProps, IEventState> {
private async onUpdateCoordinates(coordinates: ICoordinates) { private async onUpdateCoordinates(coordinates: ICoordinates) {
this.latitude = coordinates.latitude; this.latitude = coordinates.latitude;
this.longitude = coordinates.longitude; this.longitude = coordinates.longitude;
console.log('upcoor',this.latitude + ' ' + this.longitude);
const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude); const locationInfo = await this.spService.getGeoLactionName(this.latitude, this.longitude);
this.setState({ eventData: { ...this.state.eventData, location: locationInfo.display_name } }); 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> { public render(): React.ReactElement<IEventProps> {
console.log(this.state.locationLatitude + '-' + this.state.locationLongitude);
const { editorState } = this.state; const { editorState } = this.state;
return ( return (
<div> <div>
@ -531,7 +626,23 @@ export class Event extends React.Component<IEventProps, IEventState> {
{ {
!this.state.isloading && !this.state.isloading &&
<div> <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 <TextField
label={strings.EventTitleLabel} label={strings.EventTitleLabel}
value={this.state.eventData ? this.state.eventData.title : ''} value={this.state.eventData ? this.state.eventData.title : ''}
@ -562,6 +673,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
label={strings.StartDateLabel} label={strings.StartDateLabel}
onSelectDate={this.onSelectDateStart} onSelectDate={this.onSelectDateStart}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true} disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
hidden={this.state.showRecurrenceSeriesInfo}
/> />
</div> </div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}> <div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
@ -632,6 +744,7 @@ export class Event extends React.Component<IEventProps, IEventState> {
label={strings.EndDateLabel} label={strings.EndDateLabel}
onSelectDate={this.onSelectDateEnd} onSelectDate={this.onSelectDateEnd}
disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true} disabled={this.state.userPermissions.hasPermissionAdd || this.state.userPermissions.hasPermissionEdit ? false : true}
hidden={this.state.showRecurrenceSeriesInfo}
/> />
</div> </div>
<div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}> <div style={{ display: 'inline-block', verticalAlign: 'top', paddingRight: 10 }}>
@ -693,7 +806,42 @@ export class Event extends React.Component<IEventProps, IEventState> {
</div> </div>
<Label>{this.state.siteRegionalSettings ? this.state.siteRegionalSettings.Description : ''}</Label> <Label>{this.state.siteRegionalSettings ? this.state.siteRegionalSettings.Description : ''}</Label>
<br /> <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}> <div className={styles.description}>
<Editor <Editor
@ -760,7 +908,9 @@ export class Event extends React.Component<IEventProps, IEventState> {
</DialogFooter> </DialogFooter>
</Dialog> </Dialog>
</div> </div>
} }
</Panel> </Panel>
</div> </div>
); );

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -8,14 +8,13 @@ import { SPHttpClient, SPHttpClientResponse, ISPHttpClientOptions, HttpClient, M
import * as $ from 'jquery'; import * as $ from 'jquery';
import { IEventData } from './IEventData'; import { IEventData } from './IEventData';
import { registerDefaultFontFaces } from "@uifabric/styling"; import { registerDefaultFontFaces } from "@uifabric/styling";
import { EventArgs } from "@microsoft/sp-core-library";
import * as moment from 'moment'; import * as moment from 'moment';
import { SiteUser } from "@pnp/sp/src/siteusers"; import { SiteUser } from "@pnp/sp/src/siteusers";
import { IUserPermissions } from './IUserPermissions'; import { IUserPermissions } from './IUserPermissions';
import { dateAdd } from "@pnp/common"; 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 // Class Services
export default class spservices { export default class spservices {
@ -35,8 +34,6 @@ export default class spservices {
} }
// OnInit Function // OnInit Function
private async onInit() { private async onInit() {
//this.appCatalogUrl = await this.getAppCatalogUrl();
} }
/** /**
@ -46,9 +43,9 @@ export default class spservices {
* @returns {Promise<number>} * @returns {Promise<number>}
* @memberof spservices * @memberof spservices
*/ */
private async getSiteTimeZoneHoursToUtc(siteUrl: string): Promise<number> { public async getSiteTimeZoneHours(siteUrl: string): Promise<number> {
let numberHours: number = 0; let numberHours: number = 0;
let siteTimeZoneHoursToUTC: any; let siteTimeZoneHours: any;
let siteTimeZoneBias: number; let siteTimeZoneBias: number;
let siteTimeZoneDaylightBias: number; let siteTimeZoneDaylightBias: number;
let currentDateTimeOffSet: number = new Date().getTimezoneOffset() / 60; 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. // Formula to calculate the number of hours need to get UTC Date.
// numberHours = (siteTimeZoneBias / 60) + (siteTimeZoneDaylightBias / 60) - currentDateTimeOffSet; // numberHours = (siteTimeZoneBias / 60) + (siteTimeZoneDaylightBias / 60) - currentDateTimeOffSet;
if ( siteTimeZoneBias >= 0 ){ if (siteTimeZoneBias >= 0) {
numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet) + siteTimeZoneDaylightBias/60 ; numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet) + siteTimeZoneDaylightBias / 60;
}else { } else {
numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet) ; numberHours = ((siteTimeZoneBias / 60) - currentDateTimeOffSet);
} }
} }
catch (error) { catch (error) {
@ -86,26 +83,85 @@ export default class spservices {
try { try {
const web = new Web(siteUrl); const web = new Web(siteUrl);
const siteTimeZoneHoursToUTC: number = await this.getSiteTimeZoneHoursToUtc(siteUrl); const siteTimeZoneHours: number = await this.getSiteTimeZoneHours(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId"
results = await web.lists.getById(listId).items.add({ results = await web.lists.getById(listId).items.add({
Title: newEvent.title, Title: newEvent.title,
Description: newEvent.Description, Description: newEvent.Description,
Geolocation: newEvent.geolocation, Geolocation: newEvent.geolocation,
ParticipantsPickerId: { results: newEvent.attendes }, ParticipantsPickerId: { results: newEvent.attendes },
EventDate: new Date(moment(newEvent.start).add(siteTimeZoneHoursToUTC, 'hours').toISOString()), EventDate: new Date(moment(newEvent.EventDate).add(siteTimeZoneHours, 'hours').toISOString()),
EndDate: new Date(moment(newEvent.end).add(siteTimeZoneHoursToUTC, 'hours').toISOString()), EndDate: new Date(moment(newEvent.EndDate).add(siteTimeZoneHours, 'hours').toISOString()),
Location: newEvent.location, Location: newEvent.location,
fAllDayEvent: false, fAllDayEvent: false,
fRecurrence: false, fRecurrence: newEvent.fRecurrence,
Category: newEvent.Category, 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) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
return results; 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 * @param {IEventData} newEvent
@ -118,21 +174,28 @@ export default class spservices {
let results = null; let results = null;
try { 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); const web = new Web(siteUrl);
//"Title","fRecurrence", "fAllDayEvent","EventDate", "EndDate", "Description","ID", "Location","Geolocation","ParticipantsPickerId" //"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, Title: updateEvent.title,
Description: updateEvent.Description, Description: updateEvent.Description,
Geolocation: updateEvent.geolocation, Geolocation: updateEvent.geolocation,
ParticipantsPickerId: { results: updateEvent.attendes }, ParticipantsPickerId: { results: updateEvent.attendes },
EventDate: new Date(moment(updateEvent.start).add(siteTimeZoneHoursToUTC, 'hours').toISOString()), EventDate: new Date(moment(updateEvent.EventDate).add(siteTimeZoneHours, 'hours').toISOString()),
EndDate: new Date(moment(updateEvent.end).add(siteTimeZoneHoursToUTC, 'hours').toISOString()), EndDate: new Date(moment(updateEvent.EndDate).add(siteTimeZoneHours, 'hours').toISOString()),
Location: updateEvent.location, Location: updateEvent.location,
fAllDayEvent: false, fAllDayEvent: false,
fRecurrence: false, fRecurrence: updateEvent.fRecurrence,
Category: updateEvent.Category, Category: updateEvent.Category,
UID: updateEvent.UID,
RecurrenceData: updateEvent.RecurrenceData ? await this.deCodeHtmlEntities(updateEvent.RecurrenceData) : "",
EventType: updateEvent.EventType,
MasterSeriesItemID: updateEvent.MasterSeriesItemID,
}); });
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
@ -140,6 +203,25 @@ export default class spservices {
return results; 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 * @param {IEventData} event
@ -148,17 +230,44 @@ export default class spservices {
* @returns * @returns
* @memberof spservices * @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; let results = null;
try { try {
const web = new Web(siteUrl); 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) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
return results; return;
} }
/** /**
* *
@ -218,7 +327,7 @@ export default class spservices {
public async getUserProfilePictureUrl(loginName: string) { public async getUserProfilePictureUrl(loginName: string) {
let results: any = null; let results: any = null;
try { try {
results = await sp.profiles.getPropertiesFor(loginName); results = await sp.profiles.usingCaching().getPropertiesFor(loginName);
} catch (error) { } catch (error) {
results = null; results = null;
} }
@ -240,13 +349,14 @@ export default class spservices {
let userPermissions: IUserPermissions = undefined; let userPermissions: IUserPermissions = undefined;
try { try {
const web = new Web(siteUrl); const web = new Web(siteUrl);
const userEffectivePermissions = await web.lists.getById(listId).effectiveBasePermissions.get(); const userEffectivePermissions = await web.lists.getById(listId).effectiveBasePermissions.get();
// chaeck user permissions // ...
hasPermissionAdd = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.AddListItems); hasPermissionAdd = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.AddListItems);
hasPermissionEdit =sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.EditListItems); hasPermissionDelete = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.DeleteListItems);
hasPermissionDelete =sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.DeleteListItems); hasPermissionEdit = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.EditListItems);
hasPermissionView = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.ViewListItems); hasPermissionView = sp.web.lists.getById(listId).hasPermissions(userEffectivePermissions, PermissionKind.ViewListItems);
userPermissions = { hasPermissionAdd: hasPermissionAdd, hasPermissionEdit: hasPermissionEdit, hasPermissionDelete: hasPermissionDelete, hasPermissionView: hasPermissionView }; userPermissions = { hasPermissionAdd: hasPermissionAdd, hasPermissionEdit: hasPermissionEdit, hasPermissionDelete: hasPermissionDelete, hasPermissionView: hasPermissionView };
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
} }
@ -289,6 +399,7 @@ export default class spservices {
for (var i = 0; i < 6; i++) { for (var i = 0; i < 6; i++) {
var x = Math.round(Math.random() * 14); var x = Math.round(Math.random() * 14);
var y = hexValues[x]; var y = hexValues[x];
newColor += y; newColor += y;
} }
@ -344,7 +455,7 @@ export default class spservices {
} }
try { try {
// Get Regional Settings TimeZone Hours to UTC // 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 // Get Category Field Choices
const categoryDropdownOption = await this.getChoiceFieldOptions(siteUrl, listId, 'Category'); const categoryDropdownOption = await this.getChoiceFieldOptions(siteUrl, listId, 'Category');
let categoryColor: { category: string, color: string }[] = []; let categoryColor: { category: string, color: string }[] = [];
@ -353,14 +464,13 @@ export default class spservices {
} }
const web = new Web(siteUrl); const web = new Web(siteUrl);
const results = await web.lists.getById(listId).renderListDataAsStream( const results = await web.lists.getById(listId).usingCaching().renderListDataAsStream(
{ {
DatesInUtc: true, 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> <Query>
<Where> <Where>
<And> <And>
<And>
<Geq> <Geq>
<FieldRef Name='EventDate' /> <FieldRef Name='EventDate' />
<Value IncludeTimeValue='false' Type='DateTime'>${moment(eventStartDate).format('YYYY-MM-DD')}</Value> <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> <Value IncludeTimeValue='false' Type='DateTime'>${moment(eventEndDate).format('YYYY-MM-DD')}</Value>
</Leq> </Leq>
</And> </And>
<Eq>
<FieldRef Name='fRecurrence' />
<Value Type='Recurrence'>0</Value>
</Eq>
</And>
</Where> </Where>
</Query> </Query>
<RowLimit Paged=\"FALSE\">2000</RowLimit> <RowLimit Paged=\"FALSE\">2000</RowLimit>
@ -383,7 +488,8 @@ export default class spservices {
); );
if (results && results.Row.length > 0) { 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 initialsArray: string[] = event.Author[0].title.split(' ');
const initials: string = initialsArray[0].charAt(0) + initialsArray[initialsArray.length - 1].charAt(0); 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}`); 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)); attendees.push(parseInt(attendee.id));
} }
events.push({ events.push({
id: event.ID, Id: event.ID,
ID: event.ID,
EventType: event.EventType,
title: await this.deCodeHtmlEntities(event.Title), title: await this.deCodeHtmlEntities(event.Title),
Description: event.Description, Description: event.Description,
// start: moment(event.EventDate).utc().toDate().setUTCMinutes(this.siteTimeZoneOffSet),
start: new Date(moment(event.EventDate).subtract((siteTimeZoneHoursToUTC), 'hour').toISOString()), EventDate: new Date(moment(event.EventDate).subtract((siteTimeZoneHours), 'hour').toISOString()),
// end: new Date(moment(event.EndDate).toLocaleString()),
end: new Date(moment(event.EndDate).subtract(siteTimeZoneHoursToUTC, 'hour').toISOString()), EndDate: new Date(moment(event.EndDate).subtract(siteTimeZoneHours, 'hour').toISOString()),
location: event.Location, location: event.Location,
ownerEmail: event.Author[0].email, ownerEmail: event.Author[0].email,
ownerPhoto: userPictureUrl ? ownerPhoto: userPictureUrl ?
`https://outlook.office365.com/owa/service.svc/s/GetPersonaPhoto?email=${event.Author[0].email}&UA=0&size=HR96x96` : '', `https://outlook.office365.com/owa/service.svc/s/GetPersonaPhoto?email=${event.Author[0].email}&UA=0&size=HR96x96` : '',
ownerInitial: initials, ownerInitial: initials,
// color: await this.colorGenerate(), color: CategoryColorValue.length > 0 ? CategoryColorValue[0].color : '#1a75ff', // blue default
color: CategoryColorValue.length > 0 ? CategoryColorValue[0].color : await this.colorGenerate,
ownerName: event.Author[0].title, ownerName: event.Author[0].title,
attendes: attendees, attendes: attendees,
allDayEvent: false, fAllDayEvent: false,
geolocation: { Longitude: parseFloat(geolocation[0]), Latitude: parseFloat(geolocation[1]) }, 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 Data
return events; return events;
@ -441,7 +559,7 @@ export default class spservices {
let regionalSettings: RegionalSettings; let regionalSettings: RegionalSettings;
try { try {
const web = new Web(siteUrl); const web = new Web(siteUrl);
regionalSettings = await web.regionalSettings.timeZone.get(); regionalSettings = await web.regionalSettings.timeZone.usingCaching().get();
} catch (error) { } catch (error) {
return Promise.reject(error); return Promise.reject(error);
@ -723,15 +841,15 @@ export default class spservices {
"♦": "&diams;" "♦": "&diams;"
}; };
var entityMap = HtmlEntitiesMap; var entityMap = HtmlEntitiesMap;
string = string.replace(/&/g, '&amp;'); string = string.replace(/&/g, '&amp;');
string = string.replace(/"/g, '&quot;'); string = string.replace(/"/g, '&quot;');
for (var key in entityMap) { for (var key in entityMap) {
var entity = entityMap[key]; var entity = entityMap[key];
var regex = new RegExp(key, 'g'); var regex = new RegExp(key, 'g');
string = string.replace(regex, entity); string = string.replace(regex, entity);
} }
return string; return string;
} }
public async deCodeHtmlEntities(string: string) { public async deCodeHtmlEntities(string: string) {

View File

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

View File

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

View File

@ -1,4 +1,21 @@
@import '~@microsoft/sp-office-ui-fabric-core/dist/sass/SPFabricCore.scss'; @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 { .Documentcard {

View File

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

View File

@ -1,18 +1,38 @@
define([], function () { define([], function () {
return { 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:", HttpErrorMessage: "Error reading calendar events:",
CategoryPlaceHolder: "Please select category", CategoryPlaceHolder: "Please select category",
CategoryLabel: "Category", CategoryLabel: "Category",
EnDateValidationMessage: "start date is greater than end date", EnDateValidationMessage: "start date is greater than end date",
SartDateValidationMessage: "start date is greater than end date", SartDateValidationMessage: "start date is greater than end date",
eventSelectDatesLabel: "Show only the events within the following dates", eventSelectDatesLabel: "Show only the events within the following dates",
ConfirmeDeleteMessage: "Confirm delete event ?", ConfirmeDeleteMessage: "Confirm delete event ? If the event is a recurrence event all entries will be deleted ",
DialogConfirmDeleteTitle: " 'Delete Event'", DialogConfirmDeleteTitle: "Delete Event",
SpinnerDeletingLabel: "Deleting...", SpinnerDeletingLabel: "Deleting...",
DialogCloseButtonLabel: "Cancel", DialogCloseButtonLabel: "Cancel",
DialogConfirmDeleteLabel: "Delete", DialogConfirmDeleteLabel: "Delete",
SaveButtonLabel: " Save", SaveButtonLabel: "Save",
DeleteButtonLabel: "Delete", DeleteButtonLabel: "Delete",
CancelButtonLabel: "Cancel", CancelButtonLabel: "Cancel",
LoadingEventsLabel: "Loading events...", LoadingEventsLabel: "Loading events...",
@ -54,7 +74,7 @@ define([], function () {
Oct:'Oct', Oct:'Oct',
Nov:'Nov', Nov:'Nov',
Dez:'Dez', Dez:'Dez',
Dezember: "December", December: "December",
November: " 'November'", November: " 'November'",
October: "October", October: "October",
September: "September", September: "September",

View File

@ -1,4 +1,25 @@
declare interface ICalendarWebPartStrings { 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; HttpErrorMessage: string;
CategoryPlaceHolder: string; CategoryPlaceHolder: string;
CategoryLabel: string; CategoryLabel: string;
@ -52,7 +73,7 @@ declare interface ICalendarWebPartStrings {
Oct:string; Oct:string;
Nov:string; Nov:string;
Dez:string; Dez:string;
Dezember: string; December: string;
November: string; November: string;
October: string; October: string;
September: string; September: string;
@ -91,7 +112,6 @@ declare interface ICalendarWebPartStrings {
previousLabel: string; previousLabel: string;
nextLabel: string; nextLabel: string;
showMore: string; showMore: string;
PropPanelSiteUrlErrorMessage: string;
} }
declare module 'CalendarWebPartStrings' { declare module 'CalendarWebPartStrings' {

View File

@ -1,6 +1,26 @@
define([], function() { define([], function() {
return { 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:", HttpErrorMessage: "Error reading calendar events:",
CategoryPlaceHolder: "Please select category", CategoryPlaceHolder: "Please select category",
CategoryLabel: "Category", CategoryLabel: "Category",