diff --git a/.changeset/warm-singers-relate.md b/.changeset/warm-singers-relate.md new file mode 100644 index 00000000..7afec39e --- /dev/null +++ b/.changeset/warm-singers-relate.md @@ -0,0 +1,7 @@ +--- +'@axis-backstage/plugin-vacation-calendar': minor +'backend': minor +'app': minor +--- + +Added new plugin for displaying out-of-office events in a react-calendar-timeline. Also added support for microsoft autentication. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 346f7153..24a41565 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,13 @@ # Contributing to Axis Backstage plugins -Contributions are welcome, and they are greatly appreciated! We really want to improve our plugins ✨ +Contributions are welcome and greatly appreciated! We are committed to improving our plugins ✨ + +These contributing guidelines explain: + +- How to contribute effectively for optimal communication +- The conventions we would like you to follow + +If you have any questions, please let us know! ## Code of Conduct @@ -10,27 +17,43 @@ This project adheres to the [CNCF Code of Conduct][code-of-conduct]. By particip ## How can I contribute? -We welcome anything from bugs fixes or new, implemented features. You can create your own Pull request. Once you've submitted a Pull Request (PR), tests will be run and we will have a look at your PR as soon as we can. If we have any comments or questions, we will do so in the PR. +### 1. Create Issue -You need approval from one of the core maintainers (in this case, the Backstage team at Axis). Once you have one approval it's ready to be merged, this task is also done by one of us. +We welcome anything from bug fixes to new features. **Please start by creating an [issue in our repository](https://github.com/backstage/backstage/issues/new/choose)**. The Axis Backstage team will respond as soon as possible regarding: -As a contributor, here are the guidelines we would like you to follow: +- Whether this is an implementation we want in our main repository +- If there are any points to discuss before implementing the feature +- Who should implement the feature + +### 2. Create Pull Request + +#### Pull Request Best Practises + +Once the discussion in the issue is complete, you are welcome to create a Pull Request (PR). We follow [GitHub's best practices for creating pull Requests](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/getting-started/best-practices-for-pull-requests). Please review these guidelines if you are not familiar with them. -- [Api-reports](#api-reports) -- [Changesets](#changesets) -- [Styling conventions](#styling-conventions) +In addition, provide a clear title for your PR that explains the change. + +#### Pull Request Approval + +After submitting a PR, tests will run and the team will review it as soon as possible. If there are any comments or questions, we will communicate through the PR. + +You need approval from one of the core maintainers (the Axis Backstage team). Once you have approval, the PR is ready to be merged. **This task will also be done by one of us**. + +## Other contributing guidelines + +As a contributor, here are the guidelines we would like you to follow: -## Api reports +### Api reports Similar to Backstage upstream, we use api reports generated by [API Extractor](https://api-extractor.com/) to help us build better TypeScript library packages. To learn more about api-reports and how to generate them, we refer to the [upstream documentation](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#api-reports). -## Changesets +### Changesets Just like Backstage upstream, we use [changesets](https://github.com/changesets/changesets) to help us prepare releases. By using changsets we can make sure that the process generating releases is easy, and that every change gets a proper version number. If you wish to know more about the use of **changesets** in Backstage, please read the [official Backstage documentation about changesets](https://github.com/backstage/backstage/blob/master/CONTRIBUTING.md#creating-changesets). -### How to create a changeset +#### How to create a changeset 1. Run `yarn changeset` from the root of the repo 2. Select which packages you want to include a changeset for diff --git a/app-config.yaml b/app-config.yaml index e581e8f0..913cd1ee 100644 --- a/app-config.yaml +++ b/app-config.yaml @@ -72,7 +72,15 @@ techdocs: auth: # see https://backstage.io/docs/auth/ to learn about auth providers - providers: {} + providers: + microsoft: + development: + clientId: ${AUTH_MICROSOFT_CLIENT_ID} + clientSecret: ${AUTH_MICROSOFT_CLIENT_SECRET} + tenantId: ${AUTH_MICROSOFT_TENANT_ID} + signIn: + resolvers: + - resolver: emailMatchingUserEntityProfileEmail scaffolder: # see https://backstage.io/docs/features/software-templates/configuration for software template options diff --git a/examples/org.yaml b/examples/org.yaml index a10e81fc..4f9b1bf6 100644 --- a/examples/org.yaml +++ b/examples/org.yaml @@ -7,6 +7,18 @@ metadata: spec: memberOf: [guests] --- +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + name: Sophie + description: Software Developer +spec: + profile: + displayName: Sophie + email: sophie@mail.com + picture: https://api.dicebear.com/7.x/avataaars/svg?seed=Sophie + memberOf: [guests] +--- # https://backstage.io/docs/features/software-catalog/descriptor-format#kind-group apiVersion: backstage.io/v1alpha1 kind: Group diff --git a/packages/app/package.json b/packages/app/package.json index 57a45ee3..b61f8485 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -19,6 +19,7 @@ "@axis-backstage/plugin-jira-dashboard-common": "workspace:^", "@axis-backstage/plugin-readme": "workspace:^", "@axis-backstage/plugin-statuspage": "workspace:^", + "@axis-backstage/plugin-vacation-calendar": "^0.1.0", "@backstage-community/plugin-github-actions": "^0.6.16", "@backstage/app-defaults": "^1.5.5", "@backstage/catalog-model": "^1.5.0", diff --git a/packages/app/src/App.tsx b/packages/app/src/App.tsx index 776e1b49..f9756dcb 100644 --- a/packages/app/src/App.tsx +++ b/packages/app/src/App.tsx @@ -26,15 +26,37 @@ import { entityPage } from './components/catalog/EntityPage'; import { searchPage } from './components/search/SearchPage'; import { Root } from './components/Root'; -import { AlertDisplay, OAuthRequestDialog } from '@backstage/core-components'; +import { + AlertDisplay, + OAuthRequestDialog, + SignInPage, +} from '@backstage/core-components'; import { createApp } from '@backstage/app-defaults'; import { AppRouter, FlatRoutes } from '@backstage/core-app-api'; import { CatalogGraphPage } from '@backstage/plugin-catalog-graph'; import { RequirePermission } from '@backstage/plugin-permission-react'; import { catalogEntityCreatePermission } from '@backstage/plugin-catalog-common/alpha'; import { StatuspagePage } from '@axis-backstage/plugin-statuspage'; +import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar'; +import { microsoftAuthApiRef } from '@backstage/core-plugin-api'; const app = createApp({ + components: { + SignInPage: props => ( + + ), + }, apis, bindRoutes({ bind }) { bind(catalogPlugin.externalRoutes, { @@ -90,6 +112,7 @@ const routes = ( } /> } /> } /> + } /> ); diff --git a/packages/app/src/apis.ts b/packages/app/src/apis.ts index 96847f1f..c8a966de 100644 --- a/packages/app/src/apis.ts +++ b/packages/app/src/apis.ts @@ -10,12 +10,17 @@ import { createApiFactory, discoveryApiRef, fetchApiRef, + microsoftAuthApiRef, } from '@backstage/core-plugin-api'; import { UmamiAnalytics } from '@axis-backstage/plugin-analytics-module-umami'; import { StatuspageClient, statuspageApiRef, } from '@axis-backstage/plugin-statuspage'; +import { + vacationCalendarApiRef, + VacationCalendarApiClient, +} from '@axis-backstage/plugin-vacation-calendar'; export const apis: AnyApiFactory[] = [ createApiFactory({ @@ -41,5 +46,14 @@ export const apis: AnyApiFactory[] = [ factory: ({ discoveryApi, fetchApi }) => new StatuspageClient({ discoveryApi, fetchApi }), }), + createApiFactory({ + api: vacationCalendarApiRef, + deps: { + authApi: microsoftAuthApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ authApi, fetchApi }) => + new VacationCalendarApiClient({ authApi, fetchApi }), + }), ScmAuth.createDefaultApiFactory(), ]; diff --git a/packages/app/src/components/catalog/EntityPage.tsx b/packages/app/src/components/catalog/EntityPage.tsx index 57657e81..b84f6b7d 100644 --- a/packages/app/src/components/catalog/EntityPage.tsx +++ b/packages/app/src/components/catalog/EntityPage.tsx @@ -61,6 +61,7 @@ import { isStatuspageAvailable, StatuspageEntityCard, } from '@axis-backstage/plugin-statuspage'; +import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar'; const techdocsContent = ( @@ -280,6 +281,11 @@ const userPage = ( + + + + + ); @@ -299,6 +305,11 @@ const groupPage = ( + + + + + ); diff --git a/packages/backend/package.json b/packages/backend/package.json index 1287eade..9c2ca0ec 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -28,6 +28,7 @@ "@backstage/config": "^1.2.0", "@backstage/plugin-app-backend": "^0.3.67", "@backstage/plugin-auth-backend": "^0.22.5", + "@backstage/plugin-auth-backend-module-microsoft-provider": "^0.1.13", "@backstage/plugin-auth-node": "^0.4.13", "@backstage/plugin-catalog-backend": "^1.22.0", "@backstage/plugin-permission-common": "^0.7.13", diff --git a/packages/backend/src/index.ts b/packages/backend/src/index.ts index 3191e815..75dcb747 100644 --- a/packages/backend/src/index.ts +++ b/packages/backend/src/index.ts @@ -4,6 +4,7 @@ const backend = createBackend(); backend.add(import('@backstage/plugin-catalog-backend/alpha')); backend.add(import('@backstage/plugin-scaffolder-backend/alpha')); backend.add(import('@backstage/plugin-auth-backend')); +backend.add(import('@backstage/plugin-auth-backend-module-microsoft-provider')); backend.add(import('@backstage/plugin-proxy-backend/alpha')); backend.add(import('@backstage/plugin-techdocs-backend/alpha')); backend.add(import('@backstage/plugin-search-backend/alpha')); diff --git a/packages/backend/src/plugins/auth.ts b/packages/backend/src/plugins/auth.ts index 77eb6aae..47f5c501 100644 --- a/packages/backend/src/plugins/auth.ts +++ b/packages/backend/src/plugins/auth.ts @@ -49,6 +49,19 @@ export default async function createPlugin( // resolver: providers.github.resolvers.usernameMatchingUserEntityName(), }, }), + microsoft: providers.microsoft.create({ + signIn: { + resolver(_, ctx) { + const userRef = 'user:default/guest'; // Must be a full entity reference + return ctx.issueToken({ + claims: { + sub: userRef, // The user's own identity + ent: [userRef], // A list of identities that the user claims ownership through + }, + }); + }, + }, + }), }, }); } diff --git a/plugins/vacation-calendar/.eslintrc.js b/plugins/vacation-calendar/.eslintrc.js new file mode 100644 index 00000000..e2a53a6a --- /dev/null +++ b/plugins/vacation-calendar/.eslintrc.js @@ -0,0 +1 @@ +module.exports = require('@backstage/cli/config/eslint-factory')(__dirname); diff --git a/plugins/vacation-calendar/README.md b/plugins/vacation-calendar/README.md new file mode 100644 index 00000000..26580aea --- /dev/null +++ b/plugins/vacation-calendar/README.md @@ -0,0 +1,110 @@ +# Vacation Calendar plugin + +Welcome to the Vacation Calendar plugin! + +![calendar-year-light-example](https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/vacation-calendar/media/year-light.png) +![calendar-year-dark-example](https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/vacation-calendar/media/year-dark.png) + +## Introduction + +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. + +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! + +## How it works + +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. + +## Autentication + +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/). + +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. + +## Getting started + +1. First, install the plugin into your app: + +```bash +# From your Backstage root directory +yarn --cwd packages/app add @axis-backstage/plugin-vacation-calendar +``` + +2. Setup the API-factory. + +```ts +// packages/app/src/apis.ts: + +createApiFactory({ + api: vacationCalendarApiRef, + deps: { + authApi: microsoftAuthApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ authApi, fetchApi }) => + new VacationCalendarApiClient({ authApi, fetchApi }), + }), + +``` + +3. Modify your entity page to include the `VacationCalendarPage` component: + +```tsx +// In packages/app/src/components/catalog/EntityPage.tsx +import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar'; + +const groupPage = ( + + + + + + + +); +``` + +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. + +## Integration with the Catalog + +To fetch all colleagues with the same manager for a user entity, add the manager annotation to the entity's **catalog-info.yaml** file: + +```yaml +apiVersion: backstage.io/v1alpha1 +kind: User +metadata: + # ... + annotations: + manager: value # The Backstage username of the mananger +``` + +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: + +```tsx +// In packages/app/src/components/catalog/EntityPage.tsx +import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar'; + +const userPage = ( + + + + + + + +); +``` + +## Development + +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). + +You can also serve the plugin in isolation by running `yarn start` in the plugin directory. +This method of serving the plugin provides quicker iteration speed and a faster startup and hot reloads. +It is only meant for local development, and the setup for it can be found inside the [/dev](./dev) directory. + +## Screenshots + +![calendar-month-light-example](https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/vacation-calendar/media/month-light.png) +![calendar-month-dark-example](https://github.com/AxisCommunications/backstage-plugins/blob/main/plugins/vacation-calendar/media/month-dark.png) diff --git a/plugins/vacation-calendar/api-report.md b/plugins/vacation-calendar/api-report.md new file mode 100644 index 00000000..22c586b1 --- /dev/null +++ b/plugins/vacation-calendar/api-report.md @@ -0,0 +1,62 @@ +## API Report File for "@axis-backstage/plugin-vacation-calendar" + +> Do not edit this file. It is a report generated by [API Extractor](https://api-extractor.com/). + +```ts +/// + +import { ApiRef } from '@backstage/core-plugin-api'; +import { BackstagePlugin } from '@backstage/core-plugin-api'; +import { Calendar } from '@microsoft/microsoft-graph-types'; +import { FetchApi } from '@backstage/core-plugin-api'; +import { JSX as JSX_2 } from 'react'; +import { OAuthApi } from '@backstage/core-plugin-api'; +import { RouteRef } from '@backstage/core-plugin-api'; +import { ScheduleInformation } from '@microsoft/microsoft-graph-types'; + +// @public +export interface VacationCalendarApi { + getAvailability( + params: { + users: string[]; + startDateTime: string; + endDateTime: string; + }, + headers: { + [x: string]: any; + }, + ): Promise; + getCalendars(): Promise; +} + +// @public +export class VacationCalendarApiClient { + constructor(options: { authApi: OAuthApi; fetchApi: FetchApi }); + getAvailability( + params: { + users: string[]; + startDateTime: string; + endDateTime: string; + }, + headers: { + [key in string]: any; + }, + ): Promise; + getCalendars(): Promise; +} + +// @public +export const vacationCalendarApiRef: ApiRef; + +// @public +export const VacationCalendarPage: () => JSX_2.Element; + +// @public +export const vacationCalendarPlugin: BackstagePlugin< + { + root: RouteRef; + }, + {}, + {} +>; +``` diff --git a/plugins/vacation-calendar/dev/__fixtures__/availability.json b/plugins/vacation-calendar/dev/__fixtures__/availability.json new file mode 100644 index 00000000..e4bedfdb --- /dev/null +++ b/plugins/vacation-calendar/dev/__fixtures__/availability.json @@ -0,0 +1,278 @@ +[ + { + "availabilityView": "String", + "error": { "@odata.type": "microsoft.graph.freeBusyError" }, + "scheduleId": "sophie", + "scheduleItems": [ + { + "isPrivate": false, + "status": "oof", + "subject": "Vacation", + "location": "Proj: Mini", + "isMeeting": true, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-02-05T13:00:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-02-08T13:15:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": false, + "status": "oof", + "subject": "Vacation", + "location": "Proj: Mini (4 pers, NE) (Lund, SE)", + "isMeeting": true, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-09-05T13:00:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-09-10T13:15:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-11-12T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-11-30T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-04-10T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-04-20T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + } + ], + "workingHours": { + "daysOfWeek": ["monday", "tuesday", "wednesday", "thursday", "friday"], + "startTime": "08:00:00.0000000", + "endTime": "17:00:00.0000000", + "timeZone": { + "name": "W. Europe Standard Time" + } + } + }, + { + "availabilityView": "String", + "error": { "@odata.type": "microsoft.graph.freeBusyError" }, + "scheduleId": "rascal", + "scheduleItems": [ + { + "isPrivate": false, + "status": "oof", + "subject": "Vacation", + "location": "Proj: Mini (4 pers, NE) (Lund, SE)", + "isMeeting": true, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-03-05T13:00:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-03-10T13:15:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-05-12T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-05-30T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-11-05T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-11-10T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + } + ], + "workingHours": { + "daysOfWeek": ["monday", "tuesday", "wednesday", "thursday", "friday"], + "startTime": "08:00:00.0000000", + "endTime": "17:00:00.0000000", + "timeZone": { + "name": "W. Europe Standard Time" + } + } + }, + { + "availabilityView": "String", + "error": { "@odata.type": "microsoft.graph.freeBusyError" }, + "scheduleId": "buster", + "scheduleItems": [ + { + "isPrivate": false, + "status": "oof", + "subject": "Vacation", + "location": "Proj: Mini (4 pers, NE) (Lund, SE)", + "isMeeting": true, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-06-05T13:00:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-06-10T13:15:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-08-12T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-08-30T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + } + ], + "workingHours": { + "daysOfWeek": ["monday", "tuesday", "wednesday", "thursday", "friday"], + "startTime": "08:00:00.0000000", + "endTime": "17:00:00.0000000", + "timeZone": { + "name": "W. Europe Standard Time" + } + } + }, + { + "availabilityView": "String", + "error": { "@odata.type": "microsoft.graph.freeBusyError" }, + "scheduleId": "jasmine", + "scheduleItems": [ + { + "isPrivate": false, + "status": "oof", + "subject": "Vacation", + "location": "Proj: Mini (4 pers, NE) (Lund, SE)", + "isMeeting": true, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-09-05T13:00:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-09-10T13:15:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-04-12T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-04-16T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + }, + { + "isPrivate": true, + "status": "oof", + "subject": "Vacation", + "location": "", + "isMeeting": false, + "isRecurring": true, + "isException": false, + "isReminderSet": true, + "start": { + "dateTime": "2024-11-12T08:30:00.0000000", + "timeZone": "W. Europe Standard Time" + }, + "end": { + "dateTime": "2024-11-25T09:00:00.0000000", + "timeZone": "W. Europe Standard Time" + } + } + ], + "workingHours": { + "daysOfWeek": ["monday", "tuesday", "wednesday", "thursday", "friday"], + "startTime": "08:00:00.0000000", + "endTime": "17:00:00.0000000", + "timeZone": { + "name": "W. Europe Standard Time" + } + } + } +] diff --git a/plugins/vacation-calendar/dev/__fixtures__/mockEntities.json b/plugins/vacation-calendar/dev/__fixtures__/mockEntities.json new file mode 100644 index 00000000..22007e14 --- /dev/null +++ b/plugins/vacation-calendar/dev/__fixtures__/mockEntities.json @@ -0,0 +1,82 @@ +[ + { + "apiVersion": "backstage.io/v1beta1", + "kind": "User", + "metadata": { + "namespace": "default", + "annotations": { + "manager": "bossman" + }, + "name": "sophie", + "description": "Software developer" + }, + "spec": { + "profile": { + "displayName": "Sophie", + "email": "sophie@mail.com", + "picture": "https://api.dicebear.com/7.x/avataaars/svg?seed=Sophie" + }, + "memberOf": ["group:default/backstage"] + } + }, + { + "apiVersion": "backstage.io/v1beta1", + "kind": "User", + "metadata": { + "namespace": "default", + "annotations": { + "manager": "bossman" + }, + "name": "rascal", + "description": "Expert Engineer" + }, + "spec": { + "profile": { + "displayName": "Rascal", + "email": "rascal@mail.com", + "picture": "https://api.dicebear.com/7.x/avataaars/svg?seed=Rascal" + }, + "memberOf": ["group:default/backstage"] + } + }, + { + "apiVersion": "backstage.io/v1beta1", + "kind": "User", + "metadata": { + "namespace": "default", + "annotations": { + "manager": "bossman" + }, + "name": "buster", + "description": "Expert Engineer" + }, + "spec": { + "profile": { + "displayName": "Buster", + "email": "buster@mail.com", + "picture": "https://api.dicebear.com/7.x/avataaars/svg?seed=Buster" + }, + "memberOf": ["group:default/backstage"] + } + }, + { + "apiVersion": "backstage.io/v1beta1", + "kind": "User", + "metadata": { + "namespace": "default", + "annotations": { + "manager": "bossman" + }, + "name": "jasmine", + "description": "Expert Engineer" + }, + "spec": { + "profile": { + "displayName": "Jasmine", + "email": "jasmine@mail.com", + "picture": "https://api.dicebear.com/7.x/avataaars/svg?seed=Jasmine" + }, + "memberOf": ["group:default/backstage"] + } + } +] diff --git a/plugins/vacation-calendar/dev/__fixtures__/mockEntity.json b/plugins/vacation-calendar/dev/__fixtures__/mockEntity.json new file mode 100644 index 00000000..4b262dfe --- /dev/null +++ b/plugins/vacation-calendar/dev/__fixtures__/mockEntity.json @@ -0,0 +1,20 @@ +{ + "apiVersion": "backstage.io/v1beta1", + "kind": "User", + "metadata": { + "namespace": "default", + "annotations": { + "manager": "bossman" + }, + "name": "sophie", + "description": "Software developer" + }, + "spec": { + "profile": { + "displayName": "Sophie", + "email": "sophie@mail.com", + "picture": "https://api.dicebear.com/7.x/avataaars/svg?seed=Sophie" + }, + "memberOf": ["group:default/backstage"] + } +} diff --git a/plugins/vacation-calendar/dev/__fixtures__/mockVacationCalendarApi.ts b/plugins/vacation-calendar/dev/__fixtures__/mockVacationCalendarApi.ts new file mode 100644 index 00000000..e1e1d3bd --- /dev/null +++ b/plugins/vacation-calendar/dev/__fixtures__/mockVacationCalendarApi.ts @@ -0,0 +1,13 @@ +import schedule from './schedule.json'; +import availability from './availability.json'; +import { ScheduleInformation, MicrosoftCalendar } from '../../src/api/types'; +import { VacationCalendarApi } from '../../src/api'; + +export const mockVacationCalendarApi: VacationCalendarApi = { + async getCalendars() { + return Promise.resolve(schedule as MicrosoftCalendar[]); + }, + async getAvailability() { + return Promise.resolve(availability as ScheduleInformation[]); + }, +}; diff --git a/plugins/vacation-calendar/dev/__fixtures__/schedule.json b/plugins/vacation-calendar/dev/__fixtures__/schedule.json new file mode 100644 index 00000000..895bced8 --- /dev/null +++ b/plugins/vacation-calendar/dev/__fixtures__/schedule.json @@ -0,0 +1,23 @@ +[ + { + "@odata.context": "https://graph.microsoft.com/v1.0/$metadata#me/calendars/$entity", + "@odata.id": "https://graph.microsoft.com/v1.0/users('ddfcd489-628b-40d7-b48b-57002df800e5@1717622f-1d94-4d0c-9d74-709fad664b77')/calendars('AAMkAGI2TGuLAAA=')", + "id": "AAMkAGI2TGuLAAA=", + "name": "Calendar", + "color": "auto", + "isDefaultCalendar": false, + "changeKey": "nfZyf7VcrEKLNoU37KWlkQAAA0x0+w==", + "canShare": true, + "canViewPrivateItems": true, + "hexColor": "", + "canEdit": true, + "allowedOnlineMeetingProviders": ["teamsForBusiness"], + "defaultOnlineMeetingProvider": "teamsForBusiness", + "isTallyingResponses": true, + "isRemovable": false, + "owner": { + "name": "Sophie", + "address": "sophie@adatum.onmicrosoft.com" + } + } +] diff --git a/plugins/vacation-calendar/dev/index.tsx b/plugins/vacation-calendar/dev/index.tsx new file mode 100644 index 00000000..5d26dfa9 --- /dev/null +++ b/plugins/vacation-calendar/dev/index.tsx @@ -0,0 +1,55 @@ +import { createDevApp } from '@backstage/dev-utils'; +import { + CatalogApi, + catalogApiRef, + EntityProvider, +} from '@backstage/plugin-catalog-react'; +import React from 'react'; +import { vacationCalendarPlugin } from '../src/plugin'; +import { fetchApiRef, microsoftAuthApiRef } from '@backstage/core-plugin-api'; +import { GetEntitiesResponse } from '@backstage/catalog-client'; +import { UserEntity } from '@backstage/catalog-model'; +import { VacationCalendarPage } from '@axis-backstage/plugin-vacation-calendar'; +import mockEntity from './__fixtures__/mockEntity.json'; +import mockEntities from './__fixtures__/mockEntities.json'; +import { vacationCalendarApiRef } from '../src/api'; +import { mockVacationCalendarApi } from './__fixtures__/mockVacationCalendarApi'; + +const catalogApi: Partial = { + async getEntities(): Promise { + return { items: mockEntities as UserEntity[] }; + }, +}; + +createDevApp() + .registerPlugin(vacationCalendarPlugin) + .registerApi({ + api: vacationCalendarApiRef, + deps: { fetchApi: fetchApiRef }, + factory: () => mockVacationCalendarApi, + }) + .registerApi({ + api: microsoftAuthApiRef, + deps: {}, + factory: () => + ({ + async getAccessToken() { + return Promise.resolve('token'); + }, + } as unknown as typeof microsoftAuthApiRef.T), + }) + .registerApi({ + api: catalogApiRef, + deps: {}, + factory: () => catalogApi as CatalogApi, + }) + .addPage({ + element: ( + + + + ), + title: 'Root Page', + path: '/vacation-calender', + }) + .render(); diff --git a/plugins/vacation-calendar/media/month-dark.png b/plugins/vacation-calendar/media/month-dark.png new file mode 100644 index 00000000..cbf5c78f Binary files /dev/null and b/plugins/vacation-calendar/media/month-dark.png differ diff --git a/plugins/vacation-calendar/media/month-light.png b/plugins/vacation-calendar/media/month-light.png new file mode 100644 index 00000000..0d209fe3 Binary files /dev/null and b/plugins/vacation-calendar/media/month-light.png differ diff --git a/plugins/vacation-calendar/media/year-dark.png b/plugins/vacation-calendar/media/year-dark.png new file mode 100644 index 00000000..fb03afb3 Binary files /dev/null and b/plugins/vacation-calendar/media/year-dark.png differ diff --git a/plugins/vacation-calendar/media/year-light.png b/plugins/vacation-calendar/media/year-light.png new file mode 100644 index 00000000..7e94dd18 Binary files /dev/null and b/plugins/vacation-calendar/media/year-light.png differ diff --git a/plugins/vacation-calendar/package.json b/plugins/vacation-calendar/package.json new file mode 100644 index 00000000..9598f2c4 --- /dev/null +++ b/plugins/vacation-calendar/package.json @@ -0,0 +1,65 @@ +{ + "name": "@axis-backstage/plugin-vacation-calendar", + "version": "0.1.0", + "main": "src/index.ts", + "types": "src/index.ts", + "license": "Apache-2.0", + "private": true, + "publishConfig": { + "access": "public", + "main": "dist/index.esm.js", + "types": "dist/index.d.ts" + }, + "backstage": { + "role": "frontend-plugin" + }, + "sideEffects": false, + "scripts": { + "start": "backstage-cli package start", + "build": "backstage-cli package build", + "lint": "backstage-cli package lint", + "test": "backstage-cli package test", + "clean": "backstage-cli package clean", + "prepack": "backstage-cli package prepack", + "postpack": "backstage-cli package postpack" + }, + "dependencies": { + "@backstage/catalog-model": "^1.5.0", + "@backstage/core-components": "^0.14.8", + "@backstage/core-plugin-api": "^1.9.3", + "@backstage/errors": "^1.2.4", + "@backstage/plugin-catalog-react": "^1.12.1", + "@backstage/theme": "^0.5.6", + "@date-io/luxon": "3.0.0", + "@material-ui/pickers": "^3.3.11", + "@microsoft/microsoft-graph-types": "^2.40.0", + "@mui/lab": "^5.0.0-alpha.170", + "@mui/material": "^5.15.20", + "@mui/x-date-pickers": "^7.7.0", + "@tanstack/react-query": "4.29.1", + "interactjs": "^1.10.27", + "lodash": "^4.17.21", + "luxon": "^3.4.4", + "react-calendar-timeline": "^0.28.0", + "react-use": "^17.5.0" + }, + "peerDependencies": { + "react": "^16.13.1 || ^17.0.0 || ^18.0.0" + }, + "devDependencies": { + "@backstage/catalog-client": "^1.6.5", + "@backstage/cli": "^0.26.7", + "@backstage/core-app-api": "^1.12.6", + "@backstage/dev-utils": "^1.0.33", + "@backstage/test-utils": "^1.5.6", + "@testing-library/jest-dom": "^5.10.1", + "@testing-library/react": "^16.0.0", + "@testing-library/user-event": "^14.5.2", + "@types/lodash": "^4.17.5", + "@types/luxon": "^3.4.2", + "@types/react-calendar-timeline": "^0.28.6" + }, + "files": [ + "dist" + ] +} diff --git a/plugins/vacation-calendar/src/api/VacationCalendarApi.ts b/plugins/vacation-calendar/src/api/VacationCalendarApi.ts new file mode 100644 index 00000000..ed61bed8 --- /dev/null +++ b/plugins/vacation-calendar/src/api/VacationCalendarApi.ts @@ -0,0 +1,36 @@ +import { createApiRef } from '@backstage/core-plugin-api'; +import type { MicrosoftCalendar, ScheduleInformation } from './types'; + +/** + * The apiref for the VacationCalendar plugin. + * + * @public + */ +export const vacationCalendarApiRef = createApiRef({ + id: 'plugin.vacation-calendar.service', +}); + +/** + * The definition for the VacationCalendar api. + * + * @public + */ +export interface VacationCalendarApi { + /** + * Fetches schedule items for users + */ + getCalendars(): Promise; + /** + * Fetches Microsoft calendars + */ + getAvailability( + params: { + users: string[]; + startDateTime: string; + endDateTime: string; + }, + headers: { + [x: string]: any; + }, + ): Promise; +} diff --git a/plugins/vacation-calendar/src/api/VacationCalendarClient.ts b/plugins/vacation-calendar/src/api/VacationCalendarClient.ts new file mode 100644 index 00000000..7932bb9b --- /dev/null +++ b/plugins/vacation-calendar/src/api/VacationCalendarClient.ts @@ -0,0 +1,142 @@ +import { OAuthApi, FetchApi } from '@backstage/core-plugin-api'; +import { ResponseError } from '@backstage/errors'; +import type { ScheduleInformation, MicrosoftCalendar } from './types'; + +const getAvailabilityBody = ({ + users, + startDateTime, + endDateTime, +}: { + users: string[]; + startDateTime: string; + endDateTime: string; +}) => ({ + Schedules: users, + StartTime: { + dateTime: startDateTime, + timeZone: 'Europe/Paris', + }, + EndTime: { + dateTime: endDateTime, + timeZone: 'Europe/Paris', + }, + availabilityViewInterval: '1440', +}); + +/** + * The client implementation for the frontend api. + * + * @public + */ +export class VacationCalendarApiClient { + private readonly authApi: OAuthApi; + private readonly fetchApi: FetchApi; + + constructor(options: { authApi: OAuthApi; fetchApi: FetchApi }) { + this.authApi = options.authApi; + this.fetchApi = options.fetchApi; + } + + private async get( + path: string, + params: { [key in string]: any } = {}, + headers?: any, + ): Promise { + const query = new URLSearchParams(params); + const url = new URL( + `${path}?${query.toString()}`, + 'https://graph.microsoft.com', + ); + const token = await this.authApi.getAccessToken(); + let temp: any = {}; + + if (headers && typeof headers === 'object') { + temp = { + ...headers, + }; + } + + if (token) { + temp.Authorization = `Bearer ${token}`; + } + + const response = await this.fetchApi.fetch(url.toString(), { + headers: temp, + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return response.json() as Promise; + } + + private async post(path: string, body: any, headers?: any): Promise { + const url = new URL(path, 'https://graph.microsoft.com'); + const token = await this.authApi.getAccessToken(); + let temp: any = {}; + + if (headers && typeof headers === 'object') { + temp = { + ...headers, + }; + } + + if (token) { + temp.Authorization = `Bearer ${token}`; + } + temp['Content-type'] = 'application/json'; + + const response = await this.fetchApi.fetch(url.toString(), { + method: 'POST', + headers: temp, + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw await ResponseError.fromResponse(response); + } + + return response.json() as Promise; + } + + /** + * Fetches Microsoft calendars + * + * @returns the MicrosoftValendar objects + */ + async getCalendars(): Promise { + const data = await this.get<{ + id: string; + value: MicrosoftCalendar[]; + }>('v1.0/me/calendars'); + return data.value; + } + + /** + * Fetches schedule items for users + * + * @param users - list of users + * @param startDateTime - string with start date + * @param endDateTime - string with end date + * @returns Microsoft ScheduleInformation items + */ + async getAvailability( + params: { + users: string[]; + startDateTime: string; + endDateTime: string; + }, + headers: { [key in string]: any }, + ): Promise { + const data = await this.post<{ + id: string; + value: ScheduleInformation[]; + }>( + `v1.0/me/calendar/getschedule`, + getAvailabilityBody({ ...params }), + headers, + ); + return data.value; + } +} diff --git a/plugins/vacation-calendar/src/api/index.ts b/plugins/vacation-calendar/src/api/index.ts new file mode 100644 index 00000000..78e947a6 --- /dev/null +++ b/plugins/vacation-calendar/src/api/index.ts @@ -0,0 +1,3 @@ +export type { VacationCalendarApi } from './VacationCalendarApi'; +export { vacationCalendarApiRef } from './VacationCalendarApi'; +export { VacationCalendarApiClient } from './VacationCalendarClient'; diff --git a/plugins/vacation-calendar/src/api/types.ts b/plugins/vacation-calendar/src/api/types.ts new file mode 100644 index 00000000..23bac7ed --- /dev/null +++ b/plugins/vacation-calendar/src/api/types.ts @@ -0,0 +1,6 @@ +import { + Calendar as MicrosoftCalendar, + ScheduleInformation, +} from '@microsoft/microsoft-graph-types'; + +export type { MicrosoftCalendar, ScheduleInformation }; diff --git a/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx b/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx new file mode 100644 index 00000000..f414f2dc --- /dev/null +++ b/plugins/vacation-calendar/src/components/CalendarCard/CalendarCard.tsx @@ -0,0 +1,269 @@ +import React, { ReactNode, useState } from 'react'; +import useAsync from 'react-use/lib/useAsync'; +import Timeline, { + TimelineHeaders, + SidebarHeader, + DateHeader, + IntervalRenderer, +} from 'react-calendar-timeline'; +import { DateTime } from 'luxon'; +import { useApi } from '@backstage/core-plugin-api'; +import { useEntity, catalogApiRef } from '@backstage/plugin-catalog-react'; +import { + Content, + ContentHeader, + SupportButton, + Link, + ErrorPanel, + Progress, + Avatar, +} from '@backstage/core-components'; +import { DateSelector } from '../DateSelector'; +import { getGroups, getScheduleItems } from './lib'; +import { fetchGroupEntities, fetchUserEntities } from './fetch'; +import { useAvailability } from '../../hooks/useAvailibility'; +import { useSignIn } from '../../hooks'; +import { SignInContent } from '../SignInContent'; +import MuiLink from '@mui/material/Link'; +import Button from '@mui/material/Button'; +import Box from '@mui/material/Box'; +import Typography from '@mui/material/Typography'; +import Stack from '@mui/material/Stack'; + +import { Theme } from '@mui/material/styles'; + +const DEFAULT_NUM_DAYS = 60; + +const IntervalDateHeader = (props?: IntervalRenderer): ReactNode => { + return ( + theme.palette.text.primary, + position: 'absolute', + width: props?.intervalContext.interval.labelWidth, + left: props?.intervalContext.interval.left, + }} + onClick={props?.getIntervalProps().onClick} + > + {props?.intervalContext.intervalText} + + ); +}; + +export const CalendarCard = () => { + const { entity } = useEntity(); + const catalogApi = useApi(catalogApiRef); + + const [startDate, setStartDate] = useState(DateTime.now()); + const [endDate, setEndDate] = useState( + DateTime.now().endOf('day').plus({ days: DEFAULT_NUM_DAYS }), + ); + + const { isSignedIn, isInitialized, signIn } = useSignIn(); + + useAsync(async () => signIn(true), [signIn]); + + const isUserEntity = + entity.kind.toLowerCase() === 'user' && entity.metadata.name; + + const { value: users } = useAsync(async () => { + return isUserEntity + ? fetchUserEntities(catalogApi, entity) + : fetchGroupEntities(catalogApi, entity); + }, [catalogApi, entity]); + + const { + availablity, + error, + isLoading: isAvailabilityLoading, + isFetching: isAvailabilityFetching, + hasNextPage, + fetchNextPage, + } = useAvailability(users, startDate, endDate, isSignedIn); + + const showLoader = + isAvailabilityLoading || isAvailabilityFetching || !isInitialized; + + const groups = getGroups(availablity, isUserEntity, users); + const scheduleItems = getScheduleItems(availablity); + + if (users?.length === 0) { + return ( + + ); + } + if (error instanceof Error) { + return ( + + ); + } + + return ( + + + + + How it works + + 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. + + Limitations + + Due to limitations in the Microsoft Graph API the maximum range of + the dates is {DEFAULT_NUM_DAYS} days. + + + + + + + + + { + if (d instanceof DateTime) { + const { days } = endDate.diff(d, 'days').toObject(); + if (days && Math.abs(days) > DEFAULT_NUM_DAYS) { + setEndDate(d.endOf('day').plus({ days: DEFAULT_NUM_DAYS })); + } + setStartDate(d); + } + }} + initalDate={startDate} + label="Start Date" + /> + + + { + if (d instanceof DateTime) { + const { days } = startDate.diff(d, 'days').toObject(); + if (days && Math.abs(days) > DEFAULT_NUM_DAYS) { + setStartDate( + d.endOf('day').minus({ days: DEFAULT_NUM_DAYS }), + ); + } + setEndDate(d as DateTime); + } + }} + initalDate={endDate} + label="End Date" + /> + + + + + + + {showLoader && ( + + + + )} + {!isSignedIn && isInitialized && ( + + signIn(false)} /> + + )} + {!isAvailabilityLoading && isSignedIn && ( + + { + return ( + +
+
+ +
+
+ {group.title} +
+
+ + ); + }} + items={scheduleItems as any} + itemTouchSendsClick={false} + defaultTimeStart={startDate.toJSDate()} + defaultTimeEnd={endDate.toJSDate()} + > + + + {({ getRootProps }) => { + return
; + }} + + + + + + + )} + + + ); +}; diff --git a/plugins/vacation-calendar/src/components/CalendarCard/fetch.ts b/plugins/vacation-calendar/src/components/CalendarCard/fetch.ts new file mode 100644 index 00000000..57cbd085 --- /dev/null +++ b/plugins/vacation-calendar/src/components/CalendarCard/fetch.ts @@ -0,0 +1,49 @@ +import { + DEFAULT_NAMESPACE, + Entity, + UserEntity, + stringifyEntityRef, +} from '@backstage/catalog-model'; +import { CatalogApi } from '@backstage/plugin-catalog-react'; + +const MANAGER_ANNOTATION = 'manager'; + +export const fetchUserEntities = async ( + catalogApi: CatalogApi, + entity: Entity, +) => { + if (!entity.metadata?.annotations?.[MANAGER_ANNOTATION]) { + return [entity as UserEntity]; + } + + const filter = { + kind: 'User', + [`metadata.annotations.${MANAGER_ANNOTATION}`]: + entity.metadata.annotations[MANAGER_ANNOTATION], + }; + + const { items } = await catalogApi.getEntities({ filter }); + return items as UserEntity[]; +}; + +export const fetchGroupEntities = async ( + catalogApi: CatalogApi, + entity: Entity, +) => { + const { + metadata: { name: groupName, namespace = DEFAULT_NAMESPACE }, + } = entity; + + const filter = { + kind: 'User', + 'relations.memberof': [ + stringifyEntityRef({ + kind: 'group', + namespace: namespace.toLocaleLowerCase('en-US'), + name: groupName.toLocaleLowerCase('en-US'), + }), + ], + }; + + return (await catalogApi.getEntities({ filter })).items as UserEntity[]; +}; diff --git a/plugins/vacation-calendar/src/components/CalendarCard/index.ts b/plugins/vacation-calendar/src/components/CalendarCard/index.ts new file mode 100644 index 00000000..0f8ec84c --- /dev/null +++ b/plugins/vacation-calendar/src/components/CalendarCard/index.ts @@ -0,0 +1 @@ +export { CalendarCard } from './CalendarCard'; diff --git a/plugins/vacation-calendar/src/components/CalendarCard/lib.ts b/plugins/vacation-calendar/src/components/CalendarCard/lib.ts new file mode 100644 index 00000000..37ed8c41 --- /dev/null +++ b/plugins/vacation-calendar/src/components/CalendarCard/lib.ts @@ -0,0 +1,75 @@ +import { TimelineItemBase } from 'react-calendar-timeline'; +import { UserEntity, UserEntityV1alpha1 } from '@backstage/catalog-model'; +import { ScheduleInformation } from '@microsoft/microsoft-graph-types'; +import { InfiniteData } from '@tanstack/react-query'; +import { head } from 'lodash'; +import { DateTime } from 'luxon'; + +const isTimeLineItem = ( + item: TimelineItemBase | undefined, +): item is TimelineItemBase => { + return item !== undefined; +}; + +const getFullName = (users: UserEntity[], userId: string) => { + const name = head(userId.split('@')); + const user = users.find(u => u.metadata.name === name); + + if (user) return user.spec.profile?.displayName; + return userId; +}; + +const getUser = (users: UserEntity[], userId: string) => { + const name = head(userId.split('@')); + const user = users.find(u => u.metadata.name === name); + + return user; +}; + +export const getScheduleItems = ( + availablity: InfiniteData | undefined, +) => { + return availablity + ? availablity?.pages + .flatMap(p => p) + // @ts-ignore + .flatMap((a, i) => + // @ts-ignore + a.scheduleItems + // @ts-ignore + ?.filter(scheduleItem => scheduleItem.status === 'oof') + // @ts-ignore + .map(s => ({ + id: Date.now().toString(36) + Math.random().toString(36).slice(2), + group: i, + start_time: DateTime.fromISO(s.start?.dateTime!), + end_time: DateTime.fromISO(s.end?.dateTime!), + canMove: false, + canResize: false, + })), + ) + .filter(isTimeLineItem) + : []; +}; + +export const getGroups = ( + availablity: InfiniteData | undefined, + isUserEntity: string | false, + users: UserEntityV1alpha1[] | undefined, +) => { + return availablity + ? availablity.pages + .flatMap(p => p) + .map((a, i) => ({ + id: i, + // @ts-ignore + highlight: + isUserEntity && + a.scheduleId && + isUserEntity === head(a.scheduleId.split('@')), + title: getFullName(users || [], a.scheduleId || ''), + entity: getUser(users || [], a.scheduleId || ''), + height: 30, + })) + : []; +}; diff --git a/plugins/vacation-calendar/src/components/DateSelector/DateSelector.tsx b/plugins/vacation-calendar/src/components/DateSelector/DateSelector.tsx new file mode 100644 index 00000000..be2c9739 --- /dev/null +++ b/plugins/vacation-calendar/src/components/DateSelector/DateSelector.tsx @@ -0,0 +1,32 @@ +import React from 'react'; +import { DateTime } from 'luxon/src/datetime'; +import { AdapterLuxon } from '@mui/x-date-pickers/AdapterLuxon'; +import FormControl from '@mui/material/FormControl'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { DesktopDatePicker } from '@mui/x-date-pickers/DesktopDatePicker'; + +type DateSelectorProps = { + label: string; + initalDate: DateTime; + onDateChange: (date: DateTime | null | string | undefined) => void; +}; + +export const DateSelector = ({ + label, + initalDate, + onDateChange, +}: DateSelectorProps) => { + return ( + + + + + + ); +}; diff --git a/plugins/vacation-calendar/src/components/DateSelector/index.ts b/plugins/vacation-calendar/src/components/DateSelector/index.ts new file mode 100644 index 00000000..f02295b3 --- /dev/null +++ b/plugins/vacation-calendar/src/components/DateSelector/index.ts @@ -0,0 +1 @@ +export { DateSelector } from './DateSelector'; diff --git a/plugins/vacation-calendar/src/components/SignInContent/SignInContent.tsx b/plugins/vacation-calendar/src/components/SignInContent/SignInContent.tsx new file mode 100644 index 00000000..49beb26e --- /dev/null +++ b/plugins/vacation-calendar/src/components/SignInContent/SignInContent.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import Box from '@mui/material/Box'; +import Button from '@mui/material/Button'; + +type Props = { + handleAuthClick: React.MouseEventHandler; +}; + +export const SignInContent = ({ handleAuthClick }: Props) => { + return ( + + + + + + ); +}; diff --git a/plugins/vacation-calendar/src/components/SignInContent/index.ts b/plugins/vacation-calendar/src/components/SignInContent/index.ts new file mode 100644 index 00000000..c56a85cf --- /dev/null +++ b/plugins/vacation-calendar/src/components/SignInContent/index.ts @@ -0,0 +1 @@ +export { SignInContent } from './SignInContent'; diff --git a/plugins/vacation-calendar/src/components/VacationCalendar.tsx b/plugins/vacation-calendar/src/components/VacationCalendar.tsx new file mode 100644 index 00000000..e2ed36da --- /dev/null +++ b/plugins/vacation-calendar/src/components/VacationCalendar.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { QueryClient, QueryClientProvider } from '@tanstack/react-query'; +import { CalendarCard } from './CalendarCard'; + +const queryClient = new QueryClient(); + +export const VacationCalendar = () => { + return ( + + + + ); +}; diff --git a/plugins/vacation-calendar/src/components/index.ts b/plugins/vacation-calendar/src/components/index.ts new file mode 100644 index 00000000..c481a302 --- /dev/null +++ b/plugins/vacation-calendar/src/components/index.ts @@ -0,0 +1,2 @@ +export { VacationCalendar } from './VacationCalendar'; +export * from '../api'; diff --git a/plugins/vacation-calendar/src/hooks/index.ts b/plugins/vacation-calendar/src/hooks/index.ts new file mode 100644 index 00000000..b662e3ae --- /dev/null +++ b/plugins/vacation-calendar/src/hooks/index.ts @@ -0,0 +1 @@ +export { useSignIn } from './useSignIn'; diff --git a/plugins/vacation-calendar/src/hooks/useAvailibility.ts b/plugins/vacation-calendar/src/hooks/useAvailibility.ts new file mode 100644 index 00000000..ecfb1b11 --- /dev/null +++ b/plugins/vacation-calendar/src/hooks/useAvailibility.ts @@ -0,0 +1,69 @@ +import { useInfiniteQuery } from '@tanstack/react-query'; +import { UserEntityV1alpha1 } from '@backstage/catalog-model'; +import { useApi } from '@backstage/core-plugin-api'; +import { slice } from 'lodash'; +import { DateTime } from 'luxon'; +import { vacationCalendarApiRef } from '../api'; + +const PAGE_SIZE = 20; + +export const useAvailability = ( + users: UserEntityV1alpha1[] | undefined, + startDate: DateTime, + endDate: DateTime, + isSignedIn: boolean, +) => { + const calendarApi = useApi(vacationCalendarApiRef); + const { + data: availablity, + error, + isLoading: isAvailabilityLoading, + isFetching: isAvailabilityFetching, + hasNextPage, + fetchNextPage, + } = useInfiniteQuery({ + queryFn: ({ pageParam = 0 }) => { + const currentUsers = slice(users || [], pageParam, pageParam + PAGE_SIZE); + + return calendarApi.getAvailability( + { + users: currentUsers?.map(u => u.spec.profile?.email ?? '') || [], + startDateTime: startDate.toISO(), + endDateTime: endDate.toISO(), + }, + { + Prefer: 'outlook.timezone="Europe/Stockholm"', + }, + ); + }, + queryKey: [ + 'calendarAvailability', + users, + startDate.toISO(), + endDate.toISO(), + ], + getNextPageParam: (_, allPages) => { + const numberOfUsers = allPages.flatMap(p => p).length; + if (numberOfUsers === users?.length) { + return undefined; + } + // This will be starting index to fetch the next batch. + return numberOfUsers; + }, + cacheTime: 30000 * 1000, + enabled: isSignedIn && startDate.isValid && endDate.isValid && !!users, + retry: false, + refetchInterval: 30000 * 1000, + refetchIntervalInBackground: false, + refetchOnWindowFocus: false, + refetchOnReconnect: false, + }); + return { + availablity, + error, + isLoading: isAvailabilityLoading, + isFetching: isAvailabilityFetching, + hasNextPage, + fetchNextPage, + }; +}; diff --git a/plugins/vacation-calendar/src/hooks/useSignIn.ts b/plugins/vacation-calendar/src/hooks/useSignIn.ts new file mode 100644 index 00000000..67f98161 --- /dev/null +++ b/plugins/vacation-calendar/src/hooks/useSignIn.ts @@ -0,0 +1,23 @@ +import { useCallback, useState } from 'react'; +import { microsoftAuthApiRef, useApi } from '@backstage/core-plugin-api'; + +export const useSignIn = () => { + const [isSignedIn, setSignedIn] = useState(false); + const [isInitialized, setInitialized] = useState(false); + const authApi = useApi(microsoftAuthApiRef); + + const signIn = useCallback( + async (optional = false) => { + const token = await authApi.getAccessToken('Calendars.Read', { + optional, + instantPopup: !optional, + }); + + setSignedIn(!!token); + setInitialized(true); + }, + [authApi, setSignedIn], + ); + + return { isSignedIn, isInitialized, signIn }; +}; diff --git a/plugins/vacation-calendar/src/index.ts b/plugins/vacation-calendar/src/index.ts new file mode 100644 index 00000000..c0d281ba --- /dev/null +++ b/plugins/vacation-calendar/src/index.ts @@ -0,0 +1,6 @@ +/** + * Frontend plugin that fetches and displays out of office events for entities. + * + * @packageDocumentation + */ export { vacationCalendarPlugin, VacationCalendarPage } from './plugin'; +export * from './api'; diff --git a/plugins/vacation-calendar/src/plugin.test.ts b/plugins/vacation-calendar/src/plugin.test.ts new file mode 100644 index 00000000..03087d34 --- /dev/null +++ b/plugins/vacation-calendar/src/plugin.test.ts @@ -0,0 +1,7 @@ +import { vacationCalendarPlugin } from './plugin'; + +describe('vacation-calendar', () => { + it('should export plugin', () => { + expect(vacationCalendarPlugin).toBeDefined(); + }); +}); diff --git a/plugins/vacation-calendar/src/plugin.ts b/plugins/vacation-calendar/src/plugin.ts new file mode 100644 index 00000000..0b907f43 --- /dev/null +++ b/plugins/vacation-calendar/src/plugin.ts @@ -0,0 +1,43 @@ +import 'react-calendar-timeline/lib/Timeline.css'; +import { + createApiFactory, + createPlugin, + createRoutableExtension, + fetchApiRef, + microsoftAuthApiRef, +} from '@backstage/core-plugin-api'; +import { rootRouteRef } from './routes'; +import { VacationCalendarApiClient, vacationCalendarApiRef } from './api'; + +/** + * Plugin that provides the VacationCalendar api + * @public */ +export const vacationCalendarPlugin = createPlugin({ + id: 'vacation-calendar', + apis: [ + createApiFactory({ + api: vacationCalendarApiRef, + deps: { + authApi: microsoftAuthApiRef, + fetchApi: fetchApiRef, + }, + factory: ({ authApi, fetchApi }) => + new VacationCalendarApiClient({ authApi, fetchApi }), + }), + ], + routes: { + root: rootRouteRef, + }, +}); + +/** + * Routable extension for VacationCalendarPage + * @public + */ +export const VacationCalendarPage = vacationCalendarPlugin.provide( + createRoutableExtension({ + name: 'VacationCalendarPage', + component: () => import('./components').then(m => m.VacationCalendar), + mountPoint: rootRouteRef, + }), +); diff --git a/plugins/vacation-calendar/src/routes.ts b/plugins/vacation-calendar/src/routes.ts new file mode 100644 index 00000000..fae59d4c --- /dev/null +++ b/plugins/vacation-calendar/src/routes.ts @@ -0,0 +1,5 @@ +import { createRouteRef } from '@backstage/core-plugin-api'; + +export const rootRouteRef = createRouteRef({ + id: 'vacation-calendar', +}); diff --git a/plugins/vacation-calendar/src/setupTests.ts b/plugins/vacation-calendar/src/setupTests.ts new file mode 100644 index 00000000..7b0828bf --- /dev/null +++ b/plugins/vacation-calendar/src/setupTests.ts @@ -0,0 +1 @@ +import '@testing-library/jest-dom'; diff --git a/yarn.lock b/yarn.lock index 772573e7..62846f0a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1611,6 +1611,44 @@ __metadata: languageName: unknown linkType: soft +"@axis-backstage/plugin-vacation-calendar@npm:^0.1.0, @axis-backstage/plugin-vacation-calendar@workspace:plugins/vacation-calendar": + version: 0.0.0-use.local + resolution: "@axis-backstage/plugin-vacation-calendar@workspace:plugins/vacation-calendar" + dependencies: + "@backstage/catalog-client": "npm:^1.6.5" + "@backstage/catalog-model": "npm:^1.5.0" + "@backstage/cli": "npm:^0.26.7" + "@backstage/core-app-api": "npm:^1.12.6" + "@backstage/core-components": "npm:^0.14.8" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/dev-utils": "npm:^1.0.33" + "@backstage/errors": "npm:^1.2.4" + "@backstage/plugin-catalog-react": "npm:^1.12.1" + "@backstage/test-utils": "npm:^1.5.6" + "@backstage/theme": "npm:^0.5.6" + "@date-io/luxon": "npm:3.0.0" + "@material-ui/pickers": "npm:^3.3.11" + "@microsoft/microsoft-graph-types": "npm:^2.40.0" + "@mui/lab": "npm:^5.0.0-alpha.170" + "@mui/material": "npm:^5.15.20" + "@mui/x-date-pickers": "npm:^7.7.0" + "@tanstack/react-query": "npm:4.29.1" + "@testing-library/jest-dom": "npm:^5.10.1" + "@testing-library/react": "npm:^16.0.0" + "@testing-library/user-event": "npm:^14.5.2" + "@types/lodash": "npm:^4.17.5" + "@types/luxon": "npm:^3.4.2" + "@types/react-calendar-timeline": "npm:^0.28.6" + interactjs: "npm:^1.10.27" + lodash: "npm:^4.17.21" + luxon: "npm:^3.4.4" + react-calendar-timeline: "npm:^0.28.0" + react-use: "npm:^17.5.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + languageName: unknown + linkType: soft + "@azure/abort-controller@npm:^1.0.0": version: 1.1.0 resolution: "@azure/abort-controller@npm:1.1.0" @@ -3218,6 +3256,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.24.6, @babel/runtime@npm:^7.24.7": + version: 7.24.7 + resolution: "@babel/runtime@npm:7.24.7" + dependencies: + regenerator-runtime: "npm:^0.14.0" + checksum: b6fa3ec61a53402f3c1d75f4d808f48b35e0dfae0ec8e2bb5c6fc79fb95935da75766e0ca534d0f1c84871f6ae0d2ebdd950727cfadb745a2cdbef13faef5513 + languageName: node + linkType: hard + "@babel/template@npm:^7.22.15, @babel/template@npm:^7.22.5, @babel/template@npm:^7.3.3": version: 7.22.15 resolution: "@babel/template@npm:7.22.15" @@ -3303,6 +3350,25 @@ __metadata: languageName: node linkType: hard +"@backstage/app-defaults@npm:^1.5.6": + version: 1.5.6 + resolution: "@backstage/app-defaults@npm:1.5.6" + dependencies: + "@backstage/core-app-api": "npm:^1.12.6" + "@backstage/core-components": "npm:^0.14.8" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/plugin-permission-react": "npm:^0.4.23" + "@backstage/theme": "npm:^0.5.6" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 400949fe52f5735395b0916637d99eb7bca717804aa86a83d26a82f4dd033f6a29e2271ac775691f37eb8de421828cc1f07102574d36a65a036b05497ab06c58 + languageName: node + linkType: hard + "@backstage/backend-app-api@npm:^0.7.3": version: 0.7.5 resolution: "@backstage/backend-app-api@npm:0.7.5" @@ -3578,6 +3644,13 @@ __metadata: languageName: node linkType: hard +"@backstage/cli-common@npm:^0.1.14": + version: 0.1.14 + resolution: "@backstage/cli-common@npm:0.1.14" + checksum: d2290a78487add56cee15d15b60d452b118bc649f7f59e158817144dab66b7cec065ccb8af00d067fa20284bffec544f7f156b1e65ac509ae809a796ddbe78cb + languageName: node + linkType: hard + "@backstage/cli-node@npm:^0.2.5": version: 0.2.5 resolution: "@backstage/cli-node@npm:0.2.5" @@ -3594,6 +3667,22 @@ __metadata: languageName: node linkType: hard +"@backstage/cli-node@npm:^0.2.6": + version: 0.2.6 + resolution: "@backstage/cli-node@npm:0.2.6" + dependencies: + "@backstage/cli-common": "npm:^0.1.14" + "@backstage/errors": "npm:^1.2.4" + "@backstage/types": "npm:^1.1.1" + "@manypkg/get-packages": "npm:^1.1.3" + "@yarnpkg/parsers": "npm:^3.0.0" + fs-extra: "npm:^11.2.0" + semver: "npm:^7.5.3" + zod: "npm:^3.22.4" + checksum: f1f959ce5958182d0f8e64580e1c3db20f5866a26c8ac2c02975bc0b14a4d96ec1b018681bcce1e6cb500549e6c5bab9c7e76c302f17213cfb2a20947d007862 + languageName: node + linkType: hard + "@backstage/cli@npm:^0.26.6": version: 0.26.6 resolution: "@backstage/cli@npm:0.26.6" @@ -3729,6 +3818,141 @@ __metadata: languageName: node linkType: hard +"@backstage/cli@npm:^0.26.7": + version: 0.26.9 + resolution: "@backstage/cli@npm:0.26.9" + dependencies: + "@backstage/catalog-model": "npm:^1.5.0" + "@backstage/cli-common": "npm:^0.1.14" + "@backstage/cli-node": "npm:^0.2.6" + "@backstage/config": "npm:^1.2.0" + "@backstage/config-loader": "npm:^1.8.1" + "@backstage/errors": "npm:^1.2.4" + "@backstage/eslint-plugin": "npm:^0.1.8" + "@backstage/integration": "npm:^1.12.0" + "@backstage/release-manifests": "npm:^0.0.11" + "@backstage/types": "npm:^1.1.1" + "@manypkg/get-packages": "npm:^1.1.3" + "@octokit/graphql": "npm:^5.0.0" + "@octokit/graphql-schema": "npm:^13.7.0" + "@octokit/oauth-app": "npm:^4.2.0" + "@octokit/request": "npm:^6.0.0" + "@pmmmwh/react-refresh-webpack-plugin": "npm:^0.5.7" + "@rollup/plugin-commonjs": "npm:^25.0.0" + "@rollup/plugin-json": "npm:^6.0.0" + "@rollup/plugin-node-resolve": "npm:^15.0.0" + "@rollup/plugin-yaml": "npm:^4.0.0" + "@spotify/eslint-config-base": "npm:^15.0.0" + "@spotify/eslint-config-react": "npm:^15.0.0" + "@spotify/eslint-config-typescript": "npm:^15.0.0" + "@sucrase/webpack-loader": "npm:^2.0.0" + "@svgr/core": "npm:6.5.x" + "@svgr/plugin-jsx": "npm:6.5.x" + "@svgr/plugin-svgo": "npm:6.5.x" + "@svgr/rollup": "npm:6.5.x" + "@svgr/webpack": "npm:6.5.x" + "@swc/core": "npm:^1.3.46" + "@swc/helpers": "npm:^0.5.0" + "@swc/jest": "npm:^0.2.22" + "@types/jest": "npm:^29.5.11" + "@types/webpack-env": "npm:^1.15.2" + "@typescript-eslint/eslint-plugin": "npm:^6.12.0" + "@typescript-eslint/parser": "npm:^6.7.2" + "@yarnpkg/lockfile": "npm:^1.1.0" + "@yarnpkg/parsers": "npm:^3.0.0" + bfj: "npm:^8.0.0" + buffer: "npm:^6.0.3" + chalk: "npm:^4.0.0" + chokidar: "npm:^3.3.1" + commander: "npm:^12.0.0" + cross-fetch: "npm:^4.0.0" + cross-spawn: "npm:^7.0.3" + css-loader: "npm:^6.5.1" + ctrlc-windows: "npm:^2.1.0" + diff: "npm:^5.0.0" + esbuild: "npm:^0.20.0" + esbuild-loader: "npm:^4.0.0" + eslint: "npm:^8.6.0" + eslint-config-prettier: "npm:^9.0.0" + eslint-formatter-friendly: "npm:^7.0.0" + eslint-plugin-deprecation: "npm:^2.0.0" + eslint-plugin-import: "npm:^2.25.4" + eslint-plugin-jest: "npm:^27.0.0" + eslint-plugin-jsx-a11y: "npm:^6.5.1" + eslint-plugin-react: "npm:^7.28.0" + eslint-plugin-react-hooks: "npm:^4.3.0" + eslint-plugin-unused-imports: "npm:^3.0.0" + eslint-webpack-plugin: "npm:^4.0.0" + express: "npm:^4.17.1" + fork-ts-checker-webpack-plugin: "npm:^9.0.0" + fs-extra: "npm:^11.2.0" + git-url-parse: "npm:^14.0.0" + glob: "npm:^7.1.7" + global-agent: "npm:^3.0.0" + handlebars: "npm:^4.7.3" + html-webpack-plugin: "npm:^5.3.1" + inquirer: "npm:^8.2.0" + jest: "npm:^29.7.0" + jest-css-modules: "npm:^2.1.0" + jest-environment-jsdom: "npm:^29.0.2" + jest-runtime: "npm:^29.0.2" + json-schema: "npm:^0.4.0" + lodash: "npm:^4.17.21" + mini-css-extract-plugin: "npm:^2.4.2" + minimatch: "npm:^9.0.0" + node-fetch: "npm:^2.6.7" + node-libs-browser: "npm:^2.2.1" + npm-packlist: "npm:^5.0.0" + ora: "npm:^5.3.0" + p-limit: "npm:^3.1.0" + p-queue: "npm:^6.6.2" + pirates: "npm:^4.0.6" + postcss: "npm:^8.1.0" + process: "npm:^0.11.10" + react-dev-utils: "npm:^12.0.0-next.60" + react-refresh: "npm:^0.14.0" + recursive-readdir: "npm:^2.2.2" + replace-in-file: "npm:^7.1.0" + rollup: "npm:^4.0.0" + rollup-plugin-dts: "npm:^6.1.0" + rollup-plugin-esbuild: "npm:^6.1.1" + rollup-plugin-postcss: "npm:^4.0.0" + rollup-pluginutils: "npm:^2.8.2" + run-script-webpack-plugin: "npm:^0.2.0" + semver: "npm:^7.5.3" + style-loader: "npm:^3.3.1" + sucrase: "npm:^3.20.2" + swc-loader: "npm:^0.2.3" + tar: "npm:^6.1.12" + terser-webpack-plugin: "npm:^5.1.3" + util: "npm:^0.12.3" + webpack: "npm:^5.70.0" + webpack-dev-server: "npm:^5.0.0" + webpack-node-externals: "npm:^3.0.0" + yaml: "npm:^2.0.0" + yml-loader: "npm:^2.1.0" + yn: "npm:^4.0.0" + zod: "npm:^3.22.4" + peerDependencies: + "@vitejs/plugin-react": ^4.0.4 + vite: ^4.4.9 + vite-plugin-html: ^3.2.0 + vite-plugin-node-polyfills: ^0.22.0 + peerDependenciesMeta: + "@vitejs/plugin-react": + optional: true + vite: + optional: true + vite-plugin-html: + optional: true + vite-plugin-node-polyfills: + optional: true + bin: + backstage-cli: bin/backstage-cli + checksum: 39af840363e6a13577172315a3555777b7825a8b96987506e317a38473c29bd6df828488476ca8cdbdd2743166b9f29c5bc7ca5d2ee14dcc270509791effdda8 + languageName: node + linkType: hard + "@backstage/config-loader@npm:^1.8.0": version: 1.8.0 resolution: "@backstage/config-loader@npm:1.8.0" @@ -3753,6 +3977,30 @@ __metadata: languageName: node linkType: hard +"@backstage/config-loader@npm:^1.8.1": + version: 1.8.1 + resolution: "@backstage/config-loader@npm:1.8.1" + dependencies: + "@backstage/cli-common": "npm:^0.1.14" + "@backstage/config": "npm:^1.2.0" + "@backstage/errors": "npm:^1.2.4" + "@backstage/types": "npm:^1.1.1" + "@types/json-schema": "npm:^7.0.6" + ajv: "npm:^8.10.0" + chokidar: "npm:^3.5.2" + fs-extra: "npm:^11.2.0" + json-schema: "npm:^0.4.0" + json-schema-merge-allof: "npm:^0.8.1" + json-schema-traverse: "npm:^1.0.0" + lodash: "npm:^4.17.21" + minimist: "npm:^1.2.5" + node-fetch: "npm:^2.6.7" + typescript-json-schema: "npm:^0.63.0" + yaml: "npm:^2.0.0" + checksum: e5e1efb4a4f85f7054641f29b4a07f8a621dffc6e60c3ef43fcd6702122494185067e457cd3e7abb9ecebd6055d2a9154f7a2eb8450621a32f587cc30cde9b6a + languageName: node + linkType: hard + "@backstage/config@npm:^1.2.0": version: 1.2.0 resolution: "@backstage/config@npm:1.2.0" @@ -3788,6 +4036,31 @@ __metadata: languageName: node linkType: hard +"@backstage/core-app-api@npm:^1.12.6": + version: 1.12.6 + resolution: "@backstage/core-app-api@npm:1.12.6" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/types": "npm:^1.1.1" + "@backstage/version-bridge": "npm:^1.0.8" + "@types/prop-types": "npm:^15.7.3" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + history: "npm:^5.0.0" + i18next: "npm:^22.4.15" + lodash: "npm:^4.17.21" + prop-types: "npm:^15.7.2" + react-use: "npm:^17.2.4" + zen-observable: "npm:^0.10.0" + zod: "npm:^3.22.4" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 5b04e1bfd8fbf947279da5e278b17ae5fb0486220fa5e3f03add5052d5f9286add6c7bd155f635fd101454629c9a955f8e458fa31e70b0a0c06147de7f30c6cf + languageName: node + linkType: hard + "@backstage/core-compat-api@npm:^0.2.5": version: 0.2.5 resolution: "@backstage/core-compat-api@npm:0.2.5" @@ -3903,6 +4176,55 @@ __metadata: languageName: node linkType: hard +"@backstage/core-components@npm:^0.14.8": + version: 0.14.8 + resolution: "@backstage/core-components@npm:0.14.8" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/errors": "npm:^1.2.4" + "@backstage/theme": "npm:^0.5.6" + "@backstage/version-bridge": "npm:^1.0.8" + "@date-io/core": "npm:^1.3.13" + "@material-table/core": "npm:^3.1.0" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@react-hookz/web": "npm:^24.0.0" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + "@types/react-sparklines": "npm:^1.7.0" + ansi-regex: "npm:^6.0.1" + classnames: "npm:^2.2.6" + d3-selection: "npm:^3.0.0" + d3-shape: "npm:^3.0.0" + d3-zoom: "npm:^3.0.0" + dagre: "npm:^0.8.5" + linkify-react: "npm:4.1.3" + linkifyjs: "npm:4.1.3" + lodash: "npm:^4.17.21" + pluralize: "npm:^8.0.0" + qs: "npm:^6.9.4" + rc-progress: "npm:3.5.1" + react-helmet: "npm:6.1.0" + react-hook-form: "npm:^7.12.2" + react-idle-timer: "npm:5.7.2" + react-markdown: "npm:^8.0.0" + react-sparklines: "npm:^1.7.0" + react-syntax-highlighter: "npm:^15.4.5" + react-use: "npm:^17.3.2" + react-virtualized-auto-sizer: "npm:^1.0.11" + react-window: "npm:^1.8.6" + remark-gfm: "npm:^3.0.1" + zen-observable: "npm:^0.10.0" + zod: "npm:^3.22.4" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 2bd64c1e39a02210d2eed3d32c09d08ec9db493924820cb86a7c79f24bf2ef5274e33a81b713779fce2b7f2bc14f899869758d38c0af70a9d834b5e34d52b29a + languageName: node + linkType: hard + "@backstage/core-plugin-api@npm:^1.9.2": version: 1.9.2 resolution: "@backstage/core-plugin-api@npm:1.9.2" @@ -3921,6 +4243,24 @@ __metadata: languageName: node linkType: hard +"@backstage/core-plugin-api@npm:^1.9.3": + version: 1.9.3 + resolution: "@backstage/core-plugin-api@npm:1.9.3" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/errors": "npm:^1.2.4" + "@backstage/types": "npm:^1.1.1" + "@backstage/version-bridge": "npm:^1.0.8" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + history: "npm:^5.0.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: e6848394a0eb76f18f41553c358fdc29ae3fd8d489cbd0bd091d0aa5a495c06f95901b2bc99a28be7faae989ea63528c99fcf95977caff996bdfbd9d040deaf1 + languageName: node + linkType: hard + "@backstage/dev-utils@npm:^1.0.32": version: 1.0.32 resolution: "@backstage/dev-utils@npm:1.0.32" @@ -3945,6 +4285,30 @@ __metadata: languageName: node linkType: hard +"@backstage/dev-utils@npm:^1.0.33": + version: 1.0.33 + resolution: "@backstage/dev-utils@npm:1.0.33" + dependencies: + "@backstage/app-defaults": "npm:^1.5.6" + "@backstage/catalog-model": "npm:^1.5.0" + "@backstage/core-app-api": "npm:^1.12.6" + "@backstage/core-components": "npm:^0.14.8" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/integration-react": "npm:^1.1.28" + "@backstage/plugin-catalog-react": "npm:^1.12.1" + "@backstage/theme": "npm:^0.5.6" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + react-use: "npm:^17.2.4" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 837376f207e05b4f092ac48d5af87ea03879ff52da9ec7deac583e63d68efbe484a3cf16386e2b83db22b48e5460c6c1768f5148beba3123590539b2757c94f6 + languageName: node + linkType: hard + "@backstage/e2e-test-utils@npm:^0.1.1": version: 0.1.1 resolution: "@backstage/e2e-test-utils@npm:0.1.1" @@ -4020,6 +4384,26 @@ __metadata: languageName: node linkType: hard +"@backstage/frontend-plugin-api@npm:^0.6.6": + version: 0.6.6 + resolution: "@backstage/frontend-plugin-api@npm:0.6.6" + dependencies: + "@backstage/core-components": "npm:^0.14.8" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/types": "npm:^1.1.1" + "@backstage/version-bridge": "npm:^1.0.8" + "@material-ui/core": "npm:^4.12.4" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + lodash: "npm:^4.17.21" + zod: "npm:^3.22.4" + zod-to-json-schema: "npm:^3.21.4" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 495bc2c2278d2354a449e62636b95d584b92f4113da44efbd431b36a48212a56643b783f4f38cf8b6d646a719d5f9eff6b0269762fec1a4b67ed07fb99c5c292 + languageName: node + linkType: hard + "@backstage/integration-aws-node@npm:^0.1.12": version: 0.1.12 resolution: "@backstage/integration-aws-node@npm:0.1.12" @@ -4071,6 +4455,24 @@ __metadata: languageName: node linkType: hard +"@backstage/integration-react@npm:^1.1.28": + version: 1.1.28 + resolution: "@backstage/integration-react@npm:1.1.28" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/integration": "npm:^1.12.0" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@types/react": "npm:^16.13.1 || ^17.0.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 552b3c09be9d97f771239de4c5b88d22ebad941898467f37b7c18d76b012af428cf6a73c6a5e54dcec3dee88f4b54511b72f479f8d8ee002677c0c386f52b867 + languageName: node + linkType: hard + "@backstage/integration@npm:^1.10.0": version: 1.10.0 resolution: "@backstage/integration@npm:1.10.0" @@ -4105,6 +4507,23 @@ __metadata: languageName: node linkType: hard +"@backstage/integration@npm:^1.12.0": + version: 1.12.0 + resolution: "@backstage/integration@npm:1.12.0" + dependencies: + "@azure/identity": "npm:^4.0.0" + "@backstage/config": "npm:^1.2.0" + "@backstage/errors": "npm:^1.2.4" + "@octokit/auth-app": "npm:^4.0.0" + "@octokit/rest": "npm:^19.0.3" + cross-fetch: "npm:^4.0.0" + git-url-parse: "npm:^14.0.0" + lodash: "npm:^4.17.21" + luxon: "npm:^3.0.0" + checksum: 202a7959d9c750c266788b928defe440d5e312303d575806b812986a1c856366c725faa177843ea25f4c6aeb33bfc33eca629951bf2e968303b9313645a48b10 + languageName: node + linkType: hard + "@backstage/plugin-api-docs@npm:^0.11.5": version: 0.11.5 resolution: "@backstage/plugin-api-docs@npm:0.11.5" @@ -4550,6 +4969,17 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-catalog-common@npm:^1.0.24": + version: 1.0.24 + resolution: "@backstage/plugin-catalog-common@npm:1.0.24" + dependencies: + "@backstage/catalog-model": "npm:^1.5.0" + "@backstage/plugin-permission-common": "npm:^0.7.14" + "@backstage/plugin-search-common": "npm:^1.2.12" + checksum: 259d04788b69de44e25679af99520fbdd9322131b1b9ee62794ed4934d5eda37afbc764bcd5f47922206c7f539b2e4110dbc8471e64d925be8710c8ce7cf2e0f + languageName: node + linkType: hard + "@backstage/plugin-catalog-graph@npm:^0.4.5": version: 0.4.5 resolution: "@backstage/plugin-catalog-graph@npm:0.4.5" @@ -4702,6 +5132,42 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-catalog-react@npm:^1.12.1": + version: 1.12.1 + resolution: "@backstage/plugin-catalog-react@npm:1.12.1" + dependencies: + "@backstage/catalog-client": "npm:^1.6.5" + "@backstage/catalog-model": "npm:^1.5.0" + "@backstage/core-components": "npm:^0.14.8" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/errors": "npm:^1.2.4" + "@backstage/frontend-plugin-api": "npm:^0.6.6" + "@backstage/integration-react": "npm:^1.1.28" + "@backstage/plugin-catalog-common": "npm:^1.0.24" + "@backstage/plugin-permission-common": "npm:^0.7.14" + "@backstage/plugin-permission-react": "npm:^0.4.23" + "@backstage/types": "npm:^1.1.1" + "@backstage/version-bridge": "npm:^1.0.8" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@material-ui/lab": "npm:4.0.0-alpha.61" + "@react-hookz/web": "npm:^24.0.0" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + classnames: "npm:^2.2.6" + lodash: "npm:^4.17.21" + material-ui-popup-state: "npm:^1.9.3" + qs: "npm:^6.9.4" + react-use: "npm:^17.2.4" + yaml: "npm:^2.0.0" + zen-observable: "npm:^0.10.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 51dae1702e134454015b6c2a8ee87a5a1b3fab95462fa86f492aade93f6a90e1f09ee2d98da3ab7e7e002429dc61dcba2bb20dabaf86ad6a57fc5239c768022a + languageName: node + linkType: hard + "@backstage/plugin-catalog@npm:^1.20.0": version: 1.20.0 resolution: "@backstage/plugin-catalog@npm:1.20.0" @@ -4792,6 +5258,20 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-permission-common@npm:^0.7.14": + version: 0.7.14 + resolution: "@backstage/plugin-permission-common@npm:0.7.14" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/errors": "npm:^1.2.4" + "@backstage/types": "npm:^1.1.1" + cross-fetch: "npm:^4.0.0" + uuid: "npm:^9.0.0" + zod: "npm:^3.22.4" + checksum: 34f32836d9a42c0112d1a13916d7a7948c857aa6ec6da8a02bb57a71ce05af882bd3ef49930159ee8cb6b3d07798aba5441be9a7e3480fe88f84119e277acc75 + languageName: node + linkType: hard + "@backstage/plugin-permission-node@npm:^0.7.29": version: 0.7.29 resolution: "@backstage/plugin-permission-node@npm:0.7.29" @@ -4828,6 +5308,23 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-permission-react@npm:^0.4.23": + version: 0.4.23 + resolution: "@backstage/plugin-permission-react@npm:0.4.23" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/plugin-permission-common": "npm:^0.7.14" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + swr: "npm:^2.0.0" + peerDependencies: + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: 79cf7c833b7a13d27bd64698e390b2fcdaddad13be7e9a3ba25b88877657e7e298fd3a629f33ed5deaa5a19db8b85879fa6e3a389b0c9e7ae005da0b440b2408 + languageName: node + linkType: hard + "@backstage/plugin-proxy-backend@npm:^0.4.16": version: 0.4.16 resolution: "@backstage/plugin-proxy-backend@npm:0.4.16" @@ -5291,6 +5788,16 @@ __metadata: languageName: node linkType: hard +"@backstage/plugin-search-common@npm:^1.2.12": + version: 1.2.12 + resolution: "@backstage/plugin-search-common@npm:1.2.12" + dependencies: + "@backstage/plugin-permission-common": "npm:^0.7.14" + "@backstage/types": "npm:^1.1.1" + checksum: e428deb7d3e33dbe9941b0ad460d4449a782128876acdc32ed18738cd572b02d6d914c3336d0c55274be9a156f26f5b3ac46037622a148b17691618f7e026e1c + languageName: node + linkType: hard + "@backstage/plugin-search-react@npm:^1.7.11": version: 1.7.11 resolution: "@backstage/plugin-search-react@npm:1.7.11" @@ -5609,6 +6116,32 @@ __metadata: languageName: node linkType: hard +"@backstage/test-utils@npm:^1.5.6": + version: 1.5.6 + resolution: "@backstage/test-utils@npm:1.5.6" + dependencies: + "@backstage/config": "npm:^1.2.0" + "@backstage/core-app-api": "npm:^1.12.6" + "@backstage/core-plugin-api": "npm:^1.9.3" + "@backstage/plugin-permission-common": "npm:^0.7.14" + "@backstage/plugin-permission-react": "npm:^0.4.23" + "@backstage/theme": "npm:^0.5.6" + "@backstage/types": "npm:^1.1.1" + "@material-ui/core": "npm:^4.12.2" + "@material-ui/icons": "npm:^4.9.1" + "@types/react": "npm:^16.13.1 || ^17.0.0 || ^18.0.0" + cross-fetch: "npm:^4.0.0" + i18next: "npm:^22.4.15" + zen-observable: "npm:^0.10.0" + peerDependencies: + "@testing-library/react": ^15.0.0 + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-router-dom: 6.0.0-beta.0 || ^6.3.0 + checksum: d7566af04e95506d66166046edc6041d2a52340bbb7378bc2e8116dc57747cab8c88c56c9919143730a65d15f1d0e516dd2ab405653be94540d2807b4c418099 + languageName: node + linkType: hard + "@backstage/theme@npm:^0.5.3": version: 0.5.3 resolution: "@backstage/theme@npm:0.5.3" @@ -5641,6 +6174,22 @@ __metadata: languageName: node linkType: hard +"@backstage/theme@npm:^0.5.6": + version: 0.5.6 + resolution: "@backstage/theme@npm:0.5.6" + dependencies: + "@emotion/react": "npm:^11.10.5" + "@emotion/styled": "npm:^11.10.5" + "@mui/material": "npm:^5.12.2" + peerDependencies: + "@material-ui/core": ^4.12.2 + "@types/react": ^16.13.1 || ^17.0.0 || ^18.0.0 + react: ^16.13.1 || ^17.0.0 || ^18.0.0 + react-dom: ^16.13.1 || ^17.0.0 || ^18.0.0 + checksum: 50843ce3cc65e2b13dd61a24b888da3b6bf86a45a2464bba80f697c23290b9ffb8660c64eab1b0bf4b79cd271893b6162b7027662325c94a3ddfe6d4b42bb0fd + languageName: node + linkType: hard + "@backstage/types@npm:^1.1.1": version: 1.1.1 resolution: "@backstage/types@npm:1.1.1" @@ -6082,6 +6631,13 @@ __metadata: languageName: node linkType: hard +"@date-io/core@npm:^3.0.0": + version: 3.0.0 + resolution: "@date-io/core@npm:3.0.0" + checksum: c8a725ad5729b38d3f73b7c2bf852ac18a980d6b610832db9f87eb6821e6377854112ba4b5f8510d3e3f49d5290a50dd79ae7724bbafeb146bfdfb5b4c8a13ed + languageName: node + linkType: hard + "@date-io/date-fns@npm:^1.3.13": version: 1.3.13 resolution: "@date-io/date-fns@npm:1.3.13" @@ -6093,6 +6649,20 @@ __metadata: languageName: node linkType: hard +"@date-io/luxon@npm:3.0.0": + version: 3.0.0 + resolution: "@date-io/luxon@npm:3.0.0" + dependencies: + "@date-io/core": "npm:^3.0.0" + peerDependencies: + luxon: ^1.21.3 || ^2.x || ^3.x + peerDependenciesMeta: + luxon: + optional: true + checksum: 85b9b62bc61c6fb69dae06d64b63ccb6c24d059093610fdcb637ad8c90e1e60672c5478397462fc25a600efa7fe508c4829c09529a1753c4c2e30fabfdc64648 + languageName: node + linkType: hard + "@davidzemon/passport-okta-oauth@npm:^0.0.5": version: 0.0.5 resolution: "@davidzemon/passport-okta-oauth@npm:0.0.5" @@ -7035,6 +7605,13 @@ __metadata: languageName: node linkType: hard +"@interactjs/types@npm:1.10.27": + version: 1.10.27 + resolution: "@interactjs/types@npm:1.10.27" + checksum: 767afe37cd932ed248712dc0aa9c912b18af1104ec26829d655daaeef57d13c8e9e973c9f59e4be978d66fe7e86b77f58dec4688bb2e5d38b838d5a38cb27cf4 + languageName: node + linkType: hard + "@ioredis/commands@npm:^1.1.1": version: 1.2.0 resolution: "@ioredis/commands@npm:1.2.0" @@ -7713,7 +8290,7 @@ __metadata: languageName: node linkType: hard -"@material-ui/pickers@npm:^3.2.10": +"@material-ui/pickers@npm:^3.2.10, @material-ui/pickers@npm:^3.3.11": version: 3.3.11 resolution: "@material-ui/pickers@npm:3.3.11" dependencies: @@ -7871,6 +8448,13 @@ __metadata: languageName: node linkType: hard +"@microsoft/microsoft-graph-types@npm:^2.40.0": + version: 2.40.0 + resolution: "@microsoft/microsoft-graph-types@npm:2.40.0" + checksum: c6f69a0fe136579d735efafc1375e4540e01d34ea488d987a9b651f7206c40c76991fff56df31e8b2593f16846bd748d0ffcf249d1f2629bb0298e6214fe2fdc + languageName: node + linkType: hard + "@microsoft/tsdoc-config@npm:~0.16.1": version: 0.16.2 resolution: "@microsoft/tsdoc-config@npm:0.16.2" @@ -8024,6 +8608,57 @@ __metadata: languageName: node linkType: hard +"@mui/base@npm:5.0.0-beta.40": + version: 5.0.0-beta.40 + resolution: "@mui/base@npm:5.0.0-beta.40" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@floating-ui/react-dom": "npm:^2.0.8" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.14" + "@popperjs/core": "npm:^2.11.8" + clsx: "npm:^2.1.0" + prop-types: "npm:^15.8.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 631b4ee389e23d82c16c5845c2849af43000f52f1def639b9bb5bf39fd09f4eab93787d32950b715a7de7b689faab53bb7c9a78f6fd12b663876cf8128d45de1 + languageName: node + linkType: hard + +"@mui/base@npm:^5.0.0-beta.40": + version: 5.0.0-dev.20240529-082515-213b5e33ab + resolution: "@mui/base@npm:5.0.0-dev.20240529-082515-213b5e33ab" + dependencies: + "@babel/runtime": "npm:^7.24.6" + "@floating-ui/react-dom": "npm:^2.0.8" + "@mui/types": "npm:^7.2.14-dev.20240529-082515-213b5e33ab" + "@mui/utils": "npm:^6.0.0-dev.20240529-082515-213b5e33ab" + "@popperjs/core": "npm:^2.11.8" + clsx: "npm:^2.1.1" + prop-types: "npm:^15.8.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: c6d3119aeea8abbb6494b3a3a632668bbee57e4f4e1786783908f3e19deb3b764f56e7e518849a17f3eda6db213bcc147a87fd0baf98941596466601d823478b + languageName: node + linkType: hard + +"@mui/core-downloads-tracker@npm:^5.15.20": + version: 5.15.20 + resolution: "@mui/core-downloads-tracker@npm:5.15.20" + checksum: 0e1f9aa9c85d5f60fa5937edc22dfbebd512671379e3adb7c99a8ef0928bf42daed98cede41a407e810a225554943675e9d976bc0ae17acdc20445c8d2f121b3 + languageName: node + linkType: hard + "@mui/core-downloads-tracker@npm:^5.15.7": version: 5.15.7 resolution: "@mui/core-downloads-tracker@npm:5.15.7" @@ -8047,6 +8682,35 @@ __metadata: languageName: node linkType: hard +"@mui/lab@npm:^5.0.0-alpha.170": + version: 5.0.0-alpha.170 + resolution: "@mui/lab@npm:5.0.0-alpha.170" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@mui/base": "npm:5.0.0-beta.40" + "@mui/system": "npm:^5.15.15" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.14" + clsx: "npm:^2.1.0" + prop-types: "npm:^15.8.1" + peerDependencies: + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@mui/material": ">=5.15.0" + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@types/react": + optional: true + checksum: 56b095b2aee8cd1a45525fc5d0aac26d29f0111f12fbe05fe4fcf8eca2dedbb447d8d912330b469c35ab553a5776b5e91b9b7a44be5a214253301ec806c9f586 + languageName: node + linkType: hard + "@mui/material@npm:^5.12.2, @mui/material@npm:^5.15.7": version: 5.15.7 resolution: "@mui/material@npm:5.15.7" @@ -8080,6 +8744,56 @@ __metadata: languageName: node linkType: hard +"@mui/material@npm:^5.15.20": + version: 5.15.20 + resolution: "@mui/material@npm:5.15.20" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@mui/base": "npm:5.0.0-beta.40" + "@mui/core-downloads-tracker": "npm:^5.15.20" + "@mui/system": "npm:^5.15.20" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.20" + "@types/react-transition-group": "npm:^4.4.10" + clsx: "npm:^2.1.0" + csstype: "npm:^3.1.3" + prop-types: "npm:^15.8.1" + react-is: "npm:^18.2.0" + react-transition-group: "npm:^4.4.5" + peerDependencies: + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@types/react": + optional: true + checksum: 0882661f83f5b73c06d6402ddd9c0f8e471198b838c4a456db03a5256d8febc688abc41645156d155536106a44bd28798efcfdaec00b665ab91dbd5c9dee83eb + languageName: node + linkType: hard + +"@mui/private-theming@npm:^5.15.20": + version: 5.15.20 + resolution: "@mui/private-theming@npm:5.15.20" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@mui/utils": "npm:^5.15.20" + prop-types: "npm:^15.8.1" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: d6707ef884769a58a9dac23328138cd2dbb3dbdcfbf46beae406638628a77b2975aaf787f863143ae97ea2d5f6eca8d0ff929f2159f3f9b58bcedb2e01c7bce6 + languageName: node + linkType: hard + "@mui/private-theming@npm:^5.15.7": version: 5.15.7 resolution: "@mui/private-theming@npm:5.15.7" @@ -8097,6 +8811,27 @@ __metadata: languageName: node linkType: hard +"@mui/styled-engine@npm:^5.15.14": + version: 5.15.14 + resolution: "@mui/styled-engine@npm:5.15.14" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@emotion/cache": "npm:^11.11.0" + csstype: "npm:^3.1.3" + prop-types: "npm:^15.8.1" + peerDependencies: + "@emotion/react": ^11.4.1 + "@emotion/styled": ^11.3.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + checksum: 0d262ea0b3c117f865af1cd52b992592c24432e491b35e712159bb49adfd776ee9a532abbc4ab08889f308e75d30082a0fee809119d5d61a82b3277212655319 + languageName: node + linkType: hard + "@mui/styled-engine@npm:^5.15.7": version: 5.15.7 resolution: "@mui/styled-engine@npm:5.15.7" @@ -8149,6 +8884,34 @@ __metadata: languageName: node linkType: hard +"@mui/system@npm:^5.15.15, @mui/system@npm:^5.15.20": + version: 5.15.20 + resolution: "@mui/system@npm:5.15.20" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@mui/private-theming": "npm:^5.15.20" + "@mui/styled-engine": "npm:^5.15.14" + "@mui/types": "npm:^7.2.14" + "@mui/utils": "npm:^5.15.20" + clsx: "npm:^2.1.0" + csstype: "npm:^3.1.3" + prop-types: "npm:^15.8.1" + peerDependencies: + "@emotion/react": ^11.5.0 + "@emotion/styled": ^11.3.0 + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + "@types/react": + optional: true + checksum: 7561d14d613e882cbe8959cbb779e6641542242e5a79974bad306c59c1505b5924cb2b866619b55d064aae1a1e8421dc3a613ad1a4cf330be7eebd1881170cff + languageName: node + linkType: hard + "@mui/system@npm:^5.15.7": version: 5.15.7 resolution: "@mui/system@npm:5.15.7" @@ -8189,6 +8952,18 @@ __metadata: languageName: node linkType: hard +"@mui/types@npm:^7.2.14, @mui/types@npm:^7.2.14-dev.20240529-082515-213b5e33ab": + version: 7.2.14 + resolution: "@mui/types@npm:7.2.14" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: d4e0a9fce4bddfb5e0b7b6be1b15b591df33bb90ef0087e4bd5fe85f00f62776c7ed0e4698e7fb43213e1f04064aac1695b53ca52aaeaee7dbba248a792bdd1e + languageName: node + linkType: hard + "@mui/utils@npm:^5.14.15, @mui/utils@npm:^5.15.7": version: 5.15.7 resolution: "@mui/utils@npm:5.15.7" @@ -8207,6 +8982,90 @@ __metadata: languageName: node linkType: hard +"@mui/utils@npm:^5.15.14, @mui/utils@npm:^5.15.20": + version: 5.15.20 + resolution: "@mui/utils@npm:5.15.20" + dependencies: + "@babel/runtime": "npm:^7.23.9" + "@types/prop-types": "npm:^15.7.11" + prop-types: "npm:^15.8.1" + react-is: "npm:^18.2.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: d745895db047ef016681482f3d4710aa487f9e7bcd457ac2a395bdaf719a6a98f8bf5f1920d4729b0bc69f7ef2153624aa6a565ec0e31f76c33d7167397ac4d9 + languageName: node + linkType: hard + +"@mui/utils@npm:^6.0.0-dev.20240529-082515-213b5e33ab": + version: 6.0.0-dev.20240529-082515-213b5e33ab + resolution: "@mui/utils@npm:6.0.0-dev.20240529-082515-213b5e33ab" + dependencies: + "@babel/runtime": "npm:^7.24.6" + "@types/prop-types": "npm:^15.7.12" + prop-types: "npm:^15.8.1" + react-is: "npm:^18.2.0" + peerDependencies: + "@types/react": ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + checksum: 6fbd5d874936d818e9c8ae595b9111da11422e1b4d3ce973779acdf34431f01f27fbbe175ca5912c4f4f2c7f1b10b98a5cb571cf8da92d5bb46d43fe0811ff85 + languageName: node + linkType: hard + +"@mui/x-date-pickers@npm:^7.7.0": + version: 7.7.0 + resolution: "@mui/x-date-pickers@npm:7.7.0" + dependencies: + "@babel/runtime": "npm:^7.24.7" + "@mui/base": "npm:^5.0.0-beta.40" + "@mui/system": "npm:^5.15.15" + "@mui/utils": "npm:^5.15.14" + "@types/react-transition-group": "npm:^4.4.10" + clsx: "npm:^2.1.1" + prop-types: "npm:^15.8.1" + react-transition-group: "npm:^4.4.5" + peerDependencies: + "@emotion/react": ^11.9.0 + "@emotion/styled": ^11.8.1 + "@mui/material": ^5.15.14 + date-fns: ^2.25.0 || ^3.2.0 + date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 + dayjs: ^1.10.7 + luxon: ^3.0.2 + moment: ^2.29.4 + moment-hijri: ^2.1.2 + moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + "@emotion/react": + optional: true + "@emotion/styled": + optional: true + date-fns: + optional: true + date-fns-jalali: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + moment-hijri: + optional: true + moment-jalaali: + optional: true + checksum: 0df1b214ccc3fbdff1b4edb301a718a30db8c56900e4a0537de2769ace169e6c4bad8a5f91a65753bc3072831d565e8d489bf19ee016a954a45c5ba1bca5f9e2 + languageName: node + linkType: hard + "@n1ru4l/push-pull-async-iterable-iterator@npm:^3.1.0": version: 3.2.0 resolution: "@n1ru4l/push-pull-async-iterable-iterator@npm:3.2.0" @@ -12486,6 +13345,32 @@ __metadata: languageName: node linkType: hard +"@tanstack/query-core@npm:4.29.1": + version: 4.29.1 + resolution: "@tanstack/query-core@npm:4.29.1" + checksum: 38fb775e370e7cd43fa4084620d855a08cb02e52adc8c3dd9eaea92ca4aa6ba3ae3a0ab627162e2f4a974f629478b660dc6f1a77723e7aaaf7408ab7c1840847 + languageName: node + linkType: hard + +"@tanstack/react-query@npm:4.29.1": + version: 4.29.1 + resolution: "@tanstack/react-query@npm:4.29.1" + dependencies: + "@tanstack/query-core": "npm:4.29.1" + use-sync-external-store: "npm:^1.2.0" + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-dom: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native: "*" + peerDependenciesMeta: + react-dom: + optional: true + react-native: + optional: true + checksum: a267bb4e5192ef3518aa0313c8f9a92bb0ea902caeb5598e2ea0d542d7253a99f438dcbb1a58eb40e3ad54ca52a06b024188dc1864e1d153ad1a6a4f54d088ba + languageName: node + linkType: hard + "@testing-library/dom@npm:^9.0.0": version: 9.3.4 resolution: "@testing-library/dom@npm:9.3.4" @@ -12563,6 +13448,26 @@ __metadata: languageName: node linkType: hard +"@testing-library/react@npm:^16.0.0": + version: 16.0.0 + resolution: "@testing-library/react@npm:16.0.0" + dependencies: + "@babel/runtime": "npm:^7.12.5" + peerDependencies: + "@testing-library/dom": ^10.0.0 + "@types/react": ^18.0.0 + "@types/react-dom": ^18.0.0 + react: ^18.0.0 + react-dom: ^18.0.0 + peerDependenciesMeta: + "@types/react": + optional: true + "@types/react-dom": + optional: true + checksum: 297f97bf4722dad05f11d9cafd47d387dbdb096fea4b79b876c7466460f0f2e345b55b81b3e37fc81ed8185c528cb53dd8455ca1b6b019b229edf6c796f11c9f + languageName: node + linkType: hard + "@testing-library/user-event@npm:^14.0.0, @testing-library/user-event@npm:^14.5.2": version: 14.5.2 resolution: "@testing-library/user-event@npm:14.5.2" @@ -13136,6 +14041,13 @@ __metadata: languageName: node linkType: hard +"@types/lodash@npm:^4.17.5": + version: 4.17.5 + resolution: "@types/lodash@npm:4.17.5" + checksum: 55924803ed853e72261512bd3eaf2c5b16558c3817feb0a3125ef757afe46e54b86f33d1960e40b7606c0ddab91a96f47966bf5e6006b7abfd8994c13b04b19b + languageName: node + linkType: hard + "@types/long@npm:^4.0.0": version: 4.0.2 resolution: "@types/long@npm:4.0.2" @@ -13164,7 +14076,7 @@ __metadata: languageName: node linkType: hard -"@types/luxon@npm:~3.4.0": +"@types/luxon@npm:^3.4.2, @types/luxon@npm:~3.4.0": version: 3.4.2 resolution: "@types/luxon@npm:3.4.2" checksum: d835467de3daf7e17ba78b50bb5a14efd94272439ca067990d71332a54b311544459c69623eddd243b511b28d70194c9591a9ee8cf9c038962c965f991affd7e @@ -13344,6 +14256,13 @@ __metadata: languageName: node linkType: hard +"@types/prop-types@npm:^15.7.12": + version: 15.7.12 + resolution: "@types/prop-types@npm:15.7.12" + checksum: 1babcc7db6a1177779f8fde0ccc78d64d459906e6ef69a4ed4dd6339c920c2e05b074ee5a92120fe4e9d9f1a01c952f843ebd550bee2332fc2ef81d1706878f8 + languageName: node + linkType: hard + "@types/protocol-buffers-schema@npm:^3.4.1": version: 3.4.3 resolution: "@types/protocol-buffers-schema@npm:3.4.3" @@ -13376,6 +14295,16 @@ __metadata: languageName: node linkType: hard +"@types/react-calendar-timeline@npm:^0.28.6": + version: 0.28.6 + resolution: "@types/react-calendar-timeline@npm:0.28.6" + dependencies: + "@types/react": "npm:*" + moment: "npm:^2.0.0" + checksum: 6f7b1888ad8d62bd59a6157ed451aa268adbb670c136c4f78dfa52ba72deaf8fcf35f99e44b2c53e4a7123203eccee76d19c7aa1de60cbc587785b9bf38bd380 + languageName: node + linkType: hard + "@types/react-dom@npm:^18": version: 18.2.18 resolution: "@types/react-dom@npm:18.2.18" @@ -14301,6 +15230,16 @@ __metadata: languageName: node linkType: hard +"@yarnpkg/parsers@npm:^3.0.0": + version: 3.0.2 + resolution: "@yarnpkg/parsers@npm:3.0.2" + dependencies: + js-yaml: "npm:^3.10.0" + tslib: "npm:^2.4.0" + checksum: a0c340e13129643162423d7e666061c0b39b143bfad3fc5a74c7d92a30fd740f6665d41cd4e61832c20375889d793eea1d1d103cacb39ed68f7acd168add8c53 + languageName: node + linkType: hard + "@yarnpkg/parsers@npm:^3.0.0-rc.4": version: 3.0.0 resolution: "@yarnpkg/parsers@npm:3.0.0" @@ -14698,6 +15637,7 @@ __metadata: "@axis-backstage/plugin-jira-dashboard-common": "workspace:^" "@axis-backstage/plugin-readme": "workspace:^" "@axis-backstage/plugin-statuspage": "workspace:^" + "@axis-backstage/plugin-vacation-calendar": "npm:^0.1.0" "@backstage-community/plugin-github-actions": "npm:^0.6.16" "@backstage/app-defaults": "npm:^1.5.5" "@backstage/catalog-model": "npm:^1.5.0" @@ -15445,6 +16385,7 @@ __metadata: "@backstage/config": "npm:^1.2.0" "@backstage/plugin-app-backend": "npm:^0.3.67" "@backstage/plugin-auth-backend": "npm:^0.22.5" + "@backstage/plugin-auth-backend-module-microsoft-provider": "npm:^0.1.13" "@backstage/plugin-auth-node": "npm:^0.4.13" "@backstage/plugin-catalog-backend": "npm:^1.22.0" "@backstage/plugin-permission-common": "npm:^0.7.13" @@ -15556,6 +16497,13 @@ __metadata: languageName: node linkType: hard +"batch-processor@npm:1.0.0": + version: 1.0.0 + resolution: "batch-processor@npm:1.0.0" + checksum: 048b868811bed4cd03a0eec35264055f0f3fe4ab62f501809dce4a8a7b845d905fa5051b4af8b3c5123181116b1e2b6dfabf608829043b60cf61f4da3a359b60 + languageName: node + linkType: hard + "batch@npm:0.6.1": version: 0.6.1 resolution: "batch@npm:0.6.1" @@ -16592,6 +17540,13 @@ __metadata: languageName: node linkType: hard +"clsx@npm:^2.1.1": + version: 2.1.1 + resolution: "clsx@npm:2.1.1" + checksum: c4c8eb865f8c82baab07e71bfa8897c73454881c4f99d6bc81585aecd7c441746c1399d08363dc096c550cceaf97bd4ce1e8854e1771e9998d9f94c4fe075839 + languageName: node + linkType: hard + "cluster-key-slot@npm:^1.1.0": version: 1.1.2 resolution: "cluster-key-slot@npm:1.1.2" @@ -17486,6 +18441,19 @@ __metadata: languageName: node linkType: hard +"create-react-context@npm:^0.3.0": + version: 0.3.0 + resolution: "create-react-context@npm:0.3.0" + dependencies: + gud: "npm:^1.0.0" + warning: "npm:^4.0.3" + peerDependencies: + prop-types: ^15.0.0 + react: ^0.14.0 || ^15.0.0 || ^16.0.0 + checksum: 3f9dfde23da59e3f748b5f1b06c7ff8cbf48095cf2d62212427195860f1ee4b2b0b475280c19592f7fffb9fd100fd739af687281d7c5c93806d519bf66f6dd86 + languageName: node + linkType: hard + "create-require@npm:^1.1.0": version: 1.1.1 resolution: "create-require@npm:1.1.1" @@ -17796,6 +18764,13 @@ __metadata: languageName: node linkType: hard +"csstype@npm:^3.1.3": + version: 3.1.3 + resolution: "csstype@npm:3.1.3" + checksum: 80c089d6f7e0c5b2bd83cf0539ab41474198579584fa10d86d0cafe0642202343cbc119e076a0b1aece191989477081415d66c9fefbf3c957fc2fc4b7009f248 + languageName: node + linkType: hard + "csv-generate@npm:^3.4.3": version: 3.4.3 resolution: "csv-generate@npm:3.4.3" @@ -18772,6 +19747,15 @@ __metadata: languageName: node linkType: hard +"element-resize-detector@npm:^1.1.12": + version: 1.2.4 + resolution: "element-resize-detector@npm:1.2.4" + dependencies: + batch-processor: "npm:1.0.0" + checksum: 8c180c8c2a6d5b83678f994e937890f06db6355009cce2bde3c690a45510c92f53f927431926b27db416739aa7b661c7d3afe237d17cd16491ecccfa740cda08 + languageName: node + linkType: hard + "elliptic@npm:^6.5.3, elliptic@npm:^6.5.4": version: 6.5.4 resolution: "elliptic@npm:6.5.4" @@ -21362,6 +22346,13 @@ __metadata: languageName: node linkType: hard +"gud@npm:^1.0.0": + version: 1.0.0 + resolution: "gud@npm:1.0.0" + checksum: a4db6edc18e2c4e3a22dc9e639e40a4e5650d53dae9cf384a96d5380dfa17ddda376cf6b7797a5c30d140d2532e5a69d167bdb70c2c151dd673253bac6b027f3 + languageName: node + linkType: hard + "gzip-size@npm:^6.0.0": version: 6.0.0 resolution: "gzip-size@npm:6.0.0" @@ -22202,6 +23193,16 @@ __metadata: languageName: node linkType: hard +"inline-style-prefixer@npm:^7.0.0": + version: 7.0.0 + resolution: "inline-style-prefixer@npm:7.0.0" + dependencies: + css-in-js-utils: "npm:^3.1.0" + fast-loops: "npm:^1.1.3" + checksum: d19eb485961854c5e00152b351a0d161b1a8d80d415bab2ba79ad10059d03843bc1e71415d265f1ec10857f5dd4a75c2491fced08eb0c5883cf42c8a9dccb473 + languageName: node + linkType: hard + "inquirer@npm:8.2.5": version: 8.2.5 resolution: "inquirer@npm:8.2.5" @@ -22248,6 +23249,15 @@ __metadata: languageName: node linkType: hard +"interactjs@npm:^1.10.27": + version: 1.10.27 + resolution: "interactjs@npm:1.10.27" + dependencies: + "@interactjs/types": "npm:1.10.27" + checksum: 35532c636fc7cf8273999d314c9738994bfc4a1a384fd64658420055e93456aa6333a865e04b1765d39ee4e29f898854c3f3451a7a88a09141e32115cc6112e2 + languageName: node + linkType: hard + "internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5": version: 1.0.6 resolution: "internal-slot@npm:1.0.6" @@ -25061,7 +26071,7 @@ __metadata: languageName: node linkType: hard -"luxon@npm:~3.4.0": +"luxon@npm:^3.4.4, luxon@npm:~3.4.0": version: 3.4.4 resolution: "luxon@npm:3.4.4" checksum: 02e26a0b039c11fd5b75e1d734c8f0332c95510f6a514a9a0991023e43fb233884da02d7f966823ffb230632a733fc86d4a4b1e63c3fbe00058b8ee0f8c728af @@ -26308,6 +27318,13 @@ __metadata: languageName: node linkType: hard +"moment@npm:^2.0.0": + version: 2.30.1 + resolution: "moment@npm:2.30.1" + checksum: 865e4279418c6de666fca7786607705fd0189d8a7b7624e2e56be99290ac846f90878a6f602e34b4e0455c549b85385b1baf9966845962b313699e7cb847543a + languageName: node + linkType: hard + "moo@npm:^0.5.0": version: 0.5.2 resolution: "moo@npm:0.5.2" @@ -26552,6 +27569,25 @@ __metadata: languageName: node linkType: hard +"nano-css@npm:^5.6.1": + version: 5.6.1 + resolution: "nano-css@npm:5.6.1" + dependencies: + "@jridgewell/sourcemap-codec": "npm:^1.4.15" + css-tree: "npm:^1.1.2" + csstype: "npm:^3.1.2" + fastest-stable-stringify: "npm:^2.0.2" + inline-style-prefixer: "npm:^7.0.0" + rtl-css-js: "npm:^1.16.1" + stacktrace-js: "npm:^2.0.2" + stylis: "npm:^4.3.0" + peerDependencies: + react: "*" + react-dom: "*" + checksum: 7328c973852d2471bd154c61d21392a3d6357f276a7a090b8a179fb06d71ba58c987b04c0bd80efebd87aa4f433428a25e37820e65484b3c4c44b84339b99d87 + languageName: node + linkType: hard + "nanoid@npm:^3.3.6": version: 3.3.6 resolution: "nanoid@npm:3.3.6" @@ -29679,6 +30715,25 @@ __metadata: languageName: node linkType: hard +"react-calendar-timeline@npm:^0.28.0": + version: 0.28.0 + resolution: "react-calendar-timeline@npm:0.28.0" + dependencies: + classnames: "npm:^2.2.6" + create-react-context: "npm:^0.3.0" + element-resize-detector: "npm:^1.1.12" + lodash.isequal: "npm:^4.5.0" + memoize-one: "npm:^5.1.1" + peerDependencies: + interactjs: ^1.3.4 + moment: "*" + prop-types: ^15.6.2 + react: ">=16.3" + react-dom: ">=16.3" + checksum: 7943d570a746c764daad7b1f734782d3fdf4fb20bd217a41501bc7c70b6a3218724dcc4512f859b0a2e996017722f8c16aae2c48e231b428ea4dd25a792af78a + languageName: node + linkType: hard + "react-copy-to-clipboard@npm:5.1.0": version: 5.1.0 resolution: "react-copy-to-clipboard@npm:5.1.0" @@ -30115,6 +31170,31 @@ __metadata: languageName: node linkType: hard +"react-use@npm:^17.5.0": + version: 17.5.0 + resolution: "react-use@npm:17.5.0" + dependencies: + "@types/js-cookie": "npm:^2.2.6" + "@xobotyi/scrollbar-width": "npm:^1.9.5" + copy-to-clipboard: "npm:^3.3.1" + fast-deep-equal: "npm:^3.1.3" + fast-shallow-equal: "npm:^1.0.0" + js-cookie: "npm:^2.2.1" + nano-css: "npm:^5.6.1" + react-universal-interface: "npm:^0.6.2" + resize-observer-polyfill: "npm:^1.5.1" + screenfull: "npm:^5.1.0" + set-harmonic-interval: "npm:^1.0.1" + throttle-debounce: "npm:^3.0.1" + ts-easing: "npm:^0.2.0" + tslib: "npm:^2.1.0" + peerDependencies: + react: "*" + react-dom: "*" + checksum: b2e606338f329f8f26bccbd1ae428cf63e1d9b4a940cb327823270955a2aae35972be745d333d1a1bd0276a3650038d1f7f6ae1077af5cccba8234a3e7376754 + languageName: node + linkType: hard + "react-virtualized-auto-sizer@npm:^1.0.11": version: 1.0.20 resolution: "react-virtualized-auto-sizer@npm:1.0.20" @@ -31046,7 +32126,7 @@ __metadata: languageName: unknown linkType: soft -"rtl-css-js@npm:^1.14.0": +"rtl-css-js@npm:^1.14.0, rtl-css-js@npm:^1.16.1": version: 1.16.1 resolution: "rtl-css-js@npm:1.16.1" dependencies: @@ -32583,6 +33663,13 @@ __metadata: languageName: node linkType: hard +"stylis@npm:^4.3.0": + version: 4.3.2 + resolution: "stylis@npm:4.3.2" + checksum: 0410e1404cbeee3388a9e17587875211ce2f014c8379af0d1e24ca55878867c9f1ccc7b0ce9a156ca53f5d6e301391a82b0645522a604674a378b3189a4a1994 + languageName: node + linkType: hard + "sucrase@npm:^3.20.2": version: 3.34.0 resolution: "sucrase@npm:3.34.0" @@ -34571,6 +35658,15 @@ __metadata: languageName: node linkType: hard +"warning@npm:^4.0.3": + version: 4.0.3 + resolution: "warning@npm:4.0.3" + dependencies: + loose-envify: "npm:^1.0.0" + checksum: aebab445129f3e104c271f1637fa38e55eb25f968593e3825bd2f7a12bd58dc3738bb70dc8ec85826621d80b4acfed5a29ebc9da17397c6125864d72301b937e + languageName: node + linkType: hard + "watchpack@npm:^2.4.0": version: 2.4.0 resolution: "watchpack@npm:2.4.0"