@axis-backstage/plugin-vacation-calendar 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +110 -0
- package/dist/api/VacationCalendarApi.esm.js +8 -0
- package/dist/api/VacationCalendarApi.esm.js.map +1 -0
- package/dist/api/VacationCalendarClient.esm.js +101 -0
- package/dist/api/VacationCalendarClient.esm.js.map +1 -0
- package/dist/components/CalendarCard/CalendarCard.esm.js +203 -0
- package/dist/components/CalendarCard/CalendarCard.esm.js.map +1 -0
- package/dist/components/CalendarCard/fetch.esm.js +33 -0
- package/dist/components/CalendarCard/fetch.esm.js.map +1 -0
- package/dist/components/CalendarCard/lib.esm.js +46 -0
- package/dist/components/CalendarCard/lib.esm.js.map +1 -0
- package/dist/components/DateSelector/DateSelector.esm.js +25 -0
- package/dist/components/DateSelector/DateSelector.esm.js.map +1 -0
- package/dist/components/SignInContent/SignInContent.esm.js +33 -0
- package/dist/components/SignInContent/SignInContent.esm.js.map +1 -0
- package/dist/components/VacationCalendar.esm.js +11 -0
- package/dist/components/VacationCalendar.esm.js.map +1 -0
- package/dist/components/index.esm.js +4 -0
- package/dist/components/index.esm.js.map +1 -0
- package/dist/hooks/useAvailibility.esm.js +63 -0
- package/dist/hooks/useAvailibility.esm.js.map +1 -0
- package/dist/hooks/useSignIn.esm.js +23 -0
- package/dist/hooks/useSignIn.esm.js.map +1 -0
- package/dist/index.d.ts +84 -0
- package/dist/index.esm.js +4 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/plugin.esm.js +32 -0
- package/dist/plugin.esm.js.map +1 -0
- package/dist/routes.esm.js +8 -0
- package/dist/routes.esm.js.map +1 -0
- package/package.json +71 -0
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Vacation Calendar plugin
|
|
2
|
+
|
|
3
|
+
Welcome to the Vacation Calendar plugin!
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+

|
|
7
|
+
|
|
8
|
+
## Introduction
|
|
9
|
+
|
|
10
|
+
The Vacation Calendar plugin allows you to get a comprehensive overview of your colleagues' vacations and out-of-office events. Clearly see when your vacations overlap or which team members will be present on specific occasions, making your life and teamwork easier and more efficient.
|
|
11
|
+
|
|
12
|
+
Our plugin is based on [Microsoft-Calendar Plugin](https://github.com/backstage/community-plugins/tree/main/workspaces/microsoft-calendar/plugins/microsoft-calendar) with modifications for focusing on multiple users' calendars. Full credit to them for their great plugin!
|
|
13
|
+
|
|
14
|
+
## How it works
|
|
15
|
+
|
|
16
|
+
The plugin interacts with the Microsoft Graph API to fetch users' calendars and schedule items. Mark your events as Out of Office in either the Outlook client or the Teams client. By marking your events as "Out of Office," they will be correctly reflected in the Vacation Calendar plugin, providing an accurate overview of your availability.
|
|
17
|
+
|
|
18
|
+
## Autentication
|
|
19
|
+
|
|
20
|
+
The Vacation Calendar plugin requires Microsoft authentication. If you have not set this up, please follow the upstream guide on Microsoft authentication: [Backstage.io Microsoft Authentication Guide](https://backstage.io/docs/auth/microsoft/provider/).
|
|
21
|
+
|
|
22
|
+
At present, the plugin supports only Microsoft authentication and does not integrate with other Backstage authentication methods. If you need the plugin to support a different authentication method, please create an issue so we can discuss your requirements.
|
|
23
|
+
|
|
24
|
+
## Getting started
|
|
25
|
+
|
|
26
|
+
1. First, install the plugin into your app:
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
# From your Backstage root directory
|
|
30
|
+
yarn --cwd packages/app add @axis-backstage/plugin-vacation-calendar
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
2. Setup the API-factory.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
// packages/app/src/apis.ts:
|
|
37
|
+
|
|
38
|
+
createApiFactory({
|
|
39
|
+
api: vacationCalendarApiRef,
|
|
40
|
+
deps: {
|
|
41
|
+
authApi: microsoftAuthApiRef,
|
|
42
|
+
fetchApi: fetchApiRef,
|
|
43
|
+
},
|
|
44
|
+
factory: ({ authApi, fetchApi }) =>
|
|
45
|
+
new VacationCalendarApiClient({ authApi, fetchApi }),
|
|
46
|
+
}),
|
|
47
|
+
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
3. Modify your entity page to include the `VacationCalendarPage` component:
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
// In packages/app/src/components/catalog/EntityPage.tsx
|
|
54
|
+
import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar';
|
|
55
|
+
|
|
56
|
+
const groupPage = (
|
|
57
|
+
<EntityLayout>
|
|
58
|
+
<EntityLayout.Route path="/vacation-calendar" title="Out Of Office">
|
|
59
|
+
<Grid md={6}>
|
|
60
|
+
<VacationCalendarPage />
|
|
61
|
+
</Grid>
|
|
62
|
+
</EntityLayout.Route>
|
|
63
|
+
</EntityLayout>
|
|
64
|
+
);
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
By doing this, all Backstage users who are part of that group will be displayed in the `Out of Office` tab on the group's entity page.
|
|
68
|
+
|
|
69
|
+
## Integration with the Catalog
|
|
70
|
+
|
|
71
|
+
To fetch all colleagues with the same manager for a user entity, add the manager annotation to the entity's **catalog-info.yaml** file:
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
apiVersion: backstage.io/v1alpha1
|
|
75
|
+
kind: User
|
|
76
|
+
metadata:
|
|
77
|
+
# ...
|
|
78
|
+
annotations:
|
|
79
|
+
manager: value # The Backstage username of the mananger
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
If the manager annotation is set, you can display all users with the same manager for a user entity. To do this, add the `VacationCalendarPage` component to the entity page for users:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
// In packages/app/src/components/catalog/EntityPage.tsx
|
|
86
|
+
import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar';
|
|
87
|
+
|
|
88
|
+
const userPage = (
|
|
89
|
+
<EntityLayout>
|
|
90
|
+
<EntityLayout.Route path="/vacation-calendar" title="Out Of Office">
|
|
91
|
+
<Grid md={6}>
|
|
92
|
+
<VacationCalendarPage />
|
|
93
|
+
</Grid>
|
|
94
|
+
</EntityLayout.Route>
|
|
95
|
+
</EntityLayout>
|
|
96
|
+
);
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## Development
|
|
100
|
+
|
|
101
|
+
The plugin has been added to the example app in this repository, meaning you'll be able to access it by running `yarn start` in the root directory, and then navigating to [/vacation-calendar](http://localhost:3000/vacation-calendar).
|
|
102
|
+
|
|
103
|
+
You can also serve the plugin in isolation by running `yarn start` in the plugin directory.
|
|
104
|
+
This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads.
|
|
105
|
+
It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory.
|
|
106
|
+
|
|
107
|
+
## Screenshots
|
|
108
|
+
|
|
109
|
+

|
|
110
|
+

|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VacationCalendarApi.esm.js","sources":["../../src/api/VacationCalendarApi.ts"],"sourcesContent":["import { createApiRef } from '@backstage/core-plugin-api';\nimport type { MicrosoftCalendar, ScheduleInformation } from './types';\n\n/**\n * The apiref for the VacationCalendar plugin.\n *\n * @public\n */\nexport const vacationCalendarApiRef = createApiRef<VacationCalendarApi>({\n id: 'plugin.vacation-calendar.service',\n});\n\n/**\n * The definition for the VacationCalendar api.\n *\n * @public\n */\nexport interface VacationCalendarApi {\n /**\n * Fetches schedule items for users\n */\n getCalendars(): Promise<MicrosoftCalendar[]>;\n /**\n * Fetches Microsoft calendars\n */\n getAvailability(\n params: {\n users: string[];\n startDateTime: string;\n endDateTime: string;\n },\n headers: {\n [x: string]: any;\n },\n ): Promise<ScheduleInformation[]>;\n}\n"],"names":[],"mappings":";;AAQO,MAAM,yBAAyB,YAAkC,CAAA;AAAA,EACtE,EAAI,EAAA,kCAAA;AACN,CAAC;;;;"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { ResponseError } from '@backstage/errors';
|
|
2
|
+
|
|
3
|
+
const getAvailabilityBody = ({
|
|
4
|
+
users,
|
|
5
|
+
startDateTime,
|
|
6
|
+
endDateTime
|
|
7
|
+
}) => ({
|
|
8
|
+
Schedules: users,
|
|
9
|
+
StartTime: {
|
|
10
|
+
dateTime: startDateTime,
|
|
11
|
+
timeZone: "Europe/Paris"
|
|
12
|
+
},
|
|
13
|
+
EndTime: {
|
|
14
|
+
dateTime: endDateTime,
|
|
15
|
+
timeZone: "Europe/Paris"
|
|
16
|
+
},
|
|
17
|
+
availabilityViewInterval: "1440"
|
|
18
|
+
});
|
|
19
|
+
class VacationCalendarApiClient {
|
|
20
|
+
authApi;
|
|
21
|
+
fetchApi;
|
|
22
|
+
constructor(options) {
|
|
23
|
+
this.authApi = options.authApi;
|
|
24
|
+
this.fetchApi = options.fetchApi;
|
|
25
|
+
}
|
|
26
|
+
async get(path, params = {}, headers) {
|
|
27
|
+
const query = new URLSearchParams(params);
|
|
28
|
+
const url = new URL(
|
|
29
|
+
`${path}?${query.toString()}`,
|
|
30
|
+
"https://graph.microsoft.com"
|
|
31
|
+
);
|
|
32
|
+
const token = await this.authApi.getAccessToken();
|
|
33
|
+
let temp = {};
|
|
34
|
+
if (headers && typeof headers === "object") {
|
|
35
|
+
temp = {
|
|
36
|
+
...headers
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (token) {
|
|
40
|
+
temp.Authorization = `Bearer ${token}`;
|
|
41
|
+
}
|
|
42
|
+
const response = await this.fetchApi.fetch(url.toString(), {
|
|
43
|
+
headers: temp
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
throw await ResponseError.fromResponse(response);
|
|
47
|
+
}
|
|
48
|
+
return response.json();
|
|
49
|
+
}
|
|
50
|
+
async post(path, body, headers) {
|
|
51
|
+
const url = new URL(path, "https://graph.microsoft.com");
|
|
52
|
+
const token = await this.authApi.getAccessToken();
|
|
53
|
+
let temp = {};
|
|
54
|
+
if (headers && typeof headers === "object") {
|
|
55
|
+
temp = {
|
|
56
|
+
...headers
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
if (token) {
|
|
60
|
+
temp.Authorization = `Bearer ${token}`;
|
|
61
|
+
}
|
|
62
|
+
temp["Content-type"] = "application/json";
|
|
63
|
+
const response = await this.fetchApi.fetch(url.toString(), {
|
|
64
|
+
method: "POST",
|
|
65
|
+
headers: temp,
|
|
66
|
+
body: JSON.stringify(body)
|
|
67
|
+
});
|
|
68
|
+
if (!response.ok) {
|
|
69
|
+
throw await ResponseError.fromResponse(response);
|
|
70
|
+
}
|
|
71
|
+
return response.json();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Fetches Microsoft calendars
|
|
75
|
+
*
|
|
76
|
+
* @returns the MicrosoftValendar objects
|
|
77
|
+
*/
|
|
78
|
+
async getCalendars() {
|
|
79
|
+
const data = await this.get("v1.0/me/calendars");
|
|
80
|
+
return data.value;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Fetches schedule items for users
|
|
84
|
+
*
|
|
85
|
+
* @param users - list of users
|
|
86
|
+
* @param startDateTime - string with start date
|
|
87
|
+
* @param endDateTime - string with end date
|
|
88
|
+
* @returns Microsoft ScheduleInformation items
|
|
89
|
+
*/
|
|
90
|
+
async getAvailability(params, headers) {
|
|
91
|
+
const data = await this.post(
|
|
92
|
+
`v1.0/me/calendar/getschedule`,
|
|
93
|
+
getAvailabilityBody({ ...params }),
|
|
94
|
+
headers
|
|
95
|
+
);
|
|
96
|
+
return data.value;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export { VacationCalendarApiClient };
|
|
101
|
+
//# sourceMappingURL=VacationCalendarClient.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VacationCalendarClient.esm.js","sources":["../../src/api/VacationCalendarClient.ts"],"sourcesContent":["import { OAuthApi, FetchApi } from '@backstage/core-plugin-api';\nimport { ResponseError } from '@backstage/errors';\nimport type { ScheduleInformation, MicrosoftCalendar } from './types';\n\nconst getAvailabilityBody = ({\n users,\n startDateTime,\n endDateTime,\n}: {\n users: string[];\n startDateTime: string;\n endDateTime: string;\n}) => ({\n Schedules: users,\n StartTime: {\n dateTime: startDateTime,\n timeZone: 'Europe/Paris',\n },\n EndTime: {\n dateTime: endDateTime,\n timeZone: 'Europe/Paris',\n },\n availabilityViewInterval: '1440',\n});\n\n/**\n * The client implementation for the frontend api.\n *\n * @public\n */\nexport class VacationCalendarApiClient {\n private readonly authApi: OAuthApi;\n private readonly fetchApi: FetchApi;\n\n constructor(options: { authApi: OAuthApi; fetchApi: FetchApi }) {\n this.authApi = options.authApi;\n this.fetchApi = options.fetchApi;\n }\n\n private async get<T>(\n path: string,\n params: { [key in string]: any } = {},\n headers?: any,\n ): Promise<T> {\n const query = new URLSearchParams(params);\n const url = new URL(\n `${path}?${query.toString()}`,\n 'https://graph.microsoft.com',\n );\n const token = await this.authApi.getAccessToken();\n let temp: any = {};\n\n if (headers && typeof headers === 'object') {\n temp = {\n ...headers,\n };\n }\n\n if (token) {\n temp.Authorization = `Bearer ${token}`;\n }\n\n const response = await this.fetchApi.fetch(url.toString(), {\n headers: temp,\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n\n private async post<T>(path: string, body: any, headers?: any): Promise<T> {\n const url = new URL(path, 'https://graph.microsoft.com');\n const token = await this.authApi.getAccessToken();\n let temp: any = {};\n\n if (headers && typeof headers === 'object') {\n temp = {\n ...headers,\n };\n }\n\n if (token) {\n temp.Authorization = `Bearer ${token}`;\n }\n temp['Content-type'] = 'application/json';\n\n const response = await this.fetchApi.fetch(url.toString(), {\n method: 'POST',\n headers: temp,\n body: JSON.stringify(body),\n });\n\n if (!response.ok) {\n throw await ResponseError.fromResponse(response);\n }\n\n return response.json() as Promise<T>;\n }\n\n /**\n * Fetches Microsoft calendars\n *\n * @returns the MicrosoftValendar objects\n */\n async getCalendars(): Promise<MicrosoftCalendar[]> {\n const data = await this.get<{\n id: string;\n value: MicrosoftCalendar[];\n }>('v1.0/me/calendars');\n return data.value;\n }\n\n /**\n * Fetches schedule items for users\n *\n * @param users - list of users\n * @param startDateTime - string with start date\n * @param endDateTime - string with end date\n * @returns Microsoft ScheduleInformation items\n */\n async getAvailability(\n params: {\n users: string[];\n startDateTime: string;\n endDateTime: string;\n },\n headers: { [key in string]: any },\n ): Promise<ScheduleInformation[]> {\n const data = await this.post<{\n id: string;\n value: ScheduleInformation[];\n }>(\n `v1.0/me/calendar/getschedule`,\n getAvailabilityBody({ ...params }),\n headers,\n );\n return data.value;\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import React, { useState } from 'react';
|
|
2
|
+
import useAsync from 'react-use/lib/useAsync';
|
|
3
|
+
import Timeline, { TimelineHeaders, SidebarHeader, DateHeader } from 'react-calendar-timeline';
|
|
4
|
+
import { DateTime } from 'luxon';
|
|
5
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
6
|
+
import { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react';
|
|
7
|
+
import { ErrorPanel, Content, ContentHeader, SupportButton, Progress, Link, Avatar } from '@backstage/core-components';
|
|
8
|
+
import { DateSelector } from '../DateSelector/DateSelector.esm.js';
|
|
9
|
+
import { getGroups, getScheduleItems } from './lib.esm.js';
|
|
10
|
+
import { fetchUserEntities, fetchGroupEntities } from './fetch.esm.js';
|
|
11
|
+
import { useAvailability } from '../../hooks/useAvailibility.esm.js';
|
|
12
|
+
import { useSignIn } from '../../hooks/useSignIn.esm.js';
|
|
13
|
+
import { SignInContent } from '../SignInContent/SignInContent.esm.js';
|
|
14
|
+
import MuiLink from '@mui/material/Link';
|
|
15
|
+
import Button from '@mui/material/Button';
|
|
16
|
+
import Box from '@mui/material/Box';
|
|
17
|
+
import Typography from '@mui/material/Typography';
|
|
18
|
+
import Stack from '@mui/material/Stack';
|
|
19
|
+
|
|
20
|
+
const DEFAULT_NUM_DAYS = 60;
|
|
21
|
+
const IntervalDateHeader = (props) => {
|
|
22
|
+
return /* @__PURE__ */ React.createElement(
|
|
23
|
+
MuiLink,
|
|
24
|
+
{
|
|
25
|
+
sx: {
|
|
26
|
+
display: "flex",
|
|
27
|
+
alignItems: "center",
|
|
28
|
+
justifyContent: "center",
|
|
29
|
+
height: "100%",
|
|
30
|
+
borderBottom: "1px solid #bbb",
|
|
31
|
+
cursor: "pointer",
|
|
32
|
+
fontSize: "14px",
|
|
33
|
+
borderLeft: "2px solid #bbb",
|
|
34
|
+
color: (theme) => theme.palette.text.primary,
|
|
35
|
+
position: "absolute",
|
|
36
|
+
width: props?.intervalContext.interval.labelWidth,
|
|
37
|
+
left: props?.intervalContext.interval.left
|
|
38
|
+
},
|
|
39
|
+
onClick: props?.getIntervalProps().onClick
|
|
40
|
+
},
|
|
41
|
+
props?.intervalContext.intervalText
|
|
42
|
+
);
|
|
43
|
+
};
|
|
44
|
+
const CalendarCard = () => {
|
|
45
|
+
const { entity } = useEntity();
|
|
46
|
+
const catalogApi = useApi(catalogApiRef);
|
|
47
|
+
const [startDate, setStartDate] = useState(DateTime.now());
|
|
48
|
+
const [endDate, setEndDate] = useState(
|
|
49
|
+
DateTime.now().endOf("day").plus({ days: DEFAULT_NUM_DAYS })
|
|
50
|
+
);
|
|
51
|
+
const { isSignedIn, isInitialized, signIn } = useSignIn();
|
|
52
|
+
useAsync(async () => signIn(true), [signIn]);
|
|
53
|
+
const isUserEntity = entity.kind.toLowerCase() === "user" && entity.metadata.name;
|
|
54
|
+
const { value: users } = useAsync(async () => {
|
|
55
|
+
return isUserEntity ? fetchUserEntities(catalogApi, entity) : fetchGroupEntities(catalogApi, entity);
|
|
56
|
+
}, [catalogApi, entity]);
|
|
57
|
+
const {
|
|
58
|
+
availablity,
|
|
59
|
+
error,
|
|
60
|
+
isLoading: isAvailabilityLoading,
|
|
61
|
+
isFetching: isAvailabilityFetching,
|
|
62
|
+
hasNextPage,
|
|
63
|
+
fetchNextPage
|
|
64
|
+
} = useAvailability(users, startDate, endDate, isSignedIn);
|
|
65
|
+
const showLoader = isAvailabilityLoading || isAvailabilityFetching || !isInitialized;
|
|
66
|
+
const groups = getGroups(availablity, isUserEntity, users);
|
|
67
|
+
const scheduleItems = getScheduleItems(availablity);
|
|
68
|
+
if (users?.length === 0) {
|
|
69
|
+
return /* @__PURE__ */ React.createElement(
|
|
70
|
+
ErrorPanel,
|
|
71
|
+
{
|
|
72
|
+
title: "No users found.",
|
|
73
|
+
defaultExpanded: true,
|
|
74
|
+
error: Error("No users found for current entity.")
|
|
75
|
+
}
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
if (error instanceof Error) {
|
|
79
|
+
return /* @__PURE__ */ React.createElement(
|
|
80
|
+
ErrorPanel,
|
|
81
|
+
{
|
|
82
|
+
title: "Microsoft Graph Error.",
|
|
83
|
+
defaultExpanded: true,
|
|
84
|
+
error
|
|
85
|
+
}
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
return /* @__PURE__ */ React.createElement(Content, null, /* @__PURE__ */ React.createElement(ContentHeader, { title: "Out of Office Calendar" }, /* @__PURE__ */ React.createElement(SupportButton, { title: "Backstage Out Of Office Calendar" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, "How it works"), /* @__PURE__ */ React.createElement(Typography, null, 'The "Out of Office"-calendar shows Away events. If you want your calendar events to be seen in the "Out of Office"-calendar, be sure to mark your presence as "Away" in outlook.'), /* @__PURE__ */ React.createElement(Typography, { variant: "h6" }, "Limitations"), /* @__PURE__ */ React.createElement(Typography, null, "Due to limitations in the Microsoft Graph API the maximum range of the dates is ", DEFAULT_NUM_DAYS, " days.")))), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(Stack, { direction: "row", gap: 3, alignItems: "center" }, /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
|
|
89
|
+
DateSelector,
|
|
90
|
+
{
|
|
91
|
+
onDateChange: (d) => {
|
|
92
|
+
if (d instanceof DateTime) {
|
|
93
|
+
const { days } = endDate.diff(d, "days").toObject();
|
|
94
|
+
if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {
|
|
95
|
+
setEndDate(d.endOf("day").plus({ days: DEFAULT_NUM_DAYS }));
|
|
96
|
+
}
|
|
97
|
+
setStartDate(d);
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
initalDate: startDate,
|
|
101
|
+
label: "Start Date"
|
|
102
|
+
}
|
|
103
|
+
)), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
|
|
104
|
+
DateSelector,
|
|
105
|
+
{
|
|
106
|
+
onDateChange: (d) => {
|
|
107
|
+
if (d instanceof DateTime) {
|
|
108
|
+
const { days } = startDate.diff(d, "days").toObject();
|
|
109
|
+
if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {
|
|
110
|
+
setStartDate(
|
|
111
|
+
d.endOf("day").minus({ days: DEFAULT_NUM_DAYS })
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
setEndDate(d);
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
initalDate: endDate,
|
|
118
|
+
label: "End Date"
|
|
119
|
+
}
|
|
120
|
+
)), /* @__PURE__ */ React.createElement(Box, null, /* @__PURE__ */ React.createElement(
|
|
121
|
+
Button,
|
|
122
|
+
{
|
|
123
|
+
onClick: () => {
|
|
124
|
+
fetchNextPage();
|
|
125
|
+
},
|
|
126
|
+
disabled: showLoader || !hasNextPage,
|
|
127
|
+
variant: "contained",
|
|
128
|
+
color: "primary"
|
|
129
|
+
},
|
|
130
|
+
"Show more Users"
|
|
131
|
+
))), showLoader && /* @__PURE__ */ React.createElement(Box, { py: 2 }, /* @__PURE__ */ React.createElement(Progress, { variant: "query" })), !isSignedIn && isInitialized && /* @__PURE__ */ React.createElement(Box, { p: 1, pb: 0, minHeight: 200, maxHeight: 602, overflow: "auto" }, /* @__PURE__ */ React.createElement(SignInContent, { handleAuthClick: () => signIn(false) })), !isAvailabilityLoading && isSignedIn && /* @__PURE__ */ React.createElement(Box, { mt: 3, pb: 0, minHeight: 200, overflow: "auto" }, /* @__PURE__ */ React.createElement(
|
|
132
|
+
Timeline,
|
|
133
|
+
{
|
|
134
|
+
groups,
|
|
135
|
+
sidebarWidth: 250,
|
|
136
|
+
groupRenderer: ({ group }) => {
|
|
137
|
+
return /* @__PURE__ */ React.createElement(
|
|
138
|
+
Link,
|
|
139
|
+
{
|
|
140
|
+
to: `/catalog/default/user/${group.entity?.metadata.name}`
|
|
141
|
+
},
|
|
142
|
+
/* @__PURE__ */ React.createElement(
|
|
143
|
+
"div",
|
|
144
|
+
{
|
|
145
|
+
className: "custom-group",
|
|
146
|
+
style: {
|
|
147
|
+
background: group.highlight ? "linear-gradient(90deg, #FFCC33A0, transparent 100%)" : "",
|
|
148
|
+
marginLeft: "-4px",
|
|
149
|
+
paddingLeft: "4px",
|
|
150
|
+
display: "flex",
|
|
151
|
+
whiteSpace: "nowrap",
|
|
152
|
+
flexWrap: "nowrap",
|
|
153
|
+
flexDirection: "row",
|
|
154
|
+
gap: "5px"
|
|
155
|
+
}
|
|
156
|
+
},
|
|
157
|
+
/* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement(
|
|
158
|
+
Avatar,
|
|
159
|
+
{
|
|
160
|
+
displayName: group.entity?.metadata.displayName,
|
|
161
|
+
picture: group.entity?.spec.profile?.picture,
|
|
162
|
+
customStyles: {
|
|
163
|
+
width: 30,
|
|
164
|
+
height: 30
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
)),
|
|
168
|
+
/* @__PURE__ */ React.createElement("div", null, /* @__PURE__ */ React.createElement("span", { className: "title" }, group.title))
|
|
169
|
+
)
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
items: scheduleItems,
|
|
173
|
+
itemTouchSendsClick: false,
|
|
174
|
+
defaultTimeStart: startDate.toJSDate(),
|
|
175
|
+
defaultTimeEnd: endDate.toJSDate()
|
|
176
|
+
},
|
|
177
|
+
/* @__PURE__ */ React.createElement(
|
|
178
|
+
TimelineHeaders,
|
|
179
|
+
{
|
|
180
|
+
style: {
|
|
181
|
+
backgroundColor: "transparent",
|
|
182
|
+
color: "black",
|
|
183
|
+
position: "sticky",
|
|
184
|
+
top: "-6px"
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
/* @__PURE__ */ React.createElement(SidebarHeader, null, ({ getRootProps }) => {
|
|
188
|
+
return /* @__PURE__ */ React.createElement("div", { ...getRootProps() });
|
|
189
|
+
}),
|
|
190
|
+
/* @__PURE__ */ React.createElement(
|
|
191
|
+
DateHeader,
|
|
192
|
+
{
|
|
193
|
+
unit: "primaryHeader",
|
|
194
|
+
intervalRenderer: IntervalDateHeader
|
|
195
|
+
}
|
|
196
|
+
),
|
|
197
|
+
/* @__PURE__ */ React.createElement(DateHeader, { intervalRenderer: IntervalDateHeader })
|
|
198
|
+
)
|
|
199
|
+
))));
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
export { CalendarCard };
|
|
203
|
+
//# sourceMappingURL=CalendarCard.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CalendarCard.esm.js","sources":["../../../src/components/CalendarCard/CalendarCard.tsx"],"sourcesContent":["import React, { ReactNode, useState } from 'react';\nimport useAsync from 'react-use/lib/useAsync';\nimport Timeline, {\n TimelineHeaders,\n SidebarHeader,\n DateHeader,\n IntervalRenderer,\n} from 'react-calendar-timeline';\nimport { DateTime } from 'luxon';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react';\nimport {\n Content,\n ContentHeader,\n SupportButton,\n Link,\n ErrorPanel,\n Progress,\n Avatar,\n} from '@backstage/core-components';\nimport { DateSelector } from '../DateSelector';\nimport { getGroups, getScheduleItems } from './lib';\nimport { fetchGroupEntities, fetchUserEntities } from './fetch';\nimport { useAvailability } from '../../hooks/useAvailibility';\nimport { useSignIn } from '../../hooks';\nimport { SignInContent } from '../SignInContent';\nimport MuiLink from '@mui/material/Link';\nimport Button from '@mui/material/Button';\nimport Box from '@mui/material/Box';\nimport Typography from '@mui/material/Typography';\nimport Stack from '@mui/material/Stack';\n\nimport { Theme } from '@mui/material/styles';\n\nconst DEFAULT_NUM_DAYS = 60;\n\nconst IntervalDateHeader = (props?: IntervalRenderer<object>): ReactNode => {\n return (\n <MuiLink\n sx={{\n display: 'flex',\n alignItems: 'center',\n justifyContent: 'center',\n height: '100%',\n borderBottom: '1px solid #bbb',\n cursor: 'pointer',\n fontSize: '14px',\n borderLeft: '2px solid #bbb',\n color: (theme: Theme) => theme.palette.text.primary,\n position: 'absolute',\n width: props?.intervalContext.interval.labelWidth,\n left: props?.intervalContext.interval.left,\n }}\n onClick={props?.getIntervalProps().onClick}\n >\n {props?.intervalContext.intervalText}\n </MuiLink>\n );\n};\n\nexport const CalendarCard = () => {\n const { entity } = useEntity();\n const catalogApi = useApi(catalogApiRef);\n\n const [startDate, setStartDate] = useState(DateTime.now());\n const [endDate, setEndDate] = useState(\n DateTime.now().endOf('day').plus({ days: DEFAULT_NUM_DAYS }),\n );\n\n const { isSignedIn, isInitialized, signIn } = useSignIn();\n\n useAsync(async () => signIn(true), [signIn]);\n\n const isUserEntity =\n entity.kind.toLowerCase() === 'user' && entity.metadata.name;\n\n const { value: users } = useAsync(async () => {\n return isUserEntity\n ? fetchUserEntities(catalogApi, entity)\n : fetchGroupEntities(catalogApi, entity);\n }, [catalogApi, entity]);\n\n const {\n availablity,\n error,\n isLoading: isAvailabilityLoading,\n isFetching: isAvailabilityFetching,\n hasNextPage,\n fetchNextPage,\n } = useAvailability(users, startDate, endDate, isSignedIn);\n\n const showLoader =\n isAvailabilityLoading || isAvailabilityFetching || !isInitialized;\n\n const groups = getGroups(availablity, isUserEntity, users);\n const scheduleItems = getScheduleItems(availablity);\n\n if (users?.length === 0) {\n return (\n <ErrorPanel\n title=\"No users found.\"\n defaultExpanded\n error={Error('No users found for current entity.')}\n />\n );\n }\n if (error instanceof Error) {\n return (\n <ErrorPanel\n title=\"Microsoft Graph Error.\"\n defaultExpanded\n error={error as any}\n />\n );\n }\n\n return (\n <Content>\n <ContentHeader title=\"Out of Office Calendar\">\n <SupportButton title=\"Backstage Out Of Office Calendar\">\n <Box>\n <Typography variant=\"h6\">How it works</Typography>\n <Typography>\n The \"Out of Office\"-calendar shows Away events. If you want your\n calendar events to be seen in the \"Out of Office\"-calendar, be\n sure to mark your presence as \"Away\" in outlook.\n </Typography>\n <Typography variant=\"h6\">Limitations</Typography>\n <Typography>\n Due to limitations in the Microsoft Graph API the maximum range of\n the dates is {DEFAULT_NUM_DAYS} days.\n </Typography>\n </Box>\n </SupportButton>\n </ContentHeader>\n\n <Box>\n <Stack direction=\"row\" gap={3} alignItems=\"center\">\n <Box>\n <DateSelector\n onDateChange={d => {\n if (d instanceof DateTime) {\n const { days } = endDate.diff(d, 'days').toObject();\n if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {\n setEndDate(d.endOf('day').plus({ days: DEFAULT_NUM_DAYS }));\n }\n setStartDate(d);\n }\n }}\n initalDate={startDate}\n label=\"Start Date\"\n />\n </Box>\n <Box>\n <DateSelector\n onDateChange={d => {\n if (d instanceof DateTime) {\n const { days } = startDate.diff(d, 'days').toObject();\n if (days && Math.abs(days) > DEFAULT_NUM_DAYS) {\n setStartDate(\n d.endOf('day').minus({ days: DEFAULT_NUM_DAYS }),\n );\n }\n setEndDate(d as DateTime);\n }\n }}\n initalDate={endDate}\n label=\"End Date\"\n />\n </Box>\n <Box>\n <Button\n onClick={() => {\n fetchNextPage();\n }}\n disabled={showLoader || !hasNextPage}\n variant=\"contained\"\n color=\"primary\"\n >\n Show more Users\n </Button>\n </Box>\n </Stack>\n\n {showLoader && (\n <Box py={2}>\n <Progress variant=\"query\" />\n </Box>\n )}\n {!isSignedIn && isInitialized && (\n <Box p={1} pb={0} minHeight={200} maxHeight={602} overflow=\"auto\">\n <SignInContent handleAuthClick={() => signIn(false)} />\n </Box>\n )}\n {!isAvailabilityLoading && isSignedIn && (\n <Box mt={3} pb={0} minHeight={200} overflow=\"auto\">\n <Timeline\n groups={groups}\n sidebarWidth={250}\n groupRenderer={({ group }) => {\n return (\n <Link\n to={`/catalog/default/user/${group.entity?.metadata.name}`}\n >\n <div\n className=\"custom-group\"\n style={{\n background: group.highlight\n ? 'linear-gradient(90deg, #FFCC33A0, transparent 100%)'\n : '',\n marginLeft: '-4px',\n paddingLeft: '4px',\n display: 'flex',\n whiteSpace: 'nowrap',\n flexWrap: 'nowrap',\n flexDirection: 'row',\n gap: '5px',\n }}\n >\n <div>\n <Avatar\n displayName={\n group.entity?.metadata.displayName as string\n }\n picture={group.entity?.spec.profile?.picture}\n customStyles={{\n width: 30,\n height: 30,\n }}\n />\n </div>\n <div>\n <span className=\"title\">{group.title}</span>\n </div>\n </div>\n </Link>\n );\n }}\n items={scheduleItems as any}\n itemTouchSendsClick={false}\n defaultTimeStart={startDate.toJSDate()}\n defaultTimeEnd={endDate.toJSDate()}\n >\n <TimelineHeaders\n style={{\n backgroundColor: 'transparent',\n color: 'black',\n position: 'sticky',\n top: '-6px',\n }}\n >\n <SidebarHeader>\n {({ getRootProps }) => {\n return <div {...getRootProps()} />;\n }}\n </SidebarHeader>\n <DateHeader\n unit=\"primaryHeader\"\n intervalRenderer={IntervalDateHeader}\n />\n <DateHeader intervalRenderer={IntervalDateHeader} />\n </TimelineHeaders>\n </Timeline>\n </Box>\n )}\n </Box>\n </Content>\n );\n};\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { stringifyEntityRef, DEFAULT_NAMESPACE } from '@backstage/catalog-model';
|
|
2
|
+
|
|
3
|
+
const MANAGER_ANNOTATION = "manager";
|
|
4
|
+
const fetchUserEntities = async (catalogApi, entity) => {
|
|
5
|
+
if (!entity.metadata?.annotations?.[MANAGER_ANNOTATION]) {
|
|
6
|
+
return [entity];
|
|
7
|
+
}
|
|
8
|
+
const filter = {
|
|
9
|
+
kind: "User",
|
|
10
|
+
[`metadata.annotations.${MANAGER_ANNOTATION}`]: entity.metadata.annotations[MANAGER_ANNOTATION]
|
|
11
|
+
};
|
|
12
|
+
const { items } = await catalogApi.getEntities({ filter });
|
|
13
|
+
return items;
|
|
14
|
+
};
|
|
15
|
+
const fetchGroupEntities = async (catalogApi, entity) => {
|
|
16
|
+
const {
|
|
17
|
+
metadata: { name: groupName, namespace = DEFAULT_NAMESPACE }
|
|
18
|
+
} = entity;
|
|
19
|
+
const filter = {
|
|
20
|
+
kind: "User",
|
|
21
|
+
"relations.memberof": [
|
|
22
|
+
stringifyEntityRef({
|
|
23
|
+
kind: "group",
|
|
24
|
+
namespace: namespace.toLocaleLowerCase("en-US"),
|
|
25
|
+
name: groupName.toLocaleLowerCase("en-US")
|
|
26
|
+
})
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
return (await catalogApi.getEntities({ filter })).items;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { fetchGroupEntities, fetchUserEntities };
|
|
33
|
+
//# sourceMappingURL=fetch.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fetch.esm.js","sources":["../../../src/components/CalendarCard/fetch.ts"],"sourcesContent":["import {\n DEFAULT_NAMESPACE,\n Entity,\n UserEntity,\n stringifyEntityRef,\n} from '@backstage/catalog-model';\nimport { CatalogApi } from '@backstage/plugin-catalog-react';\n\nconst MANAGER_ANNOTATION = 'manager';\n\nexport const fetchUserEntities = async (\n catalogApi: CatalogApi,\n entity: Entity,\n) => {\n if (!entity.metadata?.annotations?.[MANAGER_ANNOTATION]) {\n return [entity as UserEntity];\n }\n\n const filter = {\n kind: 'User',\n [`metadata.annotations.${MANAGER_ANNOTATION}`]:\n entity.metadata.annotations[MANAGER_ANNOTATION],\n };\n\n const { items } = await catalogApi.getEntities({ filter });\n return items as UserEntity[];\n};\n\nexport const fetchGroupEntities = async (\n catalogApi: CatalogApi,\n entity: Entity,\n) => {\n const {\n metadata: { name: groupName, namespace = DEFAULT_NAMESPACE },\n } = entity;\n\n const filter = {\n kind: 'User',\n 'relations.memberof': [\n stringifyEntityRef({\n kind: 'group',\n namespace: namespace.toLocaleLowerCase('en-US'),\n name: groupName.toLocaleLowerCase('en-US'),\n }),\n ],\n };\n\n return (await catalogApi.getEntities({ filter })).items as UserEntity[];\n};\n"],"names":[],"mappings":";;AAQA,MAAM,kBAAqB,GAAA,SAAA,CAAA;AAEd,MAAA,iBAAA,GAAoB,OAC/B,UAAA,EACA,MACG,KAAA;AACH,EAAA,IAAI,CAAC,MAAA,CAAO,QAAU,EAAA,WAAA,GAAc,kBAAkB,CAAG,EAAA;AACvD,IAAA,OAAO,CAAC,MAAoB,CAAA,CAAA;AAAA,GAC9B;AAEA,EAAA,MAAM,MAAS,GAAA;AAAA,IACb,IAAM,EAAA,MAAA;AAAA,IACN,CAAC,wBAAwB,kBAAkB,CAAA,CAAE,GAC3C,MAAO,CAAA,QAAA,CAAS,YAAY,kBAAkB,CAAA;AAAA,GAClD,CAAA;AAEA,EAAM,MAAA,EAAE,OAAU,GAAA,MAAM,WAAW,WAAY,CAAA,EAAE,QAAQ,CAAA,CAAA;AACzD,EAAO,OAAA,KAAA,CAAA;AACT,EAAA;AAEa,MAAA,kBAAA,GAAqB,OAChC,UAAA,EACA,MACG,KAAA;AACH,EAAM,MAAA;AAAA,IACJ,QAAU,EAAA,EAAE,IAAM,EAAA,SAAA,EAAW,YAAY,iBAAkB,EAAA;AAAA,GACzD,GAAA,MAAA,CAAA;AAEJ,EAAA,MAAM,MAAS,GAAA;AAAA,IACb,IAAM,EAAA,MAAA;AAAA,IACN,oBAAsB,EAAA;AAAA,MACpB,kBAAmB,CAAA;AAAA,QACjB,IAAM,EAAA,OAAA;AAAA,QACN,SAAA,EAAW,SAAU,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,QAC9C,IAAA,EAAM,SAAU,CAAA,iBAAA,CAAkB,OAAO,CAAA;AAAA,OAC1C,CAAA;AAAA,KACH;AAAA,GACF,CAAA;AAEA,EAAA,OAAA,CAAQ,MAAM,UAAW,CAAA,WAAA,CAAY,EAAE,MAAA,EAAQ,CAAG,EAAA,KAAA,CAAA;AACpD;;;;"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { head } from 'lodash';
|
|
2
|
+
import { DateTime } from 'luxon';
|
|
3
|
+
|
|
4
|
+
const isTimeLineItem = (item) => {
|
|
5
|
+
return item !== void 0;
|
|
6
|
+
};
|
|
7
|
+
const getFullName = (users, userId) => {
|
|
8
|
+
const name = head(userId.split("@"));
|
|
9
|
+
const user = users.find((u) => u.metadata.name === name);
|
|
10
|
+
if (user)
|
|
11
|
+
return user.spec.profile?.displayName;
|
|
12
|
+
return userId;
|
|
13
|
+
};
|
|
14
|
+
const getUser = (users, userId) => {
|
|
15
|
+
const name = head(userId.split("@"));
|
|
16
|
+
const user = users.find((u) => u.metadata.name === name);
|
|
17
|
+
return user;
|
|
18
|
+
};
|
|
19
|
+
const getScheduleItems = (availablity) => {
|
|
20
|
+
return availablity ? availablity?.pages.flatMap((p) => p).flatMap(
|
|
21
|
+
(a, i) => (
|
|
22
|
+
// @ts-ignore
|
|
23
|
+
a.scheduleItems?.filter((scheduleItem) => scheduleItem.status === "oof").map((s) => ({
|
|
24
|
+
id: Date.now().toString(36) + Math.random().toString(36).slice(2),
|
|
25
|
+
group: i,
|
|
26
|
+
start_time: DateTime.fromISO(s.start?.dateTime),
|
|
27
|
+
end_time: DateTime.fromISO(s.end?.dateTime),
|
|
28
|
+
canMove: false,
|
|
29
|
+
canResize: false
|
|
30
|
+
}))
|
|
31
|
+
)
|
|
32
|
+
).filter(isTimeLineItem) : [];
|
|
33
|
+
};
|
|
34
|
+
const getGroups = (availablity, isUserEntity, users) => {
|
|
35
|
+
return availablity ? availablity.pages.flatMap((p) => p).map((a, i) => ({
|
|
36
|
+
id: i,
|
|
37
|
+
// @ts-ignore
|
|
38
|
+
highlight: isUserEntity && a.scheduleId && isUserEntity === head(a.scheduleId.split("@")),
|
|
39
|
+
title: getFullName(users || [], a.scheduleId || ""),
|
|
40
|
+
entity: getUser(users || [], a.scheduleId || ""),
|
|
41
|
+
height: 30
|
|
42
|
+
})) : [];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export { getGroups, getScheduleItems };
|
|
46
|
+
//# sourceMappingURL=lib.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lib.esm.js","sources":["../../../src/components/CalendarCard/lib.ts"],"sourcesContent":["import { TimelineItemBase } from 'react-calendar-timeline';\nimport { UserEntity, UserEntityV1alpha1 } from '@backstage/catalog-model';\nimport { ScheduleInformation } from '@microsoft/microsoft-graph-types';\nimport { InfiniteData } from '@tanstack/react-query';\nimport { head } from 'lodash';\nimport { DateTime } from 'luxon';\n\nconst isTimeLineItem = (\n item: TimelineItemBase<any> | undefined,\n): item is TimelineItemBase<any> => {\n return item !== undefined;\n};\n\nconst getFullName = (users: UserEntity[], userId: string) => {\n const name = head(userId.split('@'));\n const user = users.find(u => u.metadata.name === name);\n\n if (user) return user.spec.profile?.displayName;\n return userId;\n};\n\nconst getUser = (users: UserEntity[], userId: string) => {\n const name = head(userId.split('@'));\n const user = users.find(u => u.metadata.name === name);\n\n return user;\n};\n\nexport const getScheduleItems = (\n availablity: InfiniteData<ScheduleInformation[]> | undefined,\n) => {\n return availablity\n ? availablity?.pages\n .flatMap(p => p)\n // @ts-ignore\n .flatMap((a, i) =>\n // @ts-ignore\n a.scheduleItems\n // @ts-ignore\n ?.filter(scheduleItem => scheduleItem.status === 'oof')\n // @ts-ignore\n .map(s => ({\n id: Date.now().toString(36) + Math.random().toString(36).slice(2),\n group: i,\n start_time: DateTime.fromISO(s.start?.dateTime!),\n end_time: DateTime.fromISO(s.end?.dateTime!),\n canMove: false,\n canResize: false,\n })),\n )\n .filter(isTimeLineItem)\n : [];\n};\n\nexport const getGroups = (\n availablity: InfiniteData<ScheduleInformation[]> | undefined,\n isUserEntity: string | false,\n users: UserEntityV1alpha1[] | undefined,\n) => {\n return availablity\n ? availablity.pages\n .flatMap(p => p)\n .map((a, i) => ({\n id: i,\n // @ts-ignore\n highlight:\n isUserEntity &&\n a.scheduleId &&\n isUserEntity === head(a.scheduleId.split('@')),\n title: getFullName(users || [], a.scheduleId || ''),\n entity: getUser(users || [], a.scheduleId || ''),\n height: 30,\n }))\n : [];\n};\n"],"names":[],"mappings":";;;AAOA,MAAM,cAAA,GAAiB,CACrB,IACkC,KAAA;AAClC,EAAA,OAAO,IAAS,KAAA,KAAA,CAAA,CAAA;AAClB,CAAA,CAAA;AAEA,MAAM,WAAA,GAAc,CAAC,KAAA,EAAqB,MAAmB,KAAA;AAC3D,EAAA,MAAM,IAAO,GAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,GAAG,CAAC,CAAA,CAAA;AACnC,EAAA,MAAM,OAAO,KAAM,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,QAAA,CAAS,SAAS,IAAI,CAAA,CAAA;AAErD,EAAI,IAAA,IAAA;AAAM,IAAO,OAAA,IAAA,CAAK,KAAK,OAAS,EAAA,WAAA,CAAA;AACpC,EAAO,OAAA,MAAA,CAAA;AACT,CAAA,CAAA;AAEA,MAAM,OAAA,GAAU,CAAC,KAAA,EAAqB,MAAmB,KAAA;AACvD,EAAA,MAAM,IAAO,GAAA,IAAA,CAAK,MAAO,CAAA,KAAA,CAAM,GAAG,CAAC,CAAA,CAAA;AACnC,EAAA,MAAM,OAAO,KAAM,CAAA,IAAA,CAAK,OAAK,CAAE,CAAA,QAAA,CAAS,SAAS,IAAI,CAAA,CAAA;AAErD,EAAO,OAAA,IAAA,CAAA;AACT,CAAA,CAAA;AAEa,MAAA,gBAAA,GAAmB,CAC9B,WACG,KAAA;AACH,EAAA,OAAO,cACH,WAAa,EAAA,KAAA,CACV,OAAQ,CAAA,CAAA,CAAA,KAAK,CAAC,CAEd,CAAA,OAAA;AAAA,IAAQ,CAAC,CAAG,EAAA,CAAA;AAAA;AAAA,MAEX,CAAA,CAAE,eAEE,MAAO,CAAA,CAAA,YAAA,KAAgB,aAAa,MAAW,KAAA,KAAK,CAErD,CAAA,GAAA,CAAI,CAAM,CAAA,MAAA;AAAA,QACT,EAAI,EAAA,IAAA,CAAK,GAAI,EAAA,CAAE,SAAS,EAAE,CAAA,GAAI,IAAK,CAAA,MAAA,EAAS,CAAA,QAAA,CAAS,EAAE,CAAA,CAAE,MAAM,CAAC,CAAA;AAAA,QAChE,KAAO,EAAA,CAAA;AAAA,QACP,UAAY,EAAA,QAAA,CAAS,OAAQ,CAAA,CAAA,CAAE,OAAO,QAAS,CAAA;AAAA,QAC/C,QAAU,EAAA,QAAA,CAAS,OAAQ,CAAA,CAAA,CAAE,KAAK,QAAS,CAAA;AAAA,QAC3C,OAAS,EAAA,KAAA;AAAA,QACT,SAAW,EAAA,KAAA;AAAA,OACX,CAAA,CAAA;AAAA,KAAA;AAAA,GAEL,CAAA,MAAA,CAAO,cAAc,CAAA,GACxB,EAAC,CAAA;AACP,EAAA;AAEO,MAAM,SAAY,GAAA,CACvB,WACA,EAAA,YAAA,EACA,KACG,KAAA;AACH,EAAO,OAAA,WAAA,GACH,WAAY,CAAA,KAAA,CACT,OAAQ,CAAA,CAAA,CAAA,KAAK,CAAC,CACd,CAAA,GAAA,CAAI,CAAC,CAAA,EAAG,CAAO,MAAA;AAAA,IACd,EAAI,EAAA,CAAA;AAAA;AAAA,IAEJ,SAAA,EACE,YACA,IAAA,CAAA,CAAE,UACF,IAAA,YAAA,KAAiB,KAAK,CAAE,CAAA,UAAA,CAAW,KAAM,CAAA,GAAG,CAAC,CAAA;AAAA,IAC/C,OAAO,WAAY,CAAA,KAAA,IAAS,EAAI,EAAA,CAAA,CAAE,cAAc,EAAE,CAAA;AAAA,IAClD,QAAQ,OAAQ,CAAA,KAAA,IAAS,EAAI,EAAA,CAAA,CAAE,cAAc,EAAE,CAAA;AAAA,IAC/C,MAAQ,EAAA,EAAA;AAAA,GACV,CAAE,IACJ,EAAC,CAAA;AACP;;;;"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';
|
|
3
|
+
import FormControl from '@mui/material/FormControl';
|
|
4
|
+
import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';
|
|
5
|
+
import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker';
|
|
6
|
+
|
|
7
|
+
const DateSelector = ({
|
|
8
|
+
label,
|
|
9
|
+
initalDate,
|
|
10
|
+
onDateChange
|
|
11
|
+
}) => {
|
|
12
|
+
return /* @__PURE__ */ React.createElement(FormControl, { variant: "standard" }, /* @__PURE__ */ React.createElement(LocalizationProvider, { dateAdapter: AdapterLuxon }, /* @__PURE__ */ React.createElement(
|
|
13
|
+
DesktopDatePicker,
|
|
14
|
+
{
|
|
15
|
+
format: "yyyy-MM-dd",
|
|
16
|
+
label,
|
|
17
|
+
value: initalDate,
|
|
18
|
+
defaultValue: initalDate,
|
|
19
|
+
onChange: onDateChange
|
|
20
|
+
}
|
|
21
|
+
)));
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export { DateSelector };
|
|
25
|
+
//# sourceMappingURL=DateSelector.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DateSelector.esm.js","sources":["../../../src/components/DateSelector/DateSelector.tsx"],"sourcesContent":["import React from 'react';\nimport { DateTime } from 'luxon/src/datetime';\nimport { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon';\nimport FormControl from '@mui/material/FormControl';\nimport { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider';\nimport { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker';\n\ntype DateSelectorProps = {\n label: string;\n initalDate: DateTime;\n onDateChange: (date: DateTime | null | string | undefined) => void;\n};\n\nexport const DateSelector = ({\n label,\n initalDate,\n onDateChange,\n}: DateSelectorProps) => {\n return (\n <FormControl variant=\"standard\">\n <LocalizationProvider dateAdapter={AdapterLuxon}>\n <DesktopDatePicker\n format=\"yyyy-MM-dd\"\n label={label}\n value={initalDate}\n defaultValue={initalDate}\n onChange={onDateChange}\n />\n </LocalizationProvider>\n </FormControl>\n );\n};\n"],"names":[],"mappings":";;;;;;AAaO,MAAM,eAAe,CAAC;AAAA,EAC3B,KAAA;AAAA,EACA,UAAA;AAAA,EACA,YAAA;AACF,CAAyB,KAAA;AACvB,EAAA,2CACG,WAAY,EAAA,EAAA,OAAA,EAAQ,8BAClB,KAAA,CAAA,aAAA,CAAA,oBAAA,EAAA,EAAqB,aAAa,YACjC,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,iBAAA;AAAA,IAAA;AAAA,MACC,MAAO,EAAA,YAAA;AAAA,MACP,KAAA;AAAA,MACA,KAAO,EAAA,UAAA;AAAA,MACP,YAAc,EAAA,UAAA;AAAA,MACd,QAAU,EAAA,YAAA;AAAA,KAAA;AAAA,GAEd,CACF,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import Box from '@mui/material/Box';
|
|
3
|
+
import Button from '@mui/material/Button';
|
|
4
|
+
|
|
5
|
+
const SignInContent = ({ handleAuthClick }) => {
|
|
6
|
+
return /* @__PURE__ */ React.createElement(Box, { position: "relative", height: "100%", width: "100%" }, /* @__PURE__ */ React.createElement(
|
|
7
|
+
Box,
|
|
8
|
+
{
|
|
9
|
+
height: "100%",
|
|
10
|
+
width: "80%",
|
|
11
|
+
display: "flex",
|
|
12
|
+
justifyContent: "center",
|
|
13
|
+
alignItems: "center",
|
|
14
|
+
position: "absolute",
|
|
15
|
+
p: 10,
|
|
16
|
+
left: 0,
|
|
17
|
+
top: 0
|
|
18
|
+
},
|
|
19
|
+
/* @__PURE__ */ React.createElement(
|
|
20
|
+
Button,
|
|
21
|
+
{
|
|
22
|
+
variant: "contained",
|
|
23
|
+
color: "primary",
|
|
24
|
+
onClick: handleAuthClick,
|
|
25
|
+
size: "large"
|
|
26
|
+
},
|
|
27
|
+
"Sign in"
|
|
28
|
+
)
|
|
29
|
+
));
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export { SignInContent };
|
|
33
|
+
//# sourceMappingURL=SignInContent.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"SignInContent.esm.js","sources":["../../../src/components/SignInContent/SignInContent.tsx"],"sourcesContent":["import React from 'react';\nimport Box from '@mui/material/Box';\nimport Button from '@mui/material/Button';\n\ntype Props = {\n handleAuthClick: React.MouseEventHandler<HTMLElement>;\n};\n\nexport const SignInContent = ({ handleAuthClick }: Props) => {\n return (\n <Box position=\"relative\" height=\"100%\" width=\"100%\">\n <Box\n height=\"100%\"\n width=\"80%\"\n display=\"flex\"\n justifyContent=\"center\"\n alignItems=\"center\"\n position=\"absolute\"\n p={10}\n left={0}\n top={0}\n >\n <Button\n variant=\"contained\"\n color=\"primary\"\n onClick={handleAuthClick}\n size=\"large\"\n >\n Sign in\n </Button>\n </Box>\n </Box>\n );\n};\n"],"names":[],"mappings":";;;;AAQO,MAAM,aAAgB,GAAA,CAAC,EAAE,eAAA,EAA6B,KAAA;AAC3D,EAAA,2CACG,GAAI,EAAA,EAAA,QAAA,EAAS,YAAW,MAAO,EAAA,MAAA,EAAO,OAAM,MAC3C,EAAA,kBAAA,KAAA,CAAA,aAAA;AAAA,IAAC,GAAA;AAAA,IAAA;AAAA,MACC,MAAO,EAAA,MAAA;AAAA,MACP,KAAM,EAAA,KAAA;AAAA,MACN,OAAQ,EAAA,MAAA;AAAA,MACR,cAAe,EAAA,QAAA;AAAA,MACf,UAAW,EAAA,QAAA;AAAA,MACX,QAAS,EAAA,UAAA;AAAA,MACT,CAAG,EAAA,EAAA;AAAA,MACH,IAAM,EAAA,CAAA;AAAA,MACN,GAAK,EAAA,CAAA;AAAA,KAAA;AAAA,oBAEL,KAAA,CAAA,aAAA;AAAA,MAAC,MAAA;AAAA,MAAA;AAAA,QACC,OAAQ,EAAA,WAAA;AAAA,QACR,KAAM,EAAA,SAAA;AAAA,QACN,OAAS,EAAA,eAAA;AAAA,QACT,IAAK,EAAA,OAAA;AAAA,OAAA;AAAA,MACN,SAAA;AAAA,KAED;AAAA,GAEJ,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
|
|
3
|
+
import { CalendarCard } from './CalendarCard/CalendarCard.esm.js';
|
|
4
|
+
|
|
5
|
+
const queryClient = new QueryClient();
|
|
6
|
+
const VacationCalendar = () => {
|
|
7
|
+
return /* @__PURE__ */ React.createElement(QueryClientProvider, { client: queryClient }, /* @__PURE__ */ React.createElement(CalendarCard, null));
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { VacationCalendar };
|
|
11
|
+
//# sourceMappingURL=VacationCalendar.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"VacationCalendar.esm.js","sources":["../../src/components/VacationCalendar.tsx"],"sourcesContent":["import React from 'react';\nimport { QueryClient, QueryClientProvider } from '@tanstack/react-query';\nimport { CalendarCard } from './CalendarCard';\n\nconst queryClient = new QueryClient();\n\nexport const VacationCalendar = () => {\n return (\n <QueryClientProvider client={queryClient}>\n <CalendarCard />\n </QueryClientProvider>\n );\n};\n"],"names":[],"mappings":";;;;AAIA,MAAM,WAAA,GAAc,IAAI,WAAY,EAAA,CAAA;AAE7B,MAAM,mBAAmB,MAAM;AACpC,EAAA,2CACG,mBAAoB,EAAA,EAAA,MAAA,EAAQ,WAC3B,EAAA,kBAAA,KAAA,CAAA,aAAA,CAAC,kBAAa,CAChB,CAAA,CAAA;AAEJ;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { useInfiniteQuery } from '@tanstack/react-query';
|
|
2
|
+
import { useApi } from '@backstage/core-plugin-api';
|
|
3
|
+
import { slice } from 'lodash';
|
|
4
|
+
import { vacationCalendarApiRef } from '../api/VacationCalendarApi.esm.js';
|
|
5
|
+
import '@backstage/errors';
|
|
6
|
+
|
|
7
|
+
const PAGE_SIZE = 20;
|
|
8
|
+
const useAvailability = (users, startDate, endDate, isSignedIn) => {
|
|
9
|
+
const calendarApi = useApi(vacationCalendarApiRef);
|
|
10
|
+
const {
|
|
11
|
+
data: availablity,
|
|
12
|
+
error,
|
|
13
|
+
isLoading: isAvailabilityLoading,
|
|
14
|
+
isFetching: isAvailabilityFetching,
|
|
15
|
+
hasNextPage,
|
|
16
|
+
fetchNextPage
|
|
17
|
+
} = useInfiniteQuery({
|
|
18
|
+
queryFn: ({ pageParam = 0 }) => {
|
|
19
|
+
const currentUsers = slice(users || [], pageParam, pageParam + PAGE_SIZE);
|
|
20
|
+
return calendarApi.getAvailability(
|
|
21
|
+
{
|
|
22
|
+
users: currentUsers?.map((u) => u.spec.profile?.email ?? "") || [],
|
|
23
|
+
startDateTime: startDate.toISO(),
|
|
24
|
+
endDateTime: endDate.toISO()
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
Prefer: 'outlook.timezone="Europe/Stockholm"'
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
},
|
|
31
|
+
queryKey: [
|
|
32
|
+
"calendarAvailability",
|
|
33
|
+
users,
|
|
34
|
+
startDate.toISO(),
|
|
35
|
+
endDate.toISO()
|
|
36
|
+
],
|
|
37
|
+
getNextPageParam: (_, allPages) => {
|
|
38
|
+
const numberOfUsers = allPages.flatMap((p) => p).length;
|
|
39
|
+
if (numberOfUsers === users?.length) {
|
|
40
|
+
return void 0;
|
|
41
|
+
}
|
|
42
|
+
return numberOfUsers;
|
|
43
|
+
},
|
|
44
|
+
cacheTime: 3e4 * 1e3,
|
|
45
|
+
enabled: isSignedIn && startDate.isValid && endDate.isValid && !!users,
|
|
46
|
+
retry: false,
|
|
47
|
+
refetchInterval: 3e4 * 1e3,
|
|
48
|
+
refetchIntervalInBackground: false,
|
|
49
|
+
refetchOnWindowFocus: false,
|
|
50
|
+
refetchOnReconnect: false
|
|
51
|
+
});
|
|
52
|
+
return {
|
|
53
|
+
availablity,
|
|
54
|
+
error,
|
|
55
|
+
isLoading: isAvailabilityLoading,
|
|
56
|
+
isFetching: isAvailabilityFetching,
|
|
57
|
+
hasNextPage,
|
|
58
|
+
fetchNextPage
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export { useAvailability };
|
|
63
|
+
//# sourceMappingURL=useAvailibility.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useAvailibility.esm.js","sources":["../../src/hooks/useAvailibility.ts"],"sourcesContent":["import { useInfiniteQuery } from '@tanstack/react-query';\nimport { UserEntityV1alpha1 } from '@backstage/catalog-model';\nimport { useApi } from '@backstage/core-plugin-api';\nimport { slice } from 'lodash';\nimport { DateTime } from 'luxon';\nimport { vacationCalendarApiRef } from '../api';\n\nconst PAGE_SIZE = 20;\n\nexport const useAvailability = (\n users: UserEntityV1alpha1[] | undefined,\n startDate: DateTime<true>,\n endDate: DateTime<true>,\n isSignedIn: boolean,\n) => {\n const calendarApi = useApi(vacationCalendarApiRef);\n const {\n data: availablity,\n error,\n isLoading: isAvailabilityLoading,\n isFetching: isAvailabilityFetching,\n hasNextPage,\n fetchNextPage,\n } = useInfiniteQuery({\n queryFn: ({ pageParam = 0 }) => {\n const currentUsers = slice(users || [], pageParam, pageParam + PAGE_SIZE);\n\n return calendarApi.getAvailability(\n {\n users: currentUsers?.map(u => u.spec.profile?.email ?? '') || [],\n startDateTime: startDate.toISO(),\n endDateTime: endDate.toISO(),\n },\n {\n Prefer: 'outlook.timezone=\"Europe/Stockholm\"',\n },\n );\n },\n queryKey: [\n 'calendarAvailability',\n users,\n startDate.toISO(),\n endDate.toISO(),\n ],\n getNextPageParam: (_, allPages) => {\n const numberOfUsers = allPages.flatMap(p => p).length;\n if (numberOfUsers === users?.length) {\n return undefined;\n }\n // This will be starting index to fetch the next batch.\n return numberOfUsers;\n },\n cacheTime: 30000 * 1000,\n enabled: isSignedIn && startDate.isValid && endDate.isValid && !!users,\n retry: false,\n refetchInterval: 30000 * 1000,\n refetchIntervalInBackground: false,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n });\n return {\n availablity,\n error,\n isLoading: isAvailabilityLoading,\n isFetching: isAvailabilityFetching,\n hasNextPage,\n fetchNextPage,\n };\n};\n"],"names":[],"mappings":";;;;;;AAOA,MAAM,SAAY,GAAA,EAAA,CAAA;AAEX,MAAM,eAAkB,GAAA,CAC7B,KACA,EAAA,SAAA,EACA,SACA,UACG,KAAA;AACH,EAAM,MAAA,WAAA,GAAc,OAAO,sBAAsB,CAAA,CAAA;AACjD,EAAM,MAAA;AAAA,IACJ,IAAM,EAAA,WAAA;AAAA,IACN,KAAA;AAAA,IACA,SAAW,EAAA,qBAAA;AAAA,IACX,UAAY,EAAA,sBAAA;AAAA,IACZ,WAAA;AAAA,IACA,aAAA;AAAA,MACE,gBAAiB,CAAA;AAAA,IACnB,OAAS,EAAA,CAAC,EAAE,SAAA,GAAY,GAAQ,KAAA;AAC9B,MAAA,MAAM,eAAe,KAAM,CAAA,KAAA,IAAS,EAAI,EAAA,SAAA,EAAW,YAAY,SAAS,CAAA,CAAA;AAExE,MAAA,OAAO,WAAY,CAAA,eAAA;AAAA,QACjB;AAAA,UACE,KAAA,EAAO,YAAc,EAAA,GAAA,CAAI,CAAK,CAAA,KAAA,CAAA,CAAE,KAAK,OAAS,EAAA,KAAA,IAAS,EAAE,CAAA,IAAK,EAAC;AAAA,UAC/D,aAAA,EAAe,UAAU,KAAM,EAAA;AAAA,UAC/B,WAAA,EAAa,QAAQ,KAAM,EAAA;AAAA,SAC7B;AAAA,QACA;AAAA,UACE,MAAQ,EAAA,qCAAA;AAAA,SACV;AAAA,OACF,CAAA;AAAA,KACF;AAAA,IACA,QAAU,EAAA;AAAA,MACR,sBAAA;AAAA,MACA,KAAA;AAAA,MACA,UAAU,KAAM,EAAA;AAAA,MAChB,QAAQ,KAAM,EAAA;AAAA,KAChB;AAAA,IACA,gBAAA,EAAkB,CAAC,CAAA,EAAG,QAAa,KAAA;AACjC,MAAA,MAAM,aAAgB,GAAA,QAAA,CAAS,OAAQ,CAAA,CAAA,CAAA,KAAK,CAAC,CAAE,CAAA,MAAA,CAAA;AAC/C,MAAI,IAAA,aAAA,KAAkB,OAAO,MAAQ,EAAA;AACnC,QAAO,OAAA,KAAA,CAAA,CAAA;AAAA,OACT;AAEA,MAAO,OAAA,aAAA,CAAA;AAAA,KACT;AAAA,IACA,WAAW,GAAQ,GAAA,GAAA;AAAA,IACnB,SAAS,UAAc,IAAA,SAAA,CAAU,WAAW,OAAQ,CAAA,OAAA,IAAW,CAAC,CAAC,KAAA;AAAA,IACjE,KAAO,EAAA,KAAA;AAAA,IACP,iBAAiB,GAAQ,GAAA,GAAA;AAAA,IACzB,2BAA6B,EAAA,KAAA;AAAA,IAC7B,oBAAsB,EAAA,KAAA;AAAA,IACtB,kBAAoB,EAAA,KAAA;AAAA,GACrB,CAAA,CAAA;AACD,EAAO,OAAA;AAAA,IACL,WAAA;AAAA,IACA,KAAA;AAAA,IACA,SAAW,EAAA,qBAAA;AAAA,IACX,UAAY,EAAA,sBAAA;AAAA,IACZ,WAAA;AAAA,IACA,aAAA;AAAA,GACF,CAAA;AACF;;;;"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { useState, useCallback } from 'react';
|
|
2
|
+
import { useApi, microsoftAuthApiRef } from '@backstage/core-plugin-api';
|
|
3
|
+
|
|
4
|
+
const useSignIn = () => {
|
|
5
|
+
const [isSignedIn, setSignedIn] = useState(false);
|
|
6
|
+
const [isInitialized, setInitialized] = useState(false);
|
|
7
|
+
const authApi = useApi(microsoftAuthApiRef);
|
|
8
|
+
const signIn = useCallback(
|
|
9
|
+
async (optional = false) => {
|
|
10
|
+
const token = await authApi.getAccessToken("Calendars.Read", {
|
|
11
|
+
optional,
|
|
12
|
+
instantPopup: !optional
|
|
13
|
+
});
|
|
14
|
+
setSignedIn(!!token);
|
|
15
|
+
setInitialized(true);
|
|
16
|
+
},
|
|
17
|
+
[authApi, setSignedIn]
|
|
18
|
+
);
|
|
19
|
+
return { isSignedIn, isInitialized, signIn };
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export { useSignIn };
|
|
23
|
+
//# sourceMappingURL=useSignIn.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useSignIn.esm.js","sources":["../../src/hooks/useSignIn.ts"],"sourcesContent":["import { useCallback, useState } from 'react';\nimport { microsoftAuthApiRef, useApi } from '@backstage/core-plugin-api';\n\nexport const useSignIn = () => {\n const [isSignedIn, setSignedIn] = useState(false);\n const [isInitialized, setInitialized] = useState(false);\n const authApi = useApi(microsoftAuthApiRef);\n\n const signIn = useCallback(\n async (optional = false) => {\n const token = await authApi.getAccessToken('Calendars.Read', {\n optional,\n instantPopup: !optional,\n });\n\n setSignedIn(!!token);\n setInitialized(true);\n },\n [authApi, setSignedIn],\n );\n\n return { isSignedIn, isInitialized, signIn };\n};\n"],"names":[],"mappings":";;;AAGO,MAAM,YAAY,MAAM;AAC7B,EAAA,MAAM,CAAC,UAAA,EAAY,WAAW,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AAChD,EAAA,MAAM,CAAC,aAAA,EAAe,cAAc,CAAA,GAAI,SAAS,KAAK,CAAA,CAAA;AACtD,EAAM,MAAA,OAAA,GAAU,OAAO,mBAAmB,CAAA,CAAA;AAE1C,EAAA,MAAM,MAAS,GAAA,WAAA;AAAA,IACb,OAAO,WAAW,KAAU,KAAA;AAC1B,MAAA,MAAM,KAAQ,GAAA,MAAM,OAAQ,CAAA,cAAA,CAAe,gBAAkB,EAAA;AAAA,QAC3D,QAAA;AAAA,QACA,cAAc,CAAC,QAAA;AAAA,OAChB,CAAA,CAAA;AAED,MAAY,WAAA,CAAA,CAAC,CAAC,KAAK,CAAA,CAAA;AACnB,MAAA,cAAA,CAAe,IAAI,CAAA,CAAA;AAAA,KACrB;AAAA,IACA,CAAC,SAAS,WAAW,CAAA;AAAA,GACvB,CAAA;AAEA,EAAO,OAAA,EAAE,UAAY,EAAA,aAAA,EAAe,MAAO,EAAA,CAAA;AAC7C;;;;"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/// <reference types="react" />
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import * as _backstage_core_plugin_api from '@backstage/core-plugin-api';
|
|
4
|
+
import { OAuthApi, FetchApi } from '@backstage/core-plugin-api';
|
|
5
|
+
import { Calendar, ScheduleInformation } from '@microsoft/microsoft-graph-types';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Plugin that provides the VacationCalendar api
|
|
9
|
+
* @public */
|
|
10
|
+
declare const vacationCalendarPlugin: _backstage_core_plugin_api.BackstagePlugin<{
|
|
11
|
+
root: _backstage_core_plugin_api.RouteRef<undefined>;
|
|
12
|
+
}, {}, {}>;
|
|
13
|
+
/**
|
|
14
|
+
* Routable extension for VacationCalendarPage
|
|
15
|
+
* @public
|
|
16
|
+
*/
|
|
17
|
+
declare const VacationCalendarPage: () => react.JSX.Element;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The apiref for the VacationCalendar plugin.
|
|
21
|
+
*
|
|
22
|
+
* @public
|
|
23
|
+
*/
|
|
24
|
+
declare const vacationCalendarApiRef: _backstage_core_plugin_api.ApiRef<VacationCalendarApi>;
|
|
25
|
+
/**
|
|
26
|
+
* The definition for the VacationCalendar api.
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
*/
|
|
30
|
+
interface VacationCalendarApi {
|
|
31
|
+
/**
|
|
32
|
+
* Fetches schedule items for users
|
|
33
|
+
*/
|
|
34
|
+
getCalendars(): Promise<Calendar[]>;
|
|
35
|
+
/**
|
|
36
|
+
* Fetches Microsoft calendars
|
|
37
|
+
*/
|
|
38
|
+
getAvailability(params: {
|
|
39
|
+
users: string[];
|
|
40
|
+
startDateTime: string;
|
|
41
|
+
endDateTime: string;
|
|
42
|
+
}, headers: {
|
|
43
|
+
[x: string]: any;
|
|
44
|
+
}): Promise<ScheduleInformation[]>;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The client implementation for the frontend api.
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
declare class VacationCalendarApiClient {
|
|
53
|
+
private readonly authApi;
|
|
54
|
+
private readonly fetchApi;
|
|
55
|
+
constructor(options: {
|
|
56
|
+
authApi: OAuthApi;
|
|
57
|
+
fetchApi: FetchApi;
|
|
58
|
+
});
|
|
59
|
+
private get;
|
|
60
|
+
private post;
|
|
61
|
+
/**
|
|
62
|
+
* Fetches Microsoft calendars
|
|
63
|
+
*
|
|
64
|
+
* @returns the MicrosoftValendar objects
|
|
65
|
+
*/
|
|
66
|
+
getCalendars(): Promise<Calendar[]>;
|
|
67
|
+
/**
|
|
68
|
+
* Fetches schedule items for users
|
|
69
|
+
*
|
|
70
|
+
* @param users - list of users
|
|
71
|
+
* @param startDateTime - string with start date
|
|
72
|
+
* @param endDateTime - string with end date
|
|
73
|
+
* @returns Microsoft ScheduleInformation items
|
|
74
|
+
*/
|
|
75
|
+
getAvailability(params: {
|
|
76
|
+
users: string[];
|
|
77
|
+
startDateTime: string;
|
|
78
|
+
endDateTime: string;
|
|
79
|
+
}, headers: {
|
|
80
|
+
[key in string]: any;
|
|
81
|
+
}): Promise<ScheduleInformation[]>;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export { type VacationCalendarApi, VacationCalendarApiClient, VacationCalendarPage, vacationCalendarApiRef, vacationCalendarPlugin };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { VacationCalendarPage, vacationCalendarPlugin } from './plugin.esm.js';
|
|
2
|
+
export { vacationCalendarApiRef } from './api/VacationCalendarApi.esm.js';
|
|
3
|
+
export { VacationCalendarApiClient } from './api/VacationCalendarClient.esm.js';
|
|
4
|
+
//# sourceMappingURL=index.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import 'react-calendar-timeline/lib/Timeline.css';
|
|
2
|
+
import { createPlugin, createApiFactory, microsoftAuthApiRef, fetchApiRef, createRoutableExtension } from '@backstage/core-plugin-api';
|
|
3
|
+
import { rootRouteRef } from './routes.esm.js';
|
|
4
|
+
import { vacationCalendarApiRef } from './api/VacationCalendarApi.esm.js';
|
|
5
|
+
import { VacationCalendarApiClient } from './api/VacationCalendarClient.esm.js';
|
|
6
|
+
|
|
7
|
+
const vacationCalendarPlugin = createPlugin({
|
|
8
|
+
id: "vacation-calendar",
|
|
9
|
+
apis: [
|
|
10
|
+
createApiFactory({
|
|
11
|
+
api: vacationCalendarApiRef,
|
|
12
|
+
deps: {
|
|
13
|
+
authApi: microsoftAuthApiRef,
|
|
14
|
+
fetchApi: fetchApiRef
|
|
15
|
+
},
|
|
16
|
+
factory: ({ authApi, fetchApi }) => new VacationCalendarApiClient({ authApi, fetchApi })
|
|
17
|
+
})
|
|
18
|
+
],
|
|
19
|
+
routes: {
|
|
20
|
+
root: rootRouteRef
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
const VacationCalendarPage = vacationCalendarPlugin.provide(
|
|
24
|
+
createRoutableExtension({
|
|
25
|
+
name: "VacationCalendarPage",
|
|
26
|
+
component: () => import('./components/index.esm.js').then((m) => m.VacationCalendar),
|
|
27
|
+
mountPoint: rootRouteRef
|
|
28
|
+
})
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
export { VacationCalendarPage, vacationCalendarPlugin };
|
|
32
|
+
//# sourceMappingURL=plugin.esm.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plugin.esm.js","sources":["../src/plugin.ts"],"sourcesContent":["import 'react-calendar-timeline/lib/Timeline.css';\nimport {\n createApiFactory,\n createPlugin,\n createRoutableExtension,\n fetchApiRef,\n microsoftAuthApiRef,\n} from '@backstage/core-plugin-api';\nimport { rootRouteRef } from './routes';\nimport { VacationCalendarApiClient, vacationCalendarApiRef } from './api';\n\n/**\n * Plugin that provides the VacationCalendar api\n * @public */\nexport const vacationCalendarPlugin = createPlugin({\n id: 'vacation-calendar',\n apis: [\n createApiFactory({\n api: vacationCalendarApiRef,\n deps: {\n authApi: microsoftAuthApiRef,\n fetchApi: fetchApiRef,\n },\n factory: ({ authApi, fetchApi }) =>\n new VacationCalendarApiClient({ authApi, fetchApi }),\n }),\n ],\n routes: {\n root: rootRouteRef,\n },\n});\n\n/**\n * Routable extension for VacationCalendarPage\n * @public\n */\nexport const VacationCalendarPage = vacationCalendarPlugin.provide(\n createRoutableExtension({\n name: 'VacationCalendarPage',\n component: () => import('./components').then(m => m.VacationCalendar),\n mountPoint: rootRouteRef,\n }),\n);\n"],"names":[],"mappings":";;;;;;AAcO,MAAM,yBAAyB,YAAa,CAAA;AAAA,EACjD,EAAI,EAAA,mBAAA;AAAA,EACJ,IAAM,EAAA;AAAA,IACJ,gBAAiB,CAAA;AAAA,MACf,GAAK,EAAA,sBAAA;AAAA,MACL,IAAM,EAAA;AAAA,QACJ,OAAS,EAAA,mBAAA;AAAA,QACT,QAAU,EAAA,WAAA;AAAA,OACZ;AAAA,MACA,OAAA,EAAS,CAAC,EAAE,OAAS,EAAA,QAAA,EACnB,KAAA,IAAI,yBAA0B,CAAA,EAAE,OAAS,EAAA,QAAA,EAAU,CAAA;AAAA,KACtD,CAAA;AAAA,GACH;AAAA,EACA,MAAQ,EAAA;AAAA,IACN,IAAM,EAAA,YAAA;AAAA,GACR;AACF,CAAC,EAAA;AAMM,MAAM,uBAAuB,sBAAuB,CAAA,OAAA;AAAA,EACzD,uBAAwB,CAAA;AAAA,IACtB,IAAM,EAAA,sBAAA;AAAA,IACN,SAAA,EAAW,MAAM,OAAO,2BAAc,EAAE,IAAK,CAAA,CAAA,CAAA,KAAK,EAAE,gBAAgB,CAAA;AAAA,IACpE,UAAY,EAAA,YAAA;AAAA,GACb,CAAA;AACH;;;;"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"routes.esm.js","sources":["../src/routes.ts"],"sourcesContent":["import { createRouteRef } from '@backstage/core-plugin-api';\n\nexport const rootRouteRef = createRouteRef({\n id: 'vacation-calendar',\n});\n"],"names":[],"mappings":";;AAEO,MAAM,eAAe,cAAe,CAAA;AAAA,EACzC,EAAI,EAAA,mBAAA;AACN,CAAC;;;;"}
|
package/package.json
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@axis-backstage/plugin-vacation-calendar",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"main": "dist/index.esm.js",
|
|
5
|
+
"types": "dist/index.d.ts",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public",
|
|
9
|
+
"main": "dist/index.esm.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"directory": "_release/package"
|
|
12
|
+
},
|
|
13
|
+
"backstage": {
|
|
14
|
+
"role": "frontend-plugin",
|
|
15
|
+
"pluginId": "vacation-calendar",
|
|
16
|
+
"pluginPackages": [
|
|
17
|
+
"@axis-backstage/plugin-vacation-calendar"
|
|
18
|
+
]
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"scripts": {
|
|
22
|
+
"start": "backstage-cli package start",
|
|
23
|
+
"build": "backstage-cli package build",
|
|
24
|
+
"lint": "backstage-cli package lint",
|
|
25
|
+
"test": "backstage-cli package test",
|
|
26
|
+
"clean": "backstage-cli package clean",
|
|
27
|
+
"prepack": "backstage-cli package prepack",
|
|
28
|
+
"postpack": "backstage-cli package postpack"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@backstage/catalog-model": "^1.5.0",
|
|
32
|
+
"@backstage/core-components": "^0.14.8",
|
|
33
|
+
"@backstage/core-plugin-api": "^1.9.3",
|
|
34
|
+
"@backstage/errors": "^1.2.4",
|
|
35
|
+
"@backstage/plugin-catalog-react": "^1.12.1",
|
|
36
|
+
"@backstage/theme": "^0.5.6",
|
|
37
|
+
"@date-io/luxon": "3.0.0",
|
|
38
|
+
"@material-ui/pickers": "^3.3.11",
|
|
39
|
+
"@microsoft/microsoft-graph-types": "^2.40.0",
|
|
40
|
+
"@mui/lab": "^5.0.0-alpha.170",
|
|
41
|
+
"@mui/material": "^5.15.20",
|
|
42
|
+
"@mui/x-date-pickers": "^7.7.0",
|
|
43
|
+
"@tanstack/react-query": "4.29.1",
|
|
44
|
+
"interactjs": "^1.10.27",
|
|
45
|
+
"lodash": "^4.17.21",
|
|
46
|
+
"luxon": "^3.4.4",
|
|
47
|
+
"react-calendar-timeline": "^0.28.0",
|
|
48
|
+
"react-use": "^17.5.0"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"react": "^16.13.1 || ^17.0.0 || ^18.0.0"
|
|
52
|
+
},
|
|
53
|
+
"devDependencies": {
|
|
54
|
+
"@backstage/catalog-client": "^1.6.5",
|
|
55
|
+
"@backstage/cli": "^0.26.10",
|
|
56
|
+
"@backstage/core-app-api": "^1.12.6",
|
|
57
|
+
"@backstage/dev-utils": "^1.0.33",
|
|
58
|
+
"@backstage/test-utils": "^1.5.6",
|
|
59
|
+
"@testing-library/jest-dom": "^5.10.1",
|
|
60
|
+
"@testing-library/react": "^16.0.0",
|
|
61
|
+
"@testing-library/user-event": "^14.5.2",
|
|
62
|
+
"@types/lodash": "^4.17.5",
|
|
63
|
+
"@types/luxon": "^3.4.2",
|
|
64
|
+
"@types/react-calendar-timeline": "^0.28.6"
|
|
65
|
+
},
|
|
66
|
+
"files": [
|
|
67
|
+
"dist"
|
|
68
|
+
],
|
|
69
|
+
"module": "./dist/index.esm.js",
|
|
70
|
+
"directory": "_release/package"
|
|
71
|
+
}
|