Upgraded react-holiday-calender to SPFx 1.18.2

This commit is contained in:
Harminder Singh 2023-11-30 23:18:19 +05:30
parent 1f2c411017
commit 8a4bb20ad4
20 changed files with 31478 additions and 17787 deletions

View File

@ -5,9 +5,9 @@
"nodeVersion": "16.16.0",
"sdksVersions": {
"@microsoft/microsoft-graph-client": "3.0.2",
"@microsoft/teams-js": "2.4.1"
"@microsoft/teams-js": "2.12.2"
},
"version": "1.16.1",
"version": "1.18.2",
"libraryName": "holidays-calendar",
"libraryId": "c85f1137-142e-4570-b3c9-3cdc47580452",
"environment": "spo",

View File

@ -26,8 +26,8 @@ ACE card extension provides below functionalities
This sample is optimally compatible with the following environment configuration:
![SPFx 1.16.1](https://img.shields.io/badge/SPFx-1.16.1-green.svg)
![Node.js v16 | v14 | v12](https://img.shields.io/badge/Node.js-v16%20%7C%20v14%20%7C%20v12-green.svg)
![SPFx 1.18.2](https://img.shields.io/badge/SPFx-1.18.2-green.svg)
![Node.js v18 | v18](https://img.shields.io/badge/Node.js-v18%20%7C%20v16-green.svg)
![Compatible with SharePoint Online](https://img.shields.io/badge/SharePoint%20Online-Compatible-green.svg)
![Does not work with SharePoint 2019](https://img.shields.io/badge/SharePoint%20Server%202019-Incompatible-red.svg "SharePoint Server 2019 requires SPFx 1.4.1 or lower")
![Does not work with SharePoint 2016 (Feature Pack 2)](https://img.shields.io/badge/SharePoint%20Server%202016%20(Feature%20Pack%202)-Incompatible-red.svg "SharePoint Server 2016 Feature Pack 2 requires SPFx 1.1")
@ -58,6 +58,7 @@ For more information about SPFx compatibility, please refer to <https://aka.ms/s
| Version | Date | Comments |
| ------- | ---------------- | --------------- |
| 1.0 | January 29, 2021 | Initial release |
| 1.1 | November 30, 2023 | Upgraded to SPFx 1.18.2 |
## Minimal Path to Awesome

View File

@ -3,7 +3,7 @@
"solution": {
"name": "holidays-calendar",
"id": "c85f1137-142e-4570-b3c9-3cdc47580452",
"version": "1.0.0.0",
"version": "1.1.0.0",
"includeClientSideAssets": true,
"skipFeatureDeployment": true,
"webApiPermissionRequests": [
@ -45,7 +45,7 @@
"title": "holidays-calendar Feature",
"description": "The feature that activates elements of the holidays-calendar solution.",
"id": "182f7879-26d6-4fb6-b5ad-9f34a54ba79d",
"version": "1.0.0.0"
"version": "1.1.0.0"
}
]

View File

@ -2,5 +2,5 @@
"$schema": "https://developer.microsoft.com/json-schemas/spfx-build/spfx-serve.schema.json",
"port": 4321,
"https": true,
"initialPage": "https://enter-your-SharePoint-site/_layouts/workbench.aspx"
"initialPage":"https://{tenantDomain}/_layouts/workbench.aspx"
}

File diff suppressed because it is too large Load Diff

View File

@ -1,9 +1,9 @@
{
"name": "holidays-calendar",
"version": "0.0.1",
"version": "1.1",
"private": true,
"engines": {
"node": ">=16.13.0 <17.0.0"
"node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0"
},
"main": "lib/index.js",
"scripts": {
@ -13,13 +13,15 @@
"serve": "gulp bundle --custom-serve --max_old_space_size=4096 && fast-serve"
},
"dependencies": {
"@fluentui/react-components": "^9.7.4",
"@microsoft/generator-sharepoint": "^1.16.1",
"@microsoft/sp-core-library": "1.16.1",
"@microsoft/sp-lodash-subset": "1.16.1",
"@microsoft/sp-office-ui-fabric-core": "1.16.1",
"@microsoft/sp-property-pane": "1.16.1",
"@microsoft/sp-webpart-base": "1.16.1",
"@fluentui/react": "8.106.4",
"@fluentui/react-components": "^9.42.0",
"@microsoft/generator-sharepoint": "^1.18.2",
"@microsoft/sp-adaptive-card-extension-base": "1.18.2",
"@microsoft/sp-core-library": "1.18.2",
"@microsoft/sp-lodash-subset": "1.18.2",
"@microsoft/sp-office-ui-fabric-core": "1.18.2",
"@microsoft/sp-property-pane": "1.18.2",
"@microsoft/sp-webpart-base": "1.18.2",
"@pnp/core": "^3.10.0",
"@pnp/graph": "^3.10.0",
"@pnp/logging": "^3.10.0",
@ -27,27 +29,27 @@
"@pnp/sp": "^3.10.0",
"json-to-csv-export": "^2.1.1",
"moment": "^2.29.4",
"office-ui-fabric-react": "^7.199.1",
"react": "17.0.1",
"react-dom": "17.0.1",
"tslib": "2.3.1",
"@microsoft/sp-adaptive-card-extension-base": "1.16.1"
"yarn": "^1.22.21"
},
"devDependencies": {
"@microsoft/eslint-config-spfx": "1.16.1",
"@microsoft/eslint-plugin-spfx": "1.16.1",
"@microsoft/microsoft-graph-types": "^2.25.0",
"@microsoft/rush-stack-compiler-4.5": "0.2.2",
"@microsoft/sp-build-web": "1.16.1",
"@microsoft/sp-module-interfaces": "1.16.1",
"@microsoft/eslint-config-spfx": "1.18.2",
"@microsoft/eslint-plugin-spfx": "1.18.2",
"@microsoft/microsoft-graph-types": "^2.40.0",
"@microsoft/rush-stack-compiler-4.7": "0.1.0",
"@microsoft/sp-build-web": "1.18.2",
"@microsoft/sp-module-interfaces": "1.18.2",
"@rushstack/eslint-config": "2.5.1",
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/webpack-env": "~1.15.2",
"ajv": "^6.12.5",
"eslint": "8.7.0",
"eslint-plugin-react-hooks": "4.3.0",
"gulp": "4.0.2",
"spfx-fast-serve-helpers": "~1.16.0",
"typescript": "4.5.5"
"spfx-fast-serve-helpers": "~1.18.2",
"typescript": "4.7.4"
}
}

View File

@ -0,0 +1,336 @@
# Upgrade project holidays-calendar to v1.18.2
Date: 11/30/2023
## Findings
Following is the list of steps required to upgrade your project to SharePoint Framework version 1.18.2. [Summary](#Summary) of the modifications is included at the end of the report.
### FN001001 @microsoft/sp-core-library | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-core-library
Execute the following command:
```sh
npm i -SE @microsoft/sp-core-library@1.18.2
```
File: [./package.json:18:5](./package.json)
### FN001002 @microsoft/sp-lodash-subset | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-lodash-subset
Execute the following command:
```sh
npm i -SE @microsoft/sp-lodash-subset@1.18.2
```
File: [./package.json:19:5](./package.json)
### FN001003 @microsoft/sp-office-ui-fabric-core | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-office-ui-fabric-core
Execute the following command:
```sh
npm i -SE @microsoft/sp-office-ui-fabric-core@1.18.2
```
File: [./package.json:20:5](./package.json)
### FN001004 @microsoft/sp-webpart-base | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-webpart-base
Execute the following command:
```sh
npm i -SE @microsoft/sp-webpart-base@1.18.2
```
File: [./package.json:22:5](./package.json)
### FN001021 @microsoft/sp-property-pane | Required
Upgrade SharePoint Framework dependency package @microsoft/sp-property-pane
Execute the following command:
```sh
npm i -SE @microsoft/sp-property-pane@1.18.2
```
File: [./package.json:21:5](./package.json)
### FN001034 @microsoft/sp-adaptive-card-extension-base | Optional
Upgrade SharePoint Framework dependency package @microsoft/sp-adaptive-card-extension-base
Execute the following command:
```sh
npm i -SE @microsoft/sp-adaptive-card-extension-base@1.18.2
```
File: [./package.json:34:5](./package.json)
### FN002001 @microsoft/sp-build-web | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-build-web
Execute the following command:
```sh
npm i -DE @microsoft/sp-build-web@1.18.2
```
File: [./package.json:41:5](./package.json)
### FN002002 @microsoft/sp-module-interfaces | Required
Upgrade SharePoint Framework dev dependency package @microsoft/sp-module-interfaces
Execute the following command:
```sh
npm i -DE @microsoft/sp-module-interfaces@1.18.2
```
File: [./package.json:42:5](./package.json)
### FN002022 @microsoft/eslint-plugin-spfx | Required
Upgrade SharePoint Framework dev dependency package @microsoft/eslint-plugin-spfx
Execute the following command:
```sh
npm i -DE @microsoft/eslint-plugin-spfx@1.18.2
```
File: [./package.json:38:5](./package.json)
### FN002023 @microsoft/eslint-config-spfx | Required
Upgrade SharePoint Framework dev dependency package @microsoft/eslint-config-spfx
Execute the following command:
```sh
npm i -DE @microsoft/eslint-config-spfx@1.18.2
```
File: [./package.json:37:5](./package.json)
### FN010001 .yo-rc.json version | Recommended
Update version in .yo-rc.json
```json
{
"@microsoft/generator-sharepoint": {
"version": "1.18.2"
}
}
```
File: [./.yo-rc.json:10:5](./.yo-rc.json)
### FN001022 office-ui-fabric-react | Required
Remove SharePoint Framework dependency package office-ui-fabric-react
Execute the following command:
```sh
npm un -S office-ui-fabric-react
```
File: [./package.json:30:5](./package.json)
### FN001035 @fluentui/react | Required
Install SharePoint Framework dependency package @fluentui/react
Execute the following command:
```sh
npm i -SE @fluentui/react@8.106.4
```
File: [./package.json:15:3](./package.json)
### FN002026 typescript | Required
Upgrade SharePoint Framework dev dependency package typescript
Execute the following command:
```sh
npm i -DE typescript@4.7.4
```
File: [./package.json:51:5](./package.json)
### FN002028 @microsoft/rush-stack-compiler-4.7 | Required
Install SharePoint Framework dev dependency package @microsoft/rush-stack-compiler-4.7
Execute the following command:
```sh
npm i -DE @microsoft/rush-stack-compiler-4.7@0.1.0
```
File: [./package.json:36:3](./package.json)
### FN010010 .yo-rc.json @microsoft/teams-js SDK version | Recommended
Update @microsoft/teams-js SDK version in .yo-rc.json
```json
{
"@microsoft/generator-sharepoint": {
"sdkVersions": {
"@microsoft/teams-js": "2.12.0"
}
}
}
```
File: [./.yo-rc.json:2:3](./.yo-rc.json)
### FN012017 tsconfig.json extends property | Required
Update tsconfig.json extends property
```json
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json"
}
```
File: [./tsconfig.json:2:2](./tsconfig.json)
### FN021003 package.json engines.node | Required
Update package.json engines.node property
```json
{
"engines": {
"node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0"
}
}
```
File: [./package.json:6:5](./package.json)
### FN002024 eslint | Required
Install SharePoint Framework dev dependency package eslint
Execute the following command:
```sh
npm i -DE eslint@8.7.0
```
File: [./package.json:36:3](./package.json)
### FN007002 serve.json initialPage | Required
Update serve.json initialPage URL
```json
{
"initialPage": "https://{tenantDomain}/_layouts/workbench.aspx"
}
```
File: [./config/serve.json:5:3](./config/serve.json)
### FN017001 Run npm dedupe | Optional
If, after upgrading npm packages, when building the project you have errors similar to: "error TS2345: Argument of type 'SPHttpClientConfiguration' is not assignable to parameter of type 'SPHttpClientConfiguration'", try running 'npm dedupe' to cleanup npm packages.
Execute the following command:
```sh
npm dedupe
```
File: [./package.json](./package.json)
## Summary
### Execute script
```sh
npm un -S office-ui-fabric-react
npm i -SE @microsoft/sp-core-library@1.18.2 @microsoft/sp-lodash-subset@1.18.2 @microsoft/sp-office-ui-fabric-core@1.18.2 @microsoft/sp-webpart-base@1.18.2 @microsoft/sp-property-pane@1.18.2 @microsoft/sp-adaptive-card-extension-base@1.18.2 @fluentui/react@8.106.4
npm i -DE @microsoft/sp-build-web@1.18.2 @microsoft/sp-module-interfaces@1.18.2 @microsoft/eslint-plugin-spfx@1.18.2 @microsoft/eslint-config-spfx@1.18.2 typescript@4.7.4 @microsoft/rush-stack-compiler-4.7@0.1.0 eslint@8.7.0
npm dedupe
```
### Modify files
#### [./.yo-rc.json](./.yo-rc.json)
Update version in .yo-rc.json:
```json
{
"@microsoft/generator-sharepoint": {
"version": "1.18.2"
}
}
```
Update @microsoft/teams-js SDK version in .yo-rc.json:
```json
{
"@microsoft/generator-sharepoint": {
"sdkVersions": {
"@microsoft/teams-js": "2.12.0"
}
}
}
```
#### [./tsconfig.json](./tsconfig.json)
Update tsconfig.json extends property:
```json
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json"
}
```
#### [./package.json](./package.json)
Update package.json engines.node property:
```json
{
"engines": {
"node": ">=16.13.0 <17.0.0 || >=18.17.1 <19.0.0"
}
}
```
#### [./config/serve.json](./config/serve.json)
Update serve.json initialPage URL:
```json
{
"initialPage": "https://{tenantDomain}/_layouts/workbench.aspx"
}
```

View File

@ -14,15 +14,15 @@ import { IEmployeeInfo } from "../../webparts/holidaysCalendar/interfaces/IHolid
import { ItemFailureQuickView } from "./quickView/ItemFailureQuickView";
export interface IHolidayViewAdaptiveCardExtensionProps {
title: string;
title: string;
}
export interface IHolidayViewAdaptiveCardExtensionState {
holidayItems: IHolidayItem[];
listItems: IHoliday[];
currentIndex: number;
holidayService: HolidaysCalendarService;
employeeInfo: IEmployeeInfo;
holidayItems: IHolidayItem[];
listItems: IHoliday[];
currentIndex: number;
holidayService: HolidaysCalendarService;
employeeInfo: IEmployeeInfo;
}
const CARD_VIEW_REGISTRY_ID: string = "HolidayView_CARD_VIEW";
@ -30,53 +30,53 @@ export const QUICK_VIEW_REGISTRY_ID: string = "HolidayView_QUICK_VIEW";
export const ITEMSUCCESS_QUICK_VIEW_REGISTRY_ID: string = "ItemSuccess_Quick_VIEW";
export const ITEMFAILURE_QUICK_VIEW_REGISTRY_ID: string = "ItemFailure_Quick_VIEW";
export default class HolidayViewAdaptiveCardExtension extends BaseAdaptiveCardExtension<
IHolidayViewAdaptiveCardExtensionProps,
IHolidayViewAdaptiveCardExtensionState
IHolidayViewAdaptiveCardExtensionProps,
IHolidayViewAdaptiveCardExtensionState
> {
private _deferredPropertyPane: HolidayViewPropertyPane | undefined;
private spService: SPService;
private graphService: GraphService;
private holidayService: HolidaysCalendarService;
public async onInit(): Promise<void> {
this.spService = new SPService(this.context);
this.graphService = new GraphService(this.context);
this.holidayService = new HolidaysCalendarService(this.spService, this.graphService);
const employeeInfo = await this.holidayService.getEmployeeInfo();
private _deferredPropertyPane: HolidayViewPropertyPane | undefined;
private spService: SPService;
private graphService: GraphService;
private holidayService: HolidaysCalendarService;
public async onInit(): Promise<void> {
this.spService = new SPService(this.context);
this.graphService = new GraphService(this.context);
this.holidayService = new HolidaysCalendarService(this.spService, this.graphService);
const employeeInfo = await this.holidayService.getEmployeeInfo();
const listItems = await this.holidayService.getHolidaysByLocation(employeeInfo.officeLocation);
const holidayItems = this.holidayService.getHolidayItemsToRender(listItems);
const upcomingHoliday = holidayItems.filter((item) => moment().isBefore(item.holidayDate.value, "days"));
const upcomingHolidayIndex = upcomingHoliday.length > 0 ? holidayItems.findIndex((item) => item.Id == upcomingHoliday[0].Id) : 0;
const listItems = await this.holidayService.getHolidaysByLocation(employeeInfo.officeLocation);
const holidayItems = this.holidayService.getHolidayItemsToRender(listItems);
const upcomingHoliday = holidayItems.filter((item) => moment().isBefore(item.holidayDate.value, "days"));
const upcomingHolidayIndex = upcomingHoliday.length > 0 ? holidayItems.findIndex((item) => item.Id === upcomingHoliday[0].Id) : 0;
this.state = {
holidayItems: holidayItems,
listItems: listItems,
currentIndex: upcomingHolidayIndex,
holidayService: this.holidayService,
employeeInfo: employeeInfo,
};
this.state = {
holidayItems: holidayItems,
listItems: listItems,
currentIndex: upcomingHolidayIndex,
holidayService: this.holidayService,
employeeInfo: employeeInfo,
};
this.cardNavigator.register(CARD_VIEW_REGISTRY_ID, () => new CardView());
this.quickViewNavigator.register(QUICK_VIEW_REGISTRY_ID, () => new QuickView());
this.quickViewNavigator.register(ITEMSUCCESS_QUICK_VIEW_REGISTRY_ID, () => new ItemSuccessQuickView());
this.quickViewNavigator.register(ITEMFAILURE_QUICK_VIEW_REGISTRY_ID, () => new ItemFailureQuickView());
return Promise.resolve();
}
this.cardNavigator.register(CARD_VIEW_REGISTRY_ID, () => new CardView());
this.quickViewNavigator.register(QUICK_VIEW_REGISTRY_ID, () => new QuickView());
this.quickViewNavigator.register(ITEMSUCCESS_QUICK_VIEW_REGISTRY_ID, () => new ItemSuccessQuickView());
this.quickViewNavigator.register(ITEMFAILURE_QUICK_VIEW_REGISTRY_ID, () => new ItemFailureQuickView());
return Promise.resolve();
}
protected loadPropertyPaneResources(): Promise<void> {
return import(
/* webpackChunkName: 'HolidayView-property-pane'*/
"./HolidayViewPropertyPane"
).then((component) => {
this._deferredPropertyPane = new component.HolidayViewPropertyPane();
});
}
protected loadPropertyPaneResources(): Promise<void> {
return import(
/* webpackChunkName: 'HolidayView-property-pane'*/
"./HolidayViewPropertyPane"
).then((component) => {
this._deferredPropertyPane = new component.HolidayViewPropertyPane();
});
}
protected renderCard(): string | undefined {
return CARD_VIEW_REGISTRY_ID;
}
protected renderCard(): string | undefined {
return CARD_VIEW_REGISTRY_ID;
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return this._deferredPropertyPane?.getPropertyPaneConfiguration();
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return this._deferredPropertyPane?.getPropertyPaneConfiguration();
}
}

View File

@ -1,28 +1,32 @@
import { ISPFxAdaptiveCard, BaseAdaptiveCardView, IActionArguments } from "@microsoft/sp-adaptive-card-extension-base";
import { ISPFxAdaptiveCard, BaseAdaptiveCardQuickView, IActionArguments } from "@microsoft/sp-adaptive-card-extension-base";
import { IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState } from "../HolidayViewAdaptiveCardExtension";
export interface IQuickViewData {
holidayTitle: string;
detail: string;
holidayTitle: string;
detail: string;
}
export class ItemFailureQuickView extends BaseAdaptiveCardView<IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState, IQuickViewData> {
public get data(): IQuickViewData {
return {
holidayTitle: this.state.holidayItems[this.state.currentIndex].holidayTitle.label,
detail: this.state.holidayItems[this.state.currentIndex].holidayDay.label + " " + this.state.holidayItems[this.state.currentIndex].holidayDate.label,
};
}
public onAction(action: IActionArguments): void {
if (action.type === "Submit") {
const { id } = action.data;
if (id === "back") {
this.quickViewNavigator.pop();
}
}
}
public get template(): ISPFxAdaptiveCard {
return require("./template/ItemFailureTemplate.json");
}
export class ItemFailureQuickView extends BaseAdaptiveCardQuickView<
IHolidayViewAdaptiveCardExtensionProps,
IHolidayViewAdaptiveCardExtensionState,
IQuickViewData
> {
public get data(): IQuickViewData {
return {
holidayTitle: this.state.holidayItems[this.state.currentIndex].holidayTitle.label,
detail: this.state.holidayItems[this.state.currentIndex].holidayDay.label + " " + this.state.holidayItems[this.state.currentIndex].holidayDate.label,
};
}
public onAction(action: IActionArguments): void {
if (action.type === "Submit") {
const { id } = action.data;
if (id === "back") {
this.quickViewNavigator.pop();
}
}
}
public get template(): ISPFxAdaptiveCard {
return require("./template/ItemFailureTemplate.json");
}
}

View File

@ -1,28 +1,32 @@
import { ISPFxAdaptiveCard, BaseAdaptiveCardView, IActionArguments } from "@microsoft/sp-adaptive-card-extension-base";
import { ISPFxAdaptiveCard, BaseAdaptiveCardQuickView, IActionArguments } from "@microsoft/sp-adaptive-card-extension-base";
import { IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState } from "../HolidayViewAdaptiveCardExtension";
export interface IQuickViewData {
holidayTitle: string;
detail: string;
holidayTitle: string;
detail: string;
}
export class ItemSuccessQuickView extends BaseAdaptiveCardView<IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState, IQuickViewData> {
public get data(): IQuickViewData {
return {
holidayTitle: this.state.holidayItems[this.state.currentIndex].holidayTitle.label,
detail: this.state.holidayItems[this.state.currentIndex].holidayDay.label + " " + this.state.holidayItems[this.state.currentIndex].holidayDate.label,
};
}
public onAction(action: IActionArguments): void {
if (action.type === "Submit") {
const { id } = action.data;
if (id === "back") {
this.quickViewNavigator.pop();
}
}
}
public get template(): ISPFxAdaptiveCard {
return require("./template/ItemSuccessTemplate.json");
}
export class ItemSuccessQuickView extends BaseAdaptiveCardQuickView<
IHolidayViewAdaptiveCardExtensionProps,
IHolidayViewAdaptiveCardExtensionState,
IQuickViewData
> {
public get data(): IQuickViewData {
return {
holidayTitle: this.state.holidayItems[this.state.currentIndex].holidayTitle.label,
detail: this.state.holidayItems[this.state.currentIndex].holidayDay.label + " " + this.state.holidayItems[this.state.currentIndex].holidayDate.label,
};
}
public onAction(action: IActionArguments): void {
if (action.type === "Submit") {
const { id } = action.data;
if (id === "back") {
this.quickViewNavigator.pop();
}
}
}
public get template(): ISPFxAdaptiveCard {
return require("./template/ItemSuccessTemplate.json");
}
}

View File

@ -1,34 +1,34 @@
import { ISPFxAdaptiveCard, BaseAdaptiveCardView, IActionArguments } from "@microsoft/sp-adaptive-card-extension-base";
import { ISPFxAdaptiveCard, BaseAdaptiveCardQuickView, IActionArguments } from "@microsoft/sp-adaptive-card-extension-base";
import { IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState } from "../HolidayViewAdaptiveCardExtension";
import { ITEMSUCCESS_QUICK_VIEW_REGISTRY_ID, ITEMFAILURE_QUICK_VIEW_REGISTRY_ID } from "../HolidayViewAdaptiveCardExtension";
export interface IQuickViewData {
holidayTitle: string;
detail: string;
holidayTitle: string;
detail: string;
}
export class QuickView extends BaseAdaptiveCardView<IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState, IQuickViewData> {
public get data(): IQuickViewData {
return {
holidayTitle: this.state.holidayItems[this.state.currentIndex].holidayTitle.label,
detail: this.state.holidayItems[this.state.currentIndex].holidayDay.label + " " + this.state.holidayItems[this.state.currentIndex].holidayDate.label,
};
}
public onAction(action: IActionArguments): void {
if (action.type === "Submit") {
const { id } = action.data;
if (id === "addInCalendar") {
this.state.holidayService
.addLeaveInCalendar(this.state.employeeInfo, this.state.holidayItems[this.state.currentIndex])
.then((value) => this.quickViewNavigator.push(ITEMSUCCESS_QUICK_VIEW_REGISTRY_ID))
.catch((ex) => {
console.log(ex);
this.quickViewNavigator.push(ITEMFAILURE_QUICK_VIEW_REGISTRY_ID);
});
}
}
}
public get template(): ISPFxAdaptiveCard {
return require("./template/QuickViewTemplate.json");
}
export class QuickView extends BaseAdaptiveCardQuickView<IHolidayViewAdaptiveCardExtensionProps, IHolidayViewAdaptiveCardExtensionState, IQuickViewData> {
public get data(): IQuickViewData {
return {
holidayTitle: this.state.holidayItems[this.state.currentIndex].holidayTitle.label,
detail: this.state.holidayItems[this.state.currentIndex].holidayDay.label + " " + this.state.holidayItems[this.state.currentIndex].holidayDate.label,
};
}
public onAction(action: IActionArguments): void {
if (action.type === "Submit") {
const { id } = action.data;
if (id === "addInCalendar") {
this.state.holidayService
.addLeaveInCalendar(this.state.employeeInfo, this.state.holidayItems[this.state.currentIndex])
.then((value) => this.quickViewNavigator.push(ITEMSUCCESS_QUICK_VIEW_REGISTRY_ID))
.catch((ex) => {
console.log(ex);
this.quickViewNavigator.push(ITEMFAILURE_QUICK_VIEW_REGISTRY_ID);
});
}
}
}
public get template(): ISPFxAdaptiveCard {
return require("./template/QuickViewTemplate.json");
}
}

View File

@ -1,38 +1,38 @@
import { MSGraphClientV3 } from "@microsoft/sp-http-msgraph";
/* eslint-disable */
import { Event as IEventType, User } from "@microsoft/microsoft-graph-types";
import { MSGraphClientV3, GraphRequest } from "@microsoft/sp-http-msgraph";
export class GraphService {
private context: any;
private context: any;
constructor(context: any) {
this.context = context;
}
constructor(context: any) {
this.context = context;
}
private getClient = async (): Promise<MSGraphClientV3> => {
return await this.context.msGraphClientFactory.getClient("3");
};
public getMyInformation = async () => {
const client = await this.getClient();
const request: GraphRequest = client.api("/me");
const employeeInfo: User = await request.get();
return Promise.resolve(employeeInfo);
};
public getMyTimeZone = async (): Promise<string> => {
const client = await this.getClient();
const request: GraphRequest = client.api("/me/mailboxSettings");
const response = await request.get();
return Promise.resolve(response.timeZone);
};
private getClient = async (): Promise<MSGraphClientV3> => {
return await this.context.msGraphClientFactory.getClient("3");
};
public getMyInformation = async () => {
const client = await this.getClient();
const request = client.api("/me");
const employeeInfo: User = await request.get();
return Promise.resolve(employeeInfo);
};
public getMyTimeZone = async (): Promise<string> => {
const client = await this.getClient();
const request = client.api("/me/mailboxSettings");
const response = await request.get();
return Promise.resolve(response.timeZone);
};
public createEvent = async (detail: IEventType): Promise<any> => {
const client = await this.getClient();
const request: GraphRequest = client.api("/me/events");
public createEvent = async (detail: IEventType): Promise<any> => {
const client = await this.getClient();
const request = client.api("/me/events");
const response = await request.post(detail);
const response = await request.post(detail);
return Promise.resolve(response);
};
return Promise.resolve(response);
};
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { HolidaysListColumns, HolidaysListSelectColumns } from "../constants/constant";
import { ListName } from "../constants/constant";
import { GraphService } from "./GraphService";
@ -9,140 +10,140 @@ import { IEmployeeInfo } from "../../webparts/holidaysCalendar/interfaces/IHolid
import { IHolidayItem, IHoliday } from "../interfaces/HolidaysCalendar";
export class HolidaysCalendarService {
private spService: SPService;
private graphService: GraphService;
constructor(spService: SPService, graphService: GraphService) {
this.spService = spService;
this.graphService = graphService;
}
private spService: SPService;
private graphService: GraphService;
constructor(spService: SPService, graphService: GraphService) {
this.spService = spService;
this.graphService = graphService;
}
public getHolidayItemsToRender = (items: any[]): IHolidayItem[] => {
const holidayItems: IHolidayItem[] = [];
items.forEach((item) => {
holidayItems.push({
holidayTitle: {
label: item.Title,
},
holidayDate: {
value: item.Date,
label: moment(item.Date).format("MMMM Do"),
},
holidayDay: {
label: moment(item.Date).format("dddd"),
},
holidayType: {
optional: item.Optional,
},
Id: item.Id,
});
});
return holidayItems;
};
public getHolidayItemsToRender = (items: any[]): IHolidayItem[] => {
const holidayItems: IHolidayItem[] = [];
items.forEach((item) => {
holidayItems.push({
holidayTitle: {
label: item.Title,
},
holidayDate: {
value: item.Date,
label: moment(item.Date).format("MMMM Do"),
},
holidayDay: {
label: moment(item.Date).format("dddd"),
},
holidayType: {
optional: item.Optional,
},
Id: item.Id,
});
});
return holidayItems;
};
public getHolidayItemsToRender_ACE = (items: IHoliday[]): IHolidayItem[] => {
const holidayItems: IHolidayItem[] = [];
items.forEach((item) => {
if (moment().isBefore(item.Date, "days")) {
holidayItems.push({
holidayTitle: {
label: item.Title,
},
holidayDate: {
value: item.Date,
label: moment(item.Date).format("MMMM Do"),
},
holidayDay: {
label: moment(item.Date).format("dddd"),
},
holidayType: {
optional: item.Optional,
},
Id: item.Id,
});
}
});
return holidayItems;
};
public getHolidayItemsToRender_ACE = (items: IHoliday[]): IHolidayItem[] => {
const holidayItems: IHolidayItem[] = [];
items.forEach((item) => {
if (moment().isBefore(item.Date, "days")) {
holidayItems.push({
holidayTitle: {
label: item.Title,
},
holidayDate: {
value: item.Date,
label: moment(item.Date).format("MMMM Do"),
},
holidayDay: {
label: moment(item.Date).format("dddd"),
},
holidayType: {
optional: item.Optional,
},
Id: item.Id,
});
}
});
return holidayItems;
};
public getHolidaysByLocation = async (location: string): Promise<IHoliday[]> => {
const holidays: IHoliday[] = [];
const filter = `${HolidaysListColumns.Location} eq '${location}'`;
try {
const listItems: any[] = await this.spService.getListItems(ListName.Holidays, filter, HolidaysListSelectColumns);
public getHolidaysByLocation = async (location: string): Promise<IHoliday[]> => {
const holidays: IHoliday[] = [];
const filter = `${HolidaysListColumns.Location} eq '${location}'`;
try {
const listItems: any[] = await this.spService.getListItems(ListName.Holidays, filter, HolidaysListSelectColumns);
listItems.forEach((listItem: any) => {
const holiday: IHoliday = {
Id: listItem.Id,
Title: listItem.Title ?? null,
Date: Boolean(listItem.Date) ? new Date(listItem.Date) : null,
Location: listItem.Location ?? null,
Optional: listItem.Optional,
};
holidays.push(holiday);
});
return Promise.resolve(holidays);
} catch (ex) {
return Promise.reject(holidays);
}
};
listItems.forEach((listItem: any) => {
const holiday: IHoliday = {
Id: listItem.Id,
Title: listItem.Title ?? null,
Date: Boolean(listItem.Date) ? new Date(listItem.Date) : null,
Location: listItem.Location ?? null,
Optional: listItem.Optional,
};
holidays.push(holiday);
});
return Promise.resolve(holidays);
} catch (ex) {
return Promise.reject(holidays);
}
};
public getEmployeeInfo = async (): Promise<IEmployeeInfo> => {
const [myInformation, myTimeZone] = await Promise.all([this.graphService.getMyInformation(), this.graphService.getMyTimeZone()]);
const employeeInformation = {} as IEmployeeInfo;
employeeInformation.eMail = myInformation.mail;
employeeInformation.id = myInformation.id;
employeeInformation.officeLocation = myInformation.officeLocation;
employeeInformation.displayName = myInformation.displayName;
employeeInformation.timezone = myTimeZone;
return Promise.resolve(employeeInformation);
};
public addLeaveInCalendar = async (employeeInfo: IEmployeeInfo, item: IHolidayItem): Promise<boolean> => {
try {
const eventDetail: IEventType = {};
eventDetail.subject = item.holidayTitle.label ?? null;
eventDetail.start = {
dateTime: moment(item.holidayDate.value).format("YYYY-MM-DD") + "T00:00:00",
timeZone: employeeInfo.timezone,
};
eventDetail.end = {
dateTime: moment(item.holidayDate.value).format("YYYY-MM-DD") + "T23:59:59",
timeZone: employeeInfo.timezone,
};
eventDetail.showAs = "oof";
eventDetail.attendees = [
{
emailAddress: {
address: employeeInfo.eMail,
name: employeeInfo.displayName,
},
},
];
public getEmployeeInfo = async (): Promise<IEmployeeInfo> => {
const [myInformation, myTimeZone] = await Promise.all([this.graphService.getMyInformation(), this.graphService.getMyTimeZone()]);
const employeeInformation = {} as IEmployeeInfo;
employeeInformation.eMail = myInformation.mail;
employeeInformation.id = myInformation.id;
employeeInformation.officeLocation = myInformation.officeLocation;
employeeInformation.displayName = myInformation.displayName;
employeeInformation.timezone = myTimeZone;
return Promise.resolve(employeeInformation);
};
public addLeaveInCalendar = async (employeeInfo: IEmployeeInfo, item: IHolidayItem): Promise<boolean> => {
try {
const eventDetail: IEventType = {};
eventDetail.subject = item.holidayTitle.label ?? null;
eventDetail.start = {
dateTime: moment(item.holidayDate.value).format("YYYY-MM-DD") + "T00:00:00",
timeZone: employeeInfo.timezone,
};
eventDetail.end = {
dateTime: moment(item.holidayDate.value).format("YYYY-MM-DD") + "T23:59:59",
timeZone: employeeInfo.timezone,
};
eventDetail.showAs = "oof";
eventDetail.attendees = [
{
emailAddress: {
address: employeeInfo.eMail,
name: employeeInfo.displayName,
},
},
];
await this.graphService.createEvent(eventDetail);
return Promise.resolve(true);
} catch (ex) {
return Promise.reject(false);
}
};
await this.graphService.createEvent(eventDetail);
return Promise.resolve(true);
} catch (ex) {
return Promise.reject(false);
}
};
public getItemsToDownloadAsCSV = (items: IHolidayItem[]) => {
const itemsToExport: any[] = [];
items.forEach((item) => {
itemsToExport.push({
Title: item.holidayTitle.label,
Date: item.holidayDate.label,
Day: item.holidayDay.label,
IsOptional: item.holidayType.optional ? "Yes" : "No",
});
});
public getItemsToDownloadAsCSV = (items: IHolidayItem[]): any => {
const itemsToExport: any[] = [];
items.forEach((item) => {
itemsToExport.push({
Title: item.holidayTitle.label,
Date: item.holidayDate.label,
Day: item.holidayDay.label,
IsOptional: item.holidayType.optional ? "Yes" : "No",
});
});
const dataToDownload = {
data: itemsToExport,
filename: "HolidayCalendar",
delimiter: ",",
headers: ["Title", "Date", "Day", "IsOptional"],
};
const dataToDownload = {
data: itemsToExport,
filename: "HolidayCalendar",
delimiter: ",",
headers: ["Title", "Date", "Day", "IsOptional"],
};
return dataToDownload;
};
return dataToDownload;
};
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { SPFI, spfi, SPFx as spSPFx } from "@pnp/sp";
import "@pnp/sp/webs";
@ -8,60 +9,60 @@ import "@pnp/sp/items/get-all";
import "@pnp/sp/site-users/web";
export class SPService {
private sp: SPFI;
constructor(context: any) {
this.sp = spfi().using(spSPFx(context));
}
private sp: SPFI;
constructor(context: any) {
this.sp = spfi().using(spSPFx(context));
}
public getListItems = async (
listTitle: string,
filter: string = "",
columns: string = "*",
expand: string = "",
orderby?: string,
orderSequence?: boolean
): Promise<any> => {
let items: any = [];
try {
if (!orderby) {
items = await this.sp.web.lists.getByTitle(listTitle).items.select(columns).filter(filter).expand(expand).top(5000)();
} else {
return await this.sp.web.lists.getByTitle(listTitle).items.select(columns).orderBy(orderby, orderSequence).filter(filter).expand(expand).top(5000)();
}
return Promise.resolve(items);
} catch (ex) {
return Promise.reject(ex);
}
};
public getListItems = async (
listTitle: string,
filter: string = "",
columns: string = "*",
expand: string = "",
orderby?: string,
orderSequence?: boolean
): Promise<any> => {
let items: any = [];
try {
if (!orderby) {
items = await this.sp.web.lists.getByTitle(listTitle).items.select(columns).filter(filter).expand(expand).top(5000)();
} else {
return await this.sp.web.lists.getByTitle(listTitle).items.select(columns).orderBy(orderby, orderSequence).filter(filter).expand(expand).top(5000)();
}
return Promise.resolve(items);
} catch (ex) {
return Promise.reject(ex);
}
};
public getSharePointGroupDetails = async (groupTitle: string): Promise<any> => {
try {
const response = await this.sp.web.siteGroups.getByName(groupTitle)();
return Promise.resolve(response);
} catch (ex) {
return Promise.reject(ex);
}
};
public getSharePointGroupDetails = async (groupTitle: string): Promise<any> => {
try {
const response = await this.sp.web.siteGroups.getByName(groupTitle)();
return Promise.resolve(response);
} catch (ex) {
return Promise.reject(ex);
}
};
public getUserDetails = async (userId: number): Promise<any> => {
try {
const response = await this.sp.web.getUserById(userId)();
return Promise.resolve(response);
} catch (ex) {
return Promise.reject(ex);
}
};
public getUserDetails = async (userId: number): Promise<any> => {
try {
const response = await this.sp.web.getUserById(userId)();
return Promise.resolve(response);
} catch (ex) {
return Promise.reject(ex);
}
};
public ensureUser = async (loginName: string): Promise<any> => {
if (loginName.indexOf("i:0#.f|membership|") === -1) {
loginName = "i:0#.f|membership|" + loginName;
}
public ensureUser = async (loginName: string): Promise<any> => {
if (loginName.indexOf("i:0#.f|membership|") === -1) {
loginName = "i:0#.f|membership|" + loginName;
}
try {
const response = await (await this.sp.web.ensureUser(loginName)).user();
return Promise.resolve(response);
} catch (ex) {
return Promise.reject(ex);
}
};
try {
const response = await (await this.sp.web.ensureUser(loginName)).user();
return Promise.resolve(response);
} catch (ex) {
return Promise.reject(ex);
}
};
}

View File

@ -1,3 +1,4 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { GraphService } from "./../../common/services/GraphService";
import * as React from "react";
import * as ReactDom from "react-dom";
@ -11,132 +12,132 @@ import { IHolidaysCalendarProps } from "./components/IHolidaysCalendarProps";
import { SPService } from "../../common/services/SPService";
export interface IHolidaysCalendarWebPartProps {
enableDownload: boolean;
title: string;
showFixedOptional: boolean;
enableDownload: boolean;
title: string;
showFixedOptional: boolean;
}
export default class HolidaysCalendarWebPart extends BaseClientSideWebPart<IHolidaysCalendarWebPartProps> {
private _isDarkTheme: boolean = false;
private _environmentMessage: string = "";
private spService: SPService;
private graphService: GraphService;
private _isDarkTheme: boolean = false;
private _environmentMessage: string = "";
private spService: SPService;
private graphService: GraphService;
public render(): void {
const element: React.ReactElement<IHolidaysCalendarProps> = React.createElement(HolidaysCalendar, {
isDarkTheme: this._isDarkTheme,
environmentMessage: this._environmentMessage,
hasTeamsContext: !!this.context.sdks.microsoftTeams,
userDisplayName: this.context.pageContext.user.displayName,
spService: this.spService,
graphService: this.graphService,
context: this.context,
showDownload: this.properties.enableDownload ?? false,
title: this.properties.title,
showFixedOptional: this.properties.showFixedOptional,
});
public render(): void {
const element: React.ReactElement<IHolidaysCalendarProps> = React.createElement(HolidaysCalendar, {
isDarkTheme: this._isDarkTheme,
environmentMessage: this._environmentMessage,
hasTeamsContext: !!this.context.sdks.microsoftTeams,
userDisplayName: this.context.pageContext.user.displayName,
spService: this.spService,
graphService: this.graphService,
context: this.context,
showDownload: this.properties.enableDownload ?? false,
title: this.properties.title,
showFixedOptional: this.properties.showFixedOptional,
});
ReactDom.render(element, this.domElement);
}
ReactDom.render(element, this.domElement);
}
protected onInit(): Promise<void> {
return this._getEnvironmentMessage().then((message) => {
this.spService = new SPService(this.context);
this.graphService = new GraphService(this.context);
this._environmentMessage = message;
});
}
protected onInit(): Promise<void> {
return this._getEnvironmentMessage().then((message) => {
this.spService = new SPService(this.context);
this.graphService = new GraphService(this.context);
this._environmentMessage = message;
});
}
private _getEnvironmentMessage(): Promise<string> {
if (!!this.context.sdks.microsoftTeams) {
// running in Teams, office.com or Outlook
return this.context.sdks.microsoftTeams.teamsJs.app.getContext().then((context) => {
let environmentMessage: string = "";
switch (context.app.host.name) {
case "Office": // running in Office
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
break;
case "Outlook": // running in Outlook
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
break;
case "Teams": // running in Teams
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
break;
default:
throw new Error("Unknown host");
}
private _getEnvironmentMessage(): Promise<string> {
if (!!this.context.sdks.microsoftTeams) {
// running in Teams, office.com or Outlook
return this.context.sdks.microsoftTeams.teamsJs.app.getContext().then((context) => {
let environmentMessage: string = "";
switch (context.app.host.name) {
case "Office": // running in Office
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOffice : strings.AppOfficeEnvironment;
break;
case "Outlook": // running in Outlook
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentOutlook : strings.AppOutlookEnvironment;
break;
case "Teams": // running in Teams
environmentMessage = this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentTeams : strings.AppTeamsTabEnvironment;
break;
default:
throw new Error("Unknown host");
}
return environmentMessage;
});
}
return environmentMessage;
});
}
return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
}
return Promise.resolve(this.context.isServedFromLocalhost ? strings.AppLocalEnvironmentSharePoint : strings.AppSharePointEnvironment);
}
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
if (!currentTheme) {
return;
}
protected onThemeChanged(currentTheme: IReadonlyTheme | undefined): void {
if (!currentTheme) {
return;
}
this._isDarkTheme = !!currentTheme.isInverted;
const { semanticColors } = currentTheme;
this._isDarkTheme = !!currentTheme.isInverted;
const { semanticColors } = currentTheme;
if (semanticColors) {
this.domElement.style.setProperty("--bodyText", semanticColors.bodyText || null);
this.domElement.style.setProperty("--link", semanticColors.link || null);
this.domElement.style.setProperty("--linkHovered", semanticColors.linkHovered || null);
}
}
if (semanticColors) {
this.domElement.style.setProperty("--bodyText", semanticColors.bodyText || null);
this.domElement.style.setProperty("--link", semanticColors.link || null);
this.domElement.style.setProperty("--linkHovered", semanticColors.linkHovered || null);
}
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected onDispose(): void {
ReactDom.unmountComponentAtNode(this.domElement);
}
protected get dataVersion(): Version {
return Version.parse("1.0");
}
protected get dataVersion(): Version {
return Version.parse("1.0");
}
protected onPropertyPaneFieldChanged = (propertyPath: string, oldValue: any, newValue: any): void => {
if (propertyPath === "enableDownload" && oldValue !== newValue) {
this.properties.enableDownload = newValue;
}
if (propertyPath === "title" && oldValue !== newValue) {
this.properties.title = newValue;
}
if (propertyPath === "showFixedOptional" && oldValue !== newValue) {
this.properties.showFixedOptional = newValue;
}
};
protected onPropertyPaneFieldChanged = (propertyPath: string, oldValue: any, newValue: any): void => {
if (propertyPath === "enableDownload" && oldValue !== newValue) {
this.properties.enableDownload = newValue;
}
if (propertyPath === "title" && oldValue !== newValue) {
this.properties.title = newValue;
}
if (propertyPath === "showFixedOptional" && oldValue !== newValue) {
this.properties.showFixedOptional = newValue;
}
};
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription,
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField("title", {
label: "Title",
}),
PropertyPaneToggle("enableDownload", {
key: "enableDownload",
label: "Show Download Option",
checked: false,
}),
PropertyPaneToggle("showFixedOptional", {
key: "showFixedOptional",
label: "Show Fixed Optional Icons",
checked: false,
}),
],
},
],
},
],
};
}
protected getPropertyPaneConfiguration(): IPropertyPaneConfiguration {
return {
pages: [
{
header: {
description: strings.PropertyPaneDescription,
},
groups: [
{
groupName: strings.BasicGroupName,
groupFields: [
PropertyPaneTextField("title", {
label: "Title",
}),
PropertyPaneToggle("enableDownload", {
key: "enableDownload",
label: "Show Download Option",
checked: false,
}),
PropertyPaneToggle("showFixedOptional", {
key: "showFixedOptional",
label: "Show Fixed Optional Icons",
checked: false,
}),
],
},
],
},
],
};
}
}

View File

@ -7,71 +7,71 @@ import { IHolidaysCalendarProps } from "./IHolidaysCalendarProps";
import { FluentProvider, webLightTheme } from "@fluentui/react-components";
import { DismissCircleRegular } from "@fluentui/react-icons";
import csvDownload from "json-to-csv-export";
const HolidaysCalendar = (props: IHolidaysCalendarProps) => {
const [service] = React.useState<HolidaysCalendarService>(new HolidaysCalendarService(props.spService, props.graphService));
const HolidaysCalendar = (props: IHolidaysCalendarProps): JSX.Element => {
const [service] = React.useState<HolidaysCalendarService>(new HolidaysCalendarService(props.spService, props.graphService));
const [state, setState] = React.useState<IHolidaysCalendarState>({
listItems: [],
holidayListItems: [],
message: {
show: false,
intent: "success",
},
employeeInfo: null,
columns: [],
});
const [state, setState] = React.useState<IHolidaysCalendarState>({
listItems: [],
holidayListItems: [],
message: {
show: false,
intent: "success",
},
employeeInfo: null,
columns: [],
});
const handleCalenderAddClick = async (itemId: number) => {
try {
const selectedItem = state.holidayListItems.filter((item) => item.Id === itemId);
await service.addLeaveInCalendar(state.employeeInfo, selectedItem[0]);
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, message: { show: true, intent: "success" } }));
} catch (ex) {
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, message: { show: true, intent: "error" } }));
}
};
const handleCalenderAddClick = async (itemId: number): Promise<void> => {
try {
const selectedItem = state.holidayListItems.filter((item) => item.Id === itemId);
await service.addLeaveInCalendar(state.employeeInfo, selectedItem[0]);
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, message: { show: true, intent: "success" } }));
} catch (ex) {
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, message: { show: true, intent: "error" } }));
}
};
const handleDismissClick = () => {
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, message: { show: false, intent: "success" } }));
};
const handleDismissClick = (): void => {
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, message: { show: false, intent: "success" } }));
};
const handleDownload = () => {
const itemsToDownload = service.getItemsToDownloadAsCSV(state.holidayListItems);
csvDownload(itemsToDownload);
};
const handleDownload = (): void => {
const itemsToDownload = service.getItemsToDownloadAsCSV(state.holidayListItems);
csvDownload(itemsToDownload);
};
/* eslint-disable */
React.useEffect(() => {
(async () => {
const employeeInfo = await service.getEmployeeInfo();
/* eslint-disable */
React.useEffect(() => {
(async () => {
const employeeInfo = await service.getEmployeeInfo();
const listItems = await service.getHolidaysByLocation(employeeInfo.officeLocation);
const holidayItems = service.getHolidayItemsToRender(listItems);
const listItems = await service.getHolidaysByLocation(employeeInfo.officeLocation);
const holidayItems = service.getHolidayItemsToRender(listItems);
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, listItems: listItems, holidayListItems: holidayItems, employeeInfo: employeeInfo }));
})();
}, []);
return (
<>
<FluentProvider theme={webLightTheme}>
{state.message.show && (
<Alert intent={state.message.intent} action={{ icon: <DismissCircleRegular aria-label="dismiss message" onClick={handleDismissClick} /> }}>
{state.message.intent === "success" ? "Holiday added in calendar" : "Some error occurred"}
</Alert>
)}
{state.holidayListItems.length > 0 && (
<HolidaysList
items={state.holidayListItems}
onCalendarAddClick={handleCalenderAddClick}
onDownloadItems={handleDownload}
showDownload={props.showDownload}
title={props.title}
showFixedOptional={props.showFixedOptional}
/>
)}
</FluentProvider>
</>
);
setState((prevState: IHolidaysCalendarState) => ({ ...prevState, listItems: listItems, holidayListItems: holidayItems, employeeInfo: employeeInfo }));
})();
}, []);
return (
<>
<FluentProvider theme={webLightTheme}>
{state.message.show && (
<Alert intent={state.message.intent} action={{ icon: <DismissCircleRegular aria-label="dismiss message" onClick={handleDismissClick} /> }}>
{state.message.intent === "success" ? "Holiday added in calendar" : "Some error occurred"}
</Alert>
)}
{state.holidayListItems.length > 0 && (
<HolidaysList
items={state.holidayListItems}
onCalendarAddClick={handleCalenderAddClick}
onDownloadItems={handleDownload}
showDownload={props.showDownload}
title={props.title}
showFixedOptional={props.showFixedOptional}
/>
)}
</FluentProvider>
</>
);
};
export default HolidaysCalendar;

View File

@ -1,130 +1,129 @@
import * as React from "react";
import { IHolidaysListProps } from "../../../../common/interfaces/HolidaysCalendar";
import {
ColumnDefinition,
createColumn,
DataGrid,
DataGridBody,
DataGridCell,
DataGridHeader,
DataGridHeaderCell,
DataGridRow,
RowState,
TableCell,
TableCellActions,
TableCellLayout,
} from "@fluentui/react-components/unstable";
DataGrid,
DataGridBody,
DataGridCell,
DataGridHeader,
DataGridHeaderCell,
DataGridRow,
TableCell,
TableCellActions,
TableCellLayout,
Button,
Label,
makeStyles,
Title2,
TableColumnDefinition,
createTableColumn,
} from "@fluentui/react-components";
import { IHolidayItem } from "../../../../common/interfaces/HolidaysCalendar";
import { Button, Label, makeStyles, Title2 } from "@fluentui/react-components";
import { CalendarAdd20Regular, Diamond24Regular, ArrowDownload24Regular } from "@fluentui/react-icons";
const useStyles = makeStyles({
root: {
marginTop: "25px",
},
root: {
marginTop: "25px",
},
});
export default function HolidaysList(props: IHolidaysListProps) {
const classes = useStyles();
const [showOptionalIcon, setShowOptionalIcon] = React.useState<boolean>(false);
export default function HolidaysList(props: IHolidaysListProps): JSX.Element {
const classes = useStyles();
const [showOptionalIcon, setShowOptionalIcon] = React.useState<boolean>(false);
React.useEffect(() => {
if (props.items.length > 0) {
props.items.filter((item) => item.holidayType.optional).length > 0;
setShowOptionalIcon(true);
}
}, [props.items]);
const columns: ColumnDefinition<IHolidayItem>[] = React.useMemo(
() => [
createColumn<IHolidayItem>({
columnId: "HolidayTitle",
renderHeaderCell: () => {
return "";
},
renderCell: (item) => {
return (
<TableCell>
<TableCellLayout>{item.holidayTitle.label}</TableCellLayout>
<TableCellActions>
<Button icon={<CalendarAdd20Regular />} appearance="subtle" onClick={() => props.onCalendarAddClick(item.Id)} />
</TableCellActions>
</TableCell>
);
},
}),
createColumn<IHolidayItem>({
columnId: "HolidayDate",
renderHeaderCell: () => {
return "";
},
renderCell: (item) => {
return <TableCellLayout>{item.holidayDate.label}</TableCellLayout>;
},
}),
createColumn<IHolidayItem>({
columnId: "HolidayDay",
renderHeaderCell: () => {
return "";
},
renderCell: (item) => {
return <TableCellLayout>{item.holidayDay.label}</TableCellLayout>;
},
}),
createColumn<IHolidayItem>({
columnId: "HolidayType",
renderHeaderCell: () => {
return "";
},
renderCell: (item) => {
return (
<TableCellLayout>
{props.showFixedOptional ? (
item.holidayType.optional ? (
<Diamond24Regular primaryFill="Orange" />
) : (
<Diamond24Regular primaryFill="Green" />
)
) : null}
</TableCellLayout>
);
},
}),
],
[props.items, props.showFixedOptional]
);
React.useEffect(() => {
if (props.items.length > 0) {
if (props.items.filter((item) => item.holidayType.optional).length > 0) setShowOptionalIcon(true);
}
}, [props.items]);
const columns: TableColumnDefinition<IHolidayItem>[] = React.useMemo(
() => [
createTableColumn<IHolidayItem>({
columnId: "HolidayTitle",
renderHeaderCell: () => {
return "";
},
renderCell: (item: IHolidayItem) => {
return (
<TableCell>
<TableCellLayout>{item.holidayTitle.label}</TableCellLayout>
<TableCellActions>
<Button icon={<CalendarAdd20Regular />} appearance="subtle" onClick={() => props.onCalendarAddClick(item.Id)} />
</TableCellActions>
</TableCell>
);
},
}),
createTableColumn<IHolidayItem>({
columnId: "HolidayDate",
renderHeaderCell: () => {
return "";
},
renderCell: (item: IHolidayItem) => {
return <TableCellLayout>{item.holidayDate.label}</TableCellLayout>;
},
}),
createTableColumn<IHolidayItem>({
columnId: "HolidayDay",
renderHeaderCell: () => {
return "";
},
renderCell: (item: IHolidayItem) => {
return <TableCellLayout>{item.holidayDay.label}</TableCellLayout>;
},
}),
createTableColumn<IHolidayItem>({
columnId: "HolidayType",
renderHeaderCell: () => {
return "";
},
renderCell: (item: IHolidayItem) => {
return (
<TableCellLayout>
{props.showFixedOptional ? (
item.holidayType.optional ? (
<Diamond24Regular primaryFill="Orange" />
) : (
<Diamond24Regular primaryFill="Green" />
)
) : null}
</TableCellLayout>
);
},
}),
],
[props.items, props.showFixedOptional]
);
return (
<>
<Title2 align="center" style={{ fontFamily: "fontFamilyBase" }}>
{props.title}
</Title2>
<DataGrid items={props.items} columns={columns} sortable={false} getRowId={(item: IHolidayItem) => item.Id}>
<DataGridHeader>
<DataGridRow>{({ renderHeaderCell }: ColumnDefinition<IHolidayItem>) => <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>}</DataGridRow>
</DataGridHeader>
<DataGridBody>
{({ item, rowId }: RowState<IHolidayItem>) => (
<DataGridRow key={rowId}>{({ renderCell }: ColumnDefinition<IHolidayItem>) => <DataGridCell>{renderCell(item)}</DataGridCell>}</DataGridRow>
)}
</DataGridBody>
</DataGrid>
return (
<>
<Title2 align="center" style={{ fontFamily: "fontFamilyBase" }}>
{props.title}
</Title2>
<DataGrid items={props.items} columns={columns} sortable={false} getRowId={(item: IHolidayItem) => item.Id}>
<DataGridHeader>
<DataGridRow>{({ renderHeaderCell }) => <DataGridHeaderCell>{renderHeaderCell()}</DataGridHeaderCell>}</DataGridRow>
</DataGridHeader>
<DataGridBody>
{({ item, rowId }) => <DataGridRow key={rowId}>{({ renderCell }) => <DataGridCell>{renderCell(item)}</DataGridCell>}</DataGridRow>}
</DataGridBody>
</DataGrid>
{props.showDownload && (
<Button icon={<ArrowDownload24Regular />} appearance="subtle" onClick={props.onDownloadItems} className={classes.root}>
Download
</Button>
)}
{props.showFixedOptional && (
<div className={classes.root}>
<Diamond24Regular primaryFill="Green" />
<Label>Fixed Holiday</Label>
{showOptionalIcon && (
<>
<Diamond24Regular primaryFill="Orange" />
<Label>Optional/Flexible holiday</Label>
</>
)}
</div>
)}
</>
);
{props.showDownload && (
<Button icon={<ArrowDownload24Regular />} appearance="subtle" onClick={props.onDownloadItems} className={classes.root}>
Download
</Button>
)}
{props.showFixedOptional && (
<div className={classes.root}>
<Diamond24Regular primaryFill="Green" />
<Label>Fixed Holiday</Label>
{showOptionalIcon && (
<>
<Diamond24Regular primaryFill="Orange" />
<Label>Optional/Flexible holiday</Label>
</>
)}
</div>
)}
</>
);
}

View File

@ -1,20 +1,20 @@
import { ColumnDefinition } from "@fluentui/react-components/unstable";
import { TableColumnDefinition } from "@fluentui/react-components";
import { IHolidayItem, IHoliday } from "../../../common/interfaces/HolidaysCalendar";
export interface IEmployeeInfo {
eMail: string;
id: string;
officeLocation: string;
timezone: string;
displayName: string;
eMail: string;
id: string;
officeLocation: string;
timezone: string;
displayName: string;
}
export interface IHolidaysCalendarState {
listItems: IHoliday[];
holidayListItems: IHolidayItem[];
message: {
show: boolean;
intent: "success" | "error";
};
employeeInfo: IEmployeeInfo;
columns: ColumnDefinition<IHolidayItem>[];
listItems: IHoliday[];
holidayListItems: IHolidayItem[];
message: {
show: boolean;
intent: "success" | "error";
};
employeeInfo: IEmployeeInfo;
columns: TableColumnDefinition<IHolidayItem>[];
}

View File

@ -1,5 +1,5 @@
{
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.5/includes/tsconfig-web.json",
"extends": "./node_modules/@microsoft/rush-stack-compiler-4.7/includes/tsconfig-web.json",
"compilerOptions": {
"target": "es5",
"forceConsistentCasingInFileNames": true,

File diff suppressed because it is too large Load Diff