Google calendar widget
Signed-off-by: Alex Rybchenko <arybchenko@box.com>
This commit is contained in:
@@ -29,6 +29,7 @@
|
||||
"@backstage/plugin-code-coverage": "^0.1.27",
|
||||
"@backstage/plugin-cost-insights": "^0.11.22",
|
||||
"@backstage/plugin-explore": "^0.3.31",
|
||||
"@backstage/plugin-gcalendar-homepage": "^0.0.0",
|
||||
"@backstage/plugin-gcp-projects": "^0.3.19",
|
||||
"@backstage/plugin-github-actions": "^0.5.0",
|
||||
"@backstage/plugin-gocd": "^0.1.6",
|
||||
@@ -59,10 +60,10 @@
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"@octokit/rest": "^18.5.3",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^1.3.8",
|
||||
"@roadiehq/backstage-plugin-github-insights": "^1.5.0",
|
||||
"@roadiehq/backstage-plugin-github-pull-requests": "^1.4.0",
|
||||
"@roadiehq/backstage-plugin-travis-ci": "^1.3.6",
|
||||
"@roadiehq/backstage-plugin-buildkite": "^1.3.8",
|
||||
"history": "^5.0.0",
|
||||
"prop-types": "^15.7.2",
|
||||
"react": "^17.0.2",
|
||||
|
||||
@@ -33,7 +33,12 @@ import {
|
||||
createApiFactory,
|
||||
errorApiRef,
|
||||
githubAuthApiRef,
|
||||
googleAuthApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
import {
|
||||
GCalendarApiClient,
|
||||
gcalendarApiRef,
|
||||
} from '@backstage/plugin-gcalendar-homepage';
|
||||
|
||||
export const apis: AnyApiFactory[] = [
|
||||
createApiFactory({
|
||||
@@ -41,9 +46,14 @@ export const apis: AnyApiFactory[] = [
|
||||
deps: { configApi: configApiRef },
|
||||
factory: ({ configApi }) => ScmIntegrationsApi.fromConfig(configApi),
|
||||
}),
|
||||
|
||||
ScmAuth.createDefaultApiFactory(),
|
||||
|
||||
createApiFactory({
|
||||
api: gcalendarApiRef,
|
||||
deps: { authApi: googleAuthApiRef },
|
||||
factory: deps => new GCalendarApiClient(deps),
|
||||
}),
|
||||
|
||||
createApiFactory({
|
||||
api: graphQlBrowseApiRef,
|
||||
deps: { errorApi: errorApiRef, githubAuthApi: githubAuthApiRef },
|
||||
|
||||
@@ -25,6 +25,7 @@ import {
|
||||
} from '@backstage/plugin-home';
|
||||
import { Content, Header, Page } from '@backstage/core-components';
|
||||
import { HomePageSearchBar } from '@backstage/plugin-search';
|
||||
import { CalendarCard } from '@backstage/plugin-gcalendar-homepage';
|
||||
import Grid from '@material-ui/core/Grid';
|
||||
import React from 'react';
|
||||
|
||||
@@ -100,6 +101,9 @@ export const HomePage = () => (
|
||||
]}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12} md={4}>
|
||||
<CalendarCard />
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Content>
|
||||
</Page>
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
extends: [require.resolve('@backstage/cli/config/eslint')],
|
||||
};
|
||||
@@ -0,0 +1,13 @@
|
||||
# gcalendar-homepage
|
||||
|
||||
Welcome to the gcalendar-homepage plugin!
|
||||
|
||||
_This plugin was created through the Backstage CLI_
|
||||
|
||||
## Getting started
|
||||
|
||||
Your 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 [/gcalendar-homepage](http://localhost:3000/gcalendar-homepage).
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createDevApp } from '@backstage/dev-utils';
|
||||
import { gcalendarHomepagePlugin } from '../src/plugin';
|
||||
|
||||
createDevApp().registerPlugin(gcalendarHomepagePlugin).render();
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"name": "@backstage/plugin-gcalendar-homepage",
|
||||
"version": "0.0.0",
|
||||
"main": "src/index.ts",
|
||||
"types": "src/index.ts",
|
||||
"license": "Apache-2.0",
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
"main": "dist/index.esm.js",
|
||||
"types": "dist/index.d.ts"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "backstage-cli plugin:build",
|
||||
"start": "backstage-cli plugin:serve",
|
||||
"lint": "backstage-cli lint",
|
||||
"test": "backstage-cli test",
|
||||
"diff": "backstage-cli plugin:diff",
|
||||
"prepack": "backstage-cli prepack",
|
||||
"postpack": "backstage-cli postpack",
|
||||
"clean": "backstage-cli clean"
|
||||
},
|
||||
"dependencies": {
|
||||
"@backstage/core-components": "^0.8.9",
|
||||
"@backstage/core-plugin-api": "^0.6.1",
|
||||
"@backstage/dev-utils": "^0.2.22",
|
||||
"@backstage/theme": "^0.2.15",
|
||||
"@material-ui/core": "^4.9.13",
|
||||
"@material-ui/icons": "^4.9.1",
|
||||
"@material-ui/lab": "4.0.0-alpha.57",
|
||||
"@testing-library/jest-dom": "^5.16.2",
|
||||
"axios": "^0.26.0",
|
||||
"classnames": "^2.3.1",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"lodash": "^4.17.21",
|
||||
"luxon": "^2.3.0",
|
||||
"material-ui-popup-state": "^2.0.0",
|
||||
"react-query": "^3.34.16",
|
||||
"react-use": "^17.2.4",
|
||||
"sanitize-html": "^2.7.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"react": "^16.13.1 || ^17.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@backstage/cli": "^0.14.0",
|
||||
"@backstage/core-app-api": "^0.5.3",
|
||||
"@backstage/dev-utils": "^0.2.22",
|
||||
"@backstage/test-utils": "^0.2.5",
|
||||
"@testing-library/jest-dom": "^5.10.1",
|
||||
"@testing-library/react": "^11.2.5",
|
||||
"@testing-library/user-event": "^13.1.8",
|
||||
"@types/gapi": "^0.0.41",
|
||||
"@types/gapi.auth2": "^0.0.56",
|
||||
"@types/gapi.client.calendar": "^3.0.10",
|
||||
"@types/jest": "*",
|
||||
"@types/node": "*",
|
||||
"@types/sanitize-html": "^2.6.2",
|
||||
"cross-fetch": "^3.1.5",
|
||||
"msw": "^0.35.0"
|
||||
},
|
||||
"files": [
|
||||
"dist"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import axios, { AxiosInstance } from 'axios';
|
||||
|
||||
import { OAuthApi, createApiRef } from '@backstage/core-plugin-api';
|
||||
|
||||
import { GCalendar, GCalendarEvent } from '../components/CalendarCard/types';
|
||||
|
||||
type Options = {
|
||||
authApi: OAuthApi;
|
||||
};
|
||||
|
||||
export const gcalendarApiRef = createApiRef<GCalendarApiClient>({
|
||||
id: 'plugin.gcalendar.service',
|
||||
});
|
||||
|
||||
export class GCalendarApiClient {
|
||||
private readonly authApi: OAuthApi;
|
||||
private readonly http: AxiosInstance;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.authApi = options.authApi;
|
||||
this.http = axios.create({
|
||||
baseURL: 'https://www.googleapis.com/calendar/v3',
|
||||
});
|
||||
this.http.interceptors.request.use(async config => {
|
||||
const token = await this.authApi.getAccessToken();
|
||||
if (!config.headers) {
|
||||
config.headers = {};
|
||||
}
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
|
||||
return config;
|
||||
});
|
||||
}
|
||||
|
||||
public async getCalendars(params?: any): Promise<{ items: GCalendar[] }> {
|
||||
const { data } = await this.http.get('/users/me/calendarList', {
|
||||
params,
|
||||
});
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
public async getEvents(
|
||||
calendarId: string,
|
||||
params?: any,
|
||||
): Promise<{ items: GCalendarEvent[] }> {
|
||||
const { data } = await this.http.get(
|
||||
`/calendars/${encodeURIComponent(calendarId)}/events`,
|
||||
{
|
||||
params,
|
||||
},
|
||||
);
|
||||
|
||||
return data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { AttendeeChip } from './AttendeeChip';
|
||||
import { EventAttendee, ResponseStatus } from './types';
|
||||
|
||||
describe('<AttendeeChip />', () => {
|
||||
it('renders attendee email', async () => {
|
||||
const email = 'test@test.com';
|
||||
const user: EventAttendee = {
|
||||
email,
|
||||
responseStatus: ResponseStatus.needsAction,
|
||||
};
|
||||
const { queryByText } = await renderInTestApp(<AttendeeChip user={user} />);
|
||||
expect(queryByText(email)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders accepted icon', async () => {
|
||||
const email = 'test@test.com';
|
||||
const user: EventAttendee = {
|
||||
email,
|
||||
responseStatus: ResponseStatus.accepted,
|
||||
};
|
||||
const { getByTestId } = await renderInTestApp(<AttendeeChip user={user} />);
|
||||
expect(getByTestId('accepted-icon')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('renders declined icon', async () => {
|
||||
const email = 'test@test.com';
|
||||
const user: EventAttendee = {
|
||||
email,
|
||||
responseStatus: ResponseStatus.declined,
|
||||
};
|
||||
const { getByTestId } = await renderInTestApp(<AttendeeChip user={user} />);
|
||||
expect(getByTestId('declined-icon')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,87 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { BackstageTheme } from '@backstage/theme';
|
||||
|
||||
import { Badge, Chip, makeStyles } from '@material-ui/core';
|
||||
import CancelIcon from '@material-ui/icons/Cancel';
|
||||
import CheckIcon from '@material-ui/icons/CheckCircle';
|
||||
|
||||
import { EventAttendee, ResponseStatus } from './types';
|
||||
|
||||
const useStyles = makeStyles((theme: BackstageTheme) => {
|
||||
const getIconColor = (responseStatus?: string) => {
|
||||
if (!responseStatus) return theme.palette.primary.light;
|
||||
|
||||
return {
|
||||
[ResponseStatus.accepted]: theme.palette.status.ok,
|
||||
[ResponseStatus.declined]: theme.palette.status.error,
|
||||
}[responseStatus];
|
||||
};
|
||||
|
||||
return {
|
||||
responseStatus: {
|
||||
color: ({ responseStatus }: { responseStatus?: string }) =>
|
||||
getIconColor(responseStatus),
|
||||
},
|
||||
badge: {
|
||||
right: 10,
|
||||
top: 5,
|
||||
'& svg': {
|
||||
height: 16,
|
||||
width: 16,
|
||||
background: '#fff',
|
||||
},
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
const ResponseIcon = ({ responseStatus }: any) => {
|
||||
if (responseStatus === ResponseStatus.accepted) {
|
||||
return <CheckIcon data-testid="accepted-icon" />;
|
||||
}
|
||||
if (responseStatus === ResponseStatus.declined) {
|
||||
return <CancelIcon data-testid="declined-icon" />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
type AttendeeChipProps = {
|
||||
user: EventAttendee;
|
||||
};
|
||||
|
||||
export const AttendeeChip = ({ user }: AttendeeChipProps) => {
|
||||
const classes = useStyles({ responseStatus: user.responseStatus });
|
||||
|
||||
return (
|
||||
<Badge
|
||||
classes={{
|
||||
root: classes.responseStatus,
|
||||
badge: classes.badge,
|
||||
}}
|
||||
badgeContent={<ResponseIcon responseStatus={user.responseStatus} />}
|
||||
>
|
||||
<Chip
|
||||
size="small"
|
||||
variant="outlined"
|
||||
label={user.email}
|
||||
color="primary"
|
||||
/>
|
||||
</Badge>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { googleAuthApiRef, storageApiRef } from '@backstage/core-plugin-api';
|
||||
import {
|
||||
MockStorageApi,
|
||||
TestApiProvider,
|
||||
renderInTestApp,
|
||||
} from '@backstage/test-utils';
|
||||
|
||||
import { CalendarCardContainer } from '.';
|
||||
import { gcalendarApiRef, gcalendarHomepagePlugin } from '../..';
|
||||
|
||||
describe('<CalendarCard />', () => {
|
||||
const primaryCalendar = {
|
||||
id: 'test-1@test.com',
|
||||
summary: 'test-1@test.com',
|
||||
primary: true,
|
||||
};
|
||||
const nonPrimaryCalendar = {
|
||||
id: 'test-2@test.com',
|
||||
summary: 'test-2@test.com',
|
||||
primary: false,
|
||||
};
|
||||
const calendarData = {
|
||||
items: [primaryCalendar, nonPrimaryCalendar],
|
||||
};
|
||||
const eventData = {
|
||||
items: [
|
||||
{
|
||||
id: '1',
|
||||
summary: 'Test event',
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
summary: 'Test event',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const getMockedApi = (calendarMock = {}, eventMock = {}) => ({
|
||||
getCalendars: jest.fn().mockResolvedValue(calendarMock),
|
||||
getEvents: jest.fn().mockResolvedValue(eventMock),
|
||||
});
|
||||
|
||||
const getAuthMockApi = (token = 'token') => ({
|
||||
getAccessToken: jest.fn().mockResolvedValue(token),
|
||||
});
|
||||
|
||||
const mockStorage = MockStorageApi.create();
|
||||
|
||||
it('should render "Sign in" button if user not signed in', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi()],
|
||||
[googleAuthApiRef, getAuthMockApi('')],
|
||||
]}
|
||||
>
|
||||
<CalendarCardContainer />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText('Sign in')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render empty card', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi()],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
]}
|
||||
>
|
||||
<CalendarCardContainer />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText('No events')).toBeInTheDocument();
|
||||
expect(rendered.queryByText('Go to Calendar')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should select primary calendar by default', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi(calendarData, eventData)],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
]}
|
||||
>
|
||||
<CalendarCardContainer />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText(primaryCalendar.summary)).toBeInTheDocument();
|
||||
expect(
|
||||
rendered.queryByText(nonPrimaryCalendar.summary),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should render calendar events', async () => {
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi(calendarData, eventData)],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
]}
|
||||
>
|
||||
<CalendarCardContainer />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(rendered.queryByText('No events')).not.toBeInTheDocument();
|
||||
expect(rendered.queryAllByText('Test event')).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should select stored calendar', async () => {
|
||||
mockStorage
|
||||
.forBucket(gcalendarHomepagePlugin.getId())
|
||||
.set('google_calendars_selected', [nonPrimaryCalendar.id]);
|
||||
|
||||
const rendered = await renderInTestApp(
|
||||
<TestApiProvider
|
||||
apis={[
|
||||
[gcalendarApiRef, getMockedApi(calendarData, eventData)],
|
||||
[googleAuthApiRef, getAuthMockApi()],
|
||||
[storageApiRef, mockStorage],
|
||||
]}
|
||||
>
|
||||
<CalendarCardContainer />
|
||||
</TestApiProvider>,
|
||||
);
|
||||
|
||||
expect(
|
||||
rendered.queryByText(nonPrimaryCalendar.summary),
|
||||
).toBeInTheDocument();
|
||||
expect(
|
||||
rendered.queryByText(primaryCalendar.summary),
|
||||
).not.toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,146 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { sortBy } from 'lodash';
|
||||
import { DateTime } from 'luxon';
|
||||
import React, { useEffect, useMemo, useState } from 'react';
|
||||
|
||||
import { InfoCard, Progress } from '@backstage/core-components';
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
|
||||
import { Box, IconButton, Typography } from '@material-ui/core';
|
||||
import PrevIcon from '@material-ui/icons/NavigateBefore';
|
||||
import NextIcon from '@material-ui/icons/NavigateNext';
|
||||
|
||||
import {
|
||||
useCalendarsQuery,
|
||||
useEventsQuery,
|
||||
useSignIn,
|
||||
useStoredCalendars,
|
||||
} from '../../hooks';
|
||||
import calendarCardIcon from '../../icons/calendarIcon.svg';
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
import { CalendarSelect } from './CalendarSelect';
|
||||
import { SignInContent } from './SignInContent';
|
||||
import { getStartDate } from './util';
|
||||
|
||||
export const CalendarCard = () => {
|
||||
const [date, setDate] = useState(DateTime.now());
|
||||
const analytics = useAnalytics();
|
||||
|
||||
const changeDay = (offset = 1) => {
|
||||
setDate(prev => prev.plus({ day: offset }));
|
||||
analytics.captureEvent('click', 'change date');
|
||||
};
|
||||
|
||||
const { isSignedIn, isInitialized, signIn } = useSignIn();
|
||||
|
||||
useEffect(() => {
|
||||
signIn(true);
|
||||
}, [signIn]);
|
||||
|
||||
const { isLoading: isCalendarLoading, data } = useCalendarsQuery({
|
||||
enabled: isSignedIn,
|
||||
});
|
||||
const calendars = useMemo(() => data?.items || [], [data]);
|
||||
const primaryCalendarId = calendars.find(c => c.primary === true)?.id;
|
||||
const defaultSelectedCalendars = primaryCalendarId ? [primaryCalendarId] : [];
|
||||
const [storedCalendars, setStoredCalendars] = useStoredCalendars(
|
||||
defaultSelectedCalendars,
|
||||
);
|
||||
|
||||
const { events, isLoading: isEventLoading } = useEventsQuery({
|
||||
calendars,
|
||||
selectedCalendars: storedCalendars,
|
||||
enabled: isSignedIn && calendars.length > 0,
|
||||
timeMin: date.startOf('day').toISO(),
|
||||
timeMax: date.endOf('day').toISO(),
|
||||
timeZone: date.zoneName,
|
||||
});
|
||||
|
||||
return (
|
||||
<InfoCard
|
||||
noPadding
|
||||
title={
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box height={24} width={24} mr={1}>
|
||||
<img src={calendarCardIcon} alt="Google Calendar" />
|
||||
</Box>
|
||||
{isSignedIn ? (
|
||||
<>
|
||||
<IconButton onClick={() => changeDay(-1)} size="small">
|
||||
<PrevIcon />
|
||||
</IconButton>
|
||||
<IconButton onClick={() => changeDay(1)} size="small">
|
||||
<NextIcon />
|
||||
</IconButton>
|
||||
<Box mr={0.5} />
|
||||
<Typography variant="h6">
|
||||
{date.toLocaleString({
|
||||
weekday: 'short',
|
||||
month: 'short',
|
||||
day: 'numeric',
|
||||
})}
|
||||
</Typography>
|
||||
|
||||
<Box flex={1} />
|
||||
|
||||
<CalendarSelect
|
||||
calendars={calendars}
|
||||
selectedCalendars={storedCalendars}
|
||||
setSelectedCalendars={setStoredCalendars}
|
||||
disabled={isCalendarLoading || !isSignedIn}
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<Typography variant="h6">Agenda</Typography>
|
||||
)}
|
||||
</Box>
|
||||
}
|
||||
deepLink={{
|
||||
link: 'https://calendar.google.com/',
|
||||
title: 'Go to Calendar',
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
{(isCalendarLoading || !isInitialized || isEventLoading) && (
|
||||
<Box pt={2} pb={2}>
|
||||
<Progress variant="query" />
|
||||
</Box>
|
||||
)}
|
||||
{!isSignedIn && isInitialized && (
|
||||
<SignInContent handleAuthClick={() => signIn(false)} />
|
||||
)}
|
||||
{!isEventLoading && !isCalendarLoading && isSignedIn && (
|
||||
<Box p={1} pb={0} maxHeight={602} overflow="auto">
|
||||
{events.length === 0 && (
|
||||
<Box pt={2} pb={2}>
|
||||
<Typography align="center" variant="h6">
|
||||
No events
|
||||
</Typography>
|
||||
</Box>
|
||||
)}
|
||||
{sortBy(events, [getStartDate]).map(event => (
|
||||
<CalendarEvent
|
||||
key={`${event.calendarId}-${event.id}`}
|
||||
event={event}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</InfoCard>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
import { QueryClient, QueryClientProvider } from 'react-query';
|
||||
|
||||
import { CalendarCard } from './CalendarCard';
|
||||
|
||||
const queryClient = new QueryClient();
|
||||
|
||||
export const CalendarCardContainer = () => {
|
||||
return (
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<CalendarCard />
|
||||
</QueryClientProvider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { fireEvent } from '@testing-library/react';
|
||||
import React from 'react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
|
||||
describe('<CalendarEvent />', () => {
|
||||
const event = {
|
||||
summary: 'Test event',
|
||||
htmlLink: '/calendar-link',
|
||||
start: {
|
||||
dateTime: '2022-02-09T10:15:00',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2022-02-09T10:45:00',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [{ entryPointType: 'video', uri: '/zoom-link' }],
|
||||
},
|
||||
description: 'Test description',
|
||||
attendees: [
|
||||
{
|
||||
email: 'test@test.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('should render calendar event', async () => {
|
||||
const { queryByText, queryByTestId } = await renderInTestApp(
|
||||
<CalendarEvent event={event} />,
|
||||
);
|
||||
expect(queryByText(event.summary)).toBeInTheDocument();
|
||||
expect(queryByTestId('calendar-event-zoom-link')).toBeInTheDocument();
|
||||
expect(queryByTestId('calendar-event-zoom-link')).toHaveAttribute(
|
||||
'href',
|
||||
event.conferenceData.entryPoints[0].uri,
|
||||
);
|
||||
expect(queryByTestId('calendar-event-time')).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should not render time for events longer than 1 day', async () => {
|
||||
const allDayEvent = {
|
||||
summary: 'Test event',
|
||||
start: {
|
||||
date: '2022-02-09',
|
||||
},
|
||||
end: {
|
||||
date: '2022-02-19',
|
||||
},
|
||||
};
|
||||
const { queryByText, queryByTestId } = await renderInTestApp(
|
||||
<CalendarEvent event={allDayEvent} />,
|
||||
);
|
||||
expect(queryByText(allDayEvent.summary)).toBeInTheDocument();
|
||||
expect(queryByTestId('calendar-event-time')).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('should show popover on click', async () => {
|
||||
const { queryByTestId, getByTestId } = await renderInTestApp(
|
||||
<CalendarEvent event={event} />,
|
||||
);
|
||||
expect(queryByTestId('calendar-event-popover')).not.toBeInTheDocument();
|
||||
|
||||
fireEvent.click(getByTestId('calendar-event'));
|
||||
expect(queryByTestId('calendar-event-popover')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,160 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import classnames from 'classnames';
|
||||
import {
|
||||
bindPopover,
|
||||
bindTrigger,
|
||||
usePopupState,
|
||||
} from 'material-ui-popup-state/hooks';
|
||||
import React, { useState } from 'react';
|
||||
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Link,
|
||||
Paper,
|
||||
Popover,
|
||||
Tooltip,
|
||||
Typography,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import zoomIcon from '../../icons/zoomIcon.svg';
|
||||
import { CalendarEventPopoverContent } from './CalendarEventPopoverContent';
|
||||
import { GCalendarEvent, ResponseStatus } from './types';
|
||||
import { getTimePeriod, getZoomLink, isAllDay, isPassed } from './util';
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
event: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(1),
|
||||
cursor: 'pointer',
|
||||
paddingRight: 12,
|
||||
},
|
||||
declined: {
|
||||
textDecoration: 'line-through',
|
||||
},
|
||||
passed: {
|
||||
opacity: 0.6,
|
||||
transition: 'opacity 0.15s ease-in-out',
|
||||
'&:hover': {
|
||||
opacity: 1,
|
||||
},
|
||||
},
|
||||
link: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
display: 'inline-block',
|
||||
padding: 8,
|
||||
borderRadius: '50%',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.grey[100],
|
||||
},
|
||||
},
|
||||
calendarColor: ({ event }: any) => ({
|
||||
width: 8,
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
backgroundColor: event.primary
|
||||
? theme.palette.primary.light
|
||||
: event.backgroundColor,
|
||||
}),
|
||||
}));
|
||||
|
||||
export const CalendarEvent = ({ event }: { event: GCalendarEvent }) => {
|
||||
const classes = useStyles({ event });
|
||||
const popoverState = usePopupState({
|
||||
variant: 'popover',
|
||||
popupId: event.id,
|
||||
disableAutoFocus: true,
|
||||
});
|
||||
const [hovered, setHovered] = useState(false);
|
||||
const analytics = useAnalytics();
|
||||
const zoomLink = getZoomLink(event);
|
||||
|
||||
const { onClick, ...restBindProps } = bindTrigger(popoverState);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Paper
|
||||
onClick={e => {
|
||||
onClick(e);
|
||||
analytics.captureEvent('click', 'event info');
|
||||
}}
|
||||
{...restBindProps}
|
||||
onMouseEnter={() => setHovered(true)}
|
||||
onMouseLeave={() => setHovered(false)}
|
||||
elevation={hovered ? 4 : 1}
|
||||
className={classnames(classes.event, {
|
||||
[classes.passed]: isPassed(event),
|
||||
})}
|
||||
data-testid="calendar-event"
|
||||
>
|
||||
<Box className={classes.calendarColor} mr={1} alignSelf="stretch" />
|
||||
<Box flex={1} pt={1} pb={1}>
|
||||
<Typography
|
||||
variant="subtitle2"
|
||||
className={classnames({
|
||||
[classes.declined]:
|
||||
event.responseStatus === ResponseStatus.declined,
|
||||
})}
|
||||
>
|
||||
{event.summary}
|
||||
</Typography>
|
||||
{!isAllDay(event) && (
|
||||
<Typography variant="body2" data-testid="calendar-event-time">
|
||||
{getTimePeriod(event)}
|
||||
</Typography>
|
||||
)}
|
||||
</Box>
|
||||
|
||||
{zoomLink && (
|
||||
<Tooltip title="Join Zoom Meeting">
|
||||
<Link
|
||||
data-testid="calendar-event-zoom-link"
|
||||
className={classes.link}
|
||||
href={zoomLink}
|
||||
target="_blank"
|
||||
onClick={e => {
|
||||
e.stopPropagation();
|
||||
analytics.captureEvent('click', 'zoom link');
|
||||
}}
|
||||
>
|
||||
<img src={zoomIcon} alt="Zoom link" />
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Paper>
|
||||
|
||||
<Popover
|
||||
{...bindPopover(popoverState)}
|
||||
anchorOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'left',
|
||||
}}
|
||||
transformOrigin={{
|
||||
vertical: 'top',
|
||||
horizontal: 'center',
|
||||
}}
|
||||
data-testid="calendar-event-popover"
|
||||
>
|
||||
<CalendarEventPopoverContent event={event} />
|
||||
</Popover>
|
||||
</>
|
||||
);
|
||||
};
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { CalendarEventPopoverContent } from './CalendarEventPopoverContent';
|
||||
|
||||
describe('<CalendarEventPopoverContent />', () => {
|
||||
const event = {
|
||||
summary: 'Test event',
|
||||
htmlLink: '/calendar-link',
|
||||
conferenceData: {
|
||||
entryPoints: [{ entryPointType: 'video', uri: '/zoom-link' }],
|
||||
},
|
||||
description: 'Test description',
|
||||
attendees: [
|
||||
{
|
||||
email: 'test@test.com',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it('should render event info', async () => {
|
||||
const { queryByText, queryByTestId } = await renderInTestApp(
|
||||
<CalendarEventPopoverContent event={event} />,
|
||||
);
|
||||
expect(queryByText(event.summary)).toBeInTheDocument();
|
||||
expect(queryByText(event.description)).toBeInTheDocument();
|
||||
expect(queryByText(event.attendees[0].email)).toBeInTheDocument();
|
||||
expect(queryByText('Join Zoom Meeting')).toBeInTheDocument();
|
||||
expect(queryByText('Join Zoom Meeting')?.closest('a')).toHaveAttribute(
|
||||
'href',
|
||||
event.conferenceData.entryPoints[0].uri,
|
||||
);
|
||||
expect(queryByTestId('open-calendar-link')).toHaveAttribute(
|
||||
'href',
|
||||
event.htmlLink,
|
||||
);
|
||||
expect(queryByText(event.attendees[0].email)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { sortBy } from 'lodash';
|
||||
import React from 'react';
|
||||
import sanitizeHtml from 'sanitize-html';
|
||||
|
||||
import { useAnalytics } from '@backstage/core-plugin-api';
|
||||
|
||||
import {
|
||||
Box,
|
||||
Divider,
|
||||
IconButton,
|
||||
Link,
|
||||
Tooltip,
|
||||
Typography,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
|
||||
|
||||
import { AttendeeChip } from './AttendeeChip';
|
||||
import { GCalendarEvent } from './types';
|
||||
import { getTimePeriod, getZoomLink } from './util';
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
description: {
|
||||
wordBreak: 'break-word',
|
||||
'& a': {
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: 500,
|
||||
},
|
||||
},
|
||||
divider: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
type CalendarEventPopoverProps = {
|
||||
event: GCalendarEvent;
|
||||
};
|
||||
|
||||
export const CalendarEventPopoverContent = ({
|
||||
event,
|
||||
}: CalendarEventPopoverProps) => {
|
||||
const classes = useStyles({ event });
|
||||
const analytics = useAnalytics();
|
||||
const zoomLink = getZoomLink(event);
|
||||
|
||||
return (
|
||||
<Box display="flex" flexDirection="column" width={400} p={2}>
|
||||
<Box display="flex" alignItems="center">
|
||||
<Box flex={1}>
|
||||
<Typography variant="h6">{event.summary}</Typography>
|
||||
<Typography variant="subtitle2">{getTimePeriod(event)}</Typography>
|
||||
</Box>
|
||||
{event.htmlLink && (
|
||||
<Tooltip title="Open in Calendar">
|
||||
<Link
|
||||
data-testid="open-calendar-link"
|
||||
href={event.htmlLink}
|
||||
target="_blank"
|
||||
onClick={_e =>
|
||||
analytics.captureEvent('click', 'open in calendar')
|
||||
}
|
||||
>
|
||||
<IconButton>
|
||||
<ArrowForwardIcon />
|
||||
</IconButton>
|
||||
</Link>
|
||||
</Tooltip>
|
||||
)}
|
||||
</Box>
|
||||
{zoomLink && (
|
||||
<Link
|
||||
href={zoomLink}
|
||||
target="_blank"
|
||||
onClick={_e => analytics.captureEvent('click', 'zoom link')}
|
||||
>
|
||||
Join Zoom Meeting
|
||||
</Link>
|
||||
)}
|
||||
|
||||
{event.description && (
|
||||
<>
|
||||
<Divider className={classes.divider} variant="fullWidth" />
|
||||
<Box
|
||||
className={classes.description}
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: sanitizeHtml(event.description),
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
{event.attendees && (
|
||||
<>
|
||||
<Divider className={classes.divider} variant="fullWidth" />
|
||||
<Box>
|
||||
<Typography variant="subtitle2">Attendees</Typography>
|
||||
<Box mb={1} />
|
||||
{sortBy(event.attendees || [], 'email').map(user => (
|
||||
<AttendeeChip key={user.email} user={user} />
|
||||
))}
|
||||
</Box>
|
||||
</>
|
||||
)}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { sortBy } from 'lodash';
|
||||
import React from 'react';
|
||||
|
||||
import {
|
||||
Checkbox,
|
||||
FormControl,
|
||||
Input,
|
||||
ListItemText,
|
||||
MenuItem,
|
||||
Select,
|
||||
Typography,
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { GCalendar } from './types';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
formControl: {
|
||||
width: 120,
|
||||
},
|
||||
selectedCalendars: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
});
|
||||
|
||||
type CalendarSelectProps = {
|
||||
disabled: boolean;
|
||||
selectedCalendars?: string[];
|
||||
setSelectedCalendars: (value: string[]) => void;
|
||||
calendars: GCalendar[];
|
||||
};
|
||||
|
||||
export const CalendarSelect = ({
|
||||
disabled,
|
||||
selectedCalendars = [],
|
||||
setSelectedCalendars,
|
||||
calendars,
|
||||
}: CalendarSelectProps) => {
|
||||
const classes = useStyles();
|
||||
|
||||
return (
|
||||
<FormControl className={classes.formControl}>
|
||||
<Select
|
||||
labelId="calendars-label"
|
||||
disabled={disabled || calendars.length === 0}
|
||||
multiple
|
||||
value={selectedCalendars}
|
||||
onChange={async e => setSelectedCalendars(e.target.value as string[])}
|
||||
input={<Input />}
|
||||
renderValue={selected => (
|
||||
<Typography className={classes.selectedCalendars} variant="body2">
|
||||
{calendars
|
||||
.filter(c => c.id && (selected as string[]).includes(c.id))
|
||||
.map(c => c.summary)
|
||||
.join(', ')}
|
||||
</Typography>
|
||||
)}
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
style: {
|
||||
width: 350,
|
||||
},
|
||||
},
|
||||
}}
|
||||
>
|
||||
{sortBy(calendars, 'summary').map(c => (
|
||||
<MenuItem key={c.id} value={c.id}>
|
||||
<Checkbox checked={selectedCalendars.includes(c.id!)} />
|
||||
<ListItemText primary={c.summary} />
|
||||
</MenuItem>
|
||||
))}
|
||||
</Select>
|
||||
</FormControl>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import React from 'react';
|
||||
|
||||
import { Box, Button, styled } from '@material-ui/core';
|
||||
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
import { events } from './signInEventMock';
|
||||
|
||||
type SignInContentProps = {
|
||||
handleAuthClick: React.MouseEventHandler<HTMLElement>;
|
||||
};
|
||||
|
||||
const TransparentBox = styled(Box)({
|
||||
opacity: 0.3,
|
||||
filter: 'blur(1.5px)',
|
||||
});
|
||||
|
||||
export const SignInContent = ({ handleAuthClick }: SignInContentProps) => {
|
||||
return (
|
||||
<Box position="relative" height="100%" width="100%">
|
||||
<TransparentBox p={1}>
|
||||
{events.map(event => (
|
||||
<CalendarEvent key={event.id} event={event} />
|
||||
))}
|
||||
</TransparentBox>
|
||||
|
||||
<Box
|
||||
height="100%"
|
||||
width="100%"
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
position="absolute"
|
||||
left={0}
|
||||
top={0}
|
||||
>
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={handleAuthClick}
|
||||
size="large"
|
||||
>
|
||||
Sign in
|
||||
</Button>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,16 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { CalendarCardContainer } from './CalendarCardContainer';
|
||||
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { GCalendarEvent } from './types';
|
||||
|
||||
export const events: GCalendarEvent[] = [
|
||||
{
|
||||
id: '1',
|
||||
htmlLink: 'https://www.google.com/calendar/',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T09:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T10:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
htmlLink: 'https://www.google.com/calendar/',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T10:30:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T10:45:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
htmlLink: 'https://www.google.com/calendar',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T12:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T13:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
label: 'zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
htmlLink: 'https://www.google.com/calendar',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T15:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T16:30:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
htmlLink: 'https://www.google.com/calendar',
|
||||
summary: 'Backstage Community Sessions',
|
||||
start: {
|
||||
dateTime: '2021-12-07T17:00:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
end: {
|
||||
dateTime: '2021-12-07T17:30:00+01:00',
|
||||
timeZone: 'Europe/London',
|
||||
},
|
||||
conferenceData: {
|
||||
entryPoints: [
|
||||
{
|
||||
entryPointType: 'video',
|
||||
uri: 'https://zoom.us',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/// <reference types="gapi.auth2" />
|
||||
/// <reference types="gapi.client.calendar" />
|
||||
|
||||
export type GCalendar = gapi.client.calendar.CalendarListEntry;
|
||||
|
||||
export type EventAttendee = gapi.client.calendar.EventAttendee;
|
||||
|
||||
export type GCalendarEvent = gapi.client.calendar.Event &
|
||||
Pick<GCalendar, 'backgroundColor' | 'primary'> &
|
||||
Pick<EventAttendee, 'responseStatus'> & {
|
||||
calendarId?: string;
|
||||
};
|
||||
|
||||
export enum ResponseStatus {
|
||||
needsAction = 'needsAction',
|
||||
accepted = 'accepted',
|
||||
declined = 'declined',
|
||||
maybe = 'tentative',
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { GCalendarEvent } from './types';
|
||||
|
||||
export function getZoomLink(event: GCalendarEvent) {
|
||||
const videoEntrypoint = event.conferenceData?.entryPoints?.find(
|
||||
e => e.entryPointType === 'video',
|
||||
);
|
||||
|
||||
return videoEntrypoint?.uri ?? '';
|
||||
}
|
||||
|
||||
export function getTimePeriod(event: GCalendarEvent) {
|
||||
if (isAllDay(event)) {
|
||||
return getAllDayTimePeriod(event);
|
||||
}
|
||||
|
||||
const format: Intl.DateTimeFormatOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
};
|
||||
|
||||
const startTime = DateTime.fromISO(event.start?.dateTime || '');
|
||||
const endTime = DateTime.fromISO(event.end?.dateTime || '');
|
||||
|
||||
return `${startTime.toLocaleString(format)} - ${endTime.toLocaleString(
|
||||
format,
|
||||
)}`;
|
||||
}
|
||||
|
||||
function getAllDayTimePeriod(event: GCalendarEvent) {
|
||||
const format: Intl.DateTimeFormatOptions = { month: 'long', day: 'numeric' };
|
||||
const startTime = DateTime.fromISO(
|
||||
event.start?.dateTime || event.start?.date || '',
|
||||
);
|
||||
const endTime = DateTime.fromISO(
|
||||
event.end?.dateTime || event.end?.date || '',
|
||||
).minus({ day: 1 });
|
||||
|
||||
if (startTime.toISO() === endTime.toISO()) {
|
||||
return startTime.toLocaleString(format);
|
||||
}
|
||||
|
||||
return `${startTime.toLocaleString(format)} - ${endTime.toLocaleString(
|
||||
format,
|
||||
)}`;
|
||||
}
|
||||
|
||||
export function isPassed(event: GCalendarEvent) {
|
||||
if (!event.end?.dateTime && !event.end?.date) return false;
|
||||
const eventDate = DateTime.fromISO(event.end?.dateTime || event.end?.date!);
|
||||
return DateTime.now() >= eventDate;
|
||||
}
|
||||
|
||||
export function isAllDay(event: GCalendarEvent) {
|
||||
if (event.start?.date || event.end?.date) {
|
||||
return true;
|
||||
}
|
||||
const startTime = DateTime.fromISO(event.start?.dateTime || '');
|
||||
const endTime = DateTime.fromISO(event.end?.dateTime || '');
|
||||
|
||||
return endTime.diff(startTime, 'day').days >= 1;
|
||||
}
|
||||
|
||||
export function getStartDate(event: GCalendarEvent) {
|
||||
return DateTime.fromISO(event.start?.dateTime || event.start?.date || '');
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { useCalendarsQuery } from './useCalendarsQuery';
|
||||
export { useEventsQuery } from './useEventsQuery';
|
||||
export { useSignIn } from './useSignIn';
|
||||
export { useStoredCalendars } from './useStoredCalendars';
|
||||
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useQuery } from 'react-query';
|
||||
|
||||
import { errorApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { gcalendarApiRef } from '../api';
|
||||
|
||||
type Options = {
|
||||
enabled: boolean;
|
||||
refreshTime?: number;
|
||||
};
|
||||
|
||||
export const useCalendarsQuery = ({ enabled }: Options) => {
|
||||
const calendarApi = useApi(gcalendarApiRef);
|
||||
const errorApi = useApi(errorApiRef);
|
||||
|
||||
return useQuery(
|
||||
['calendars'],
|
||||
async () =>
|
||||
calendarApi.getCalendars({
|
||||
minAccessRole: 'reader',
|
||||
}),
|
||||
{
|
||||
enabled,
|
||||
keepPreviousData: true,
|
||||
refetchInterval: 3600000,
|
||||
onError: () => {
|
||||
errorApi.post({
|
||||
name: 'API error',
|
||||
message: 'Failed to fetch calendars.',
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,96 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { compact, unescape } from 'lodash';
|
||||
import { useMemo } from 'react';
|
||||
import { useQueries } from 'react-query';
|
||||
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { gcalendarApiRef } from '../api';
|
||||
import { GCalendar, GCalendarEvent } from '../components/CalendarCard/types';
|
||||
|
||||
type Options = {
|
||||
selectedCalendars?: string[];
|
||||
timeMin: string;
|
||||
timeMax: string;
|
||||
enabled: boolean;
|
||||
calendars: GCalendar[];
|
||||
timeZone: string;
|
||||
refreshTime?: number;
|
||||
};
|
||||
|
||||
export const useEventsQuery = ({
|
||||
selectedCalendars = [],
|
||||
calendars = [],
|
||||
enabled,
|
||||
timeMin,
|
||||
timeMax,
|
||||
timeZone,
|
||||
}: Options) => {
|
||||
const calendarApi = useApi(gcalendarApiRef);
|
||||
const eventQueries = useQueries(
|
||||
selectedCalendars
|
||||
.filter(id => calendars.find(c => c.id === id))
|
||||
.map(calendarId => {
|
||||
const calendar = calendars.find(c => c.id === calendarId);
|
||||
|
||||
return {
|
||||
queryKey: ['calendarEvents', calendarId, timeMin, timeMax],
|
||||
enabled,
|
||||
initialData: [],
|
||||
refetchInterval: 60000,
|
||||
refetchIntervalInBackground: true,
|
||||
queryFn: async (): Promise<GCalendarEvent[]> => {
|
||||
const data = await calendarApi.getEvents(calendarId, {
|
||||
calendarId,
|
||||
timeMin,
|
||||
timeMax,
|
||||
showDeleted: false,
|
||||
singleEvents: true,
|
||||
maxResults: 100,
|
||||
orderBy: 'startTime',
|
||||
timeZone,
|
||||
});
|
||||
|
||||
return (data.items || []).map(event => {
|
||||
const responseStatus = event.attendees?.find(
|
||||
a => !!a.self,
|
||||
)?.responseStatus;
|
||||
|
||||
return {
|
||||
...event,
|
||||
summary: unescape(event.summary || ''),
|
||||
calendarId,
|
||||
backgroundColor: calendar?.backgroundColor,
|
||||
primary: !!calendar?.primary,
|
||||
responseStatus,
|
||||
};
|
||||
});
|
||||
},
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const events = useMemo(
|
||||
() => compact(eventQueries.map(({ data }) => data).flat()),
|
||||
[eventQueries],
|
||||
);
|
||||
|
||||
const isLoading =
|
||||
!!eventQueries.find(q => q.isFetching) && events.length === 0;
|
||||
|
||||
return { events, isLoading };
|
||||
};
|
||||
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useCallback, useState } from 'react';
|
||||
|
||||
import { googleAuthApiRef, useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
export const useSignIn = () => {
|
||||
const [isSignedIn, setSignedIn] = useState(false);
|
||||
const [isInitialized, setInitialized] = useState(false);
|
||||
const authApi = useApi(googleAuthApiRef);
|
||||
|
||||
const signIn = useCallback(
|
||||
async (optional = false) => {
|
||||
const token = await authApi.getAccessToken(
|
||||
'https://www.googleapis.com/auth/calendar.readonly',
|
||||
{
|
||||
optional,
|
||||
instantPopup: !optional,
|
||||
},
|
||||
);
|
||||
|
||||
setSignedIn(!!token);
|
||||
setInitialized(true);
|
||||
},
|
||||
[authApi, setSignedIn],
|
||||
);
|
||||
|
||||
return { isSignedIn, isInitialized, signIn };
|
||||
};
|
||||
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { useApi, storageApiRef } from '@backstage/core-plugin-api';
|
||||
import { useObservable } from 'react-use';
|
||||
|
||||
import { gcalendarHomepagePlugin } from '../plugin';
|
||||
|
||||
export enum LocalStorageKeys {
|
||||
selectedCalendars = 'google_calendars_selected',
|
||||
}
|
||||
|
||||
export function useStoredCalendars(
|
||||
defaultValue: string[],
|
||||
): [string[], (value: string[]) => void] {
|
||||
const storageBucket = gcalendarHomepagePlugin.getId();
|
||||
const storageKey = LocalStorageKeys.selectedCalendars;
|
||||
const storageApi = useApi(storageApiRef).forBucket(storageBucket);
|
||||
const setValue = (value: string[]) => {
|
||||
storageApi.set(storageKey, value);
|
||||
};
|
||||
const snapshot = useObservable(
|
||||
storageApi.observe$<string[]>(storageKey),
|
||||
storageApi.snapshot(storageKey),
|
||||
);
|
||||
let result: string[];
|
||||
if (snapshot.presence === 'absent') {
|
||||
result = defaultValue;
|
||||
} else {
|
||||
result = snapshot.value!;
|
||||
}
|
||||
return [result, setValue];
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="186 38 76 76">
|
||||
<path fill="#fff" d="M244 56h-40v40h40V56z" />
|
||||
<path fill="#EA4335" d="M244 114l18-18h-18v18z" />
|
||||
<path fill="#FBBC04" d="M262 56h-18v40h18V56z" />
|
||||
<path fill="#34A853" d="M244 96h-40v18h40V96z" />
|
||||
<path fill="#188038" d="M186 96v12c0 3.315 2.685 6 6 6h12V96h-18z" />
|
||||
<path fill="#1967D2" d="M262 56V44c0-3.315-2.685-6-6-6h-12v18h18z" />
|
||||
<path fill="#4285F4" d="M244 38h-52c-3.315 0 -6 2.685-6 6v52h18V56h40V38z" />
|
||||
<path fill="#4285F4" d="M212.205 87.03c-1.495-1.01-2.53-2.485-3.095-4.435l3.47-1.43c.315 1.2.865 2.13 1.65 2.79.78.66 1.73.985 2.84.985 1.135 0 2.11-.345 2.925-1.035s1.225-1.57 1.225-2.635c0-1.09-.43-1.98-1.29-2.67-.86-.69-1.94-1.035-3.23-1.035h-2.005V74.13h1.8c1.11 0 2.045-.3 2.805-.9.76-.6 1.14-1.42 1.14-2.465 0 -.93-.34-1.67-1.02-2.225-.68-.555-1.54-.835-2.585-.835-1.02 0 -1.83.27-2.43.815a4.784 4.784 0 0 0 -1.31 2.005l-3.435-1.43c.455-1.29 1.29-2.43 2.515-3.415 1.225-.985 2.79-1.48 4.69-1.48 1.405 0 2.67.27 3.79.815 1.12.545 2 1.3 2.635 2.26.635.965.95 2.045.95 3.245 0 1.225-.295 2.26-.885 3.11-.59.85-1.315 1.5-2.175 1.955v.205a6.605 6.605 0 0 1 2.79 2.175c.725.975 1.09 2.14 1.09 3.5 0 1.36-.345 2.575-1.035 3.64s-1.645 1.905-2.855 2.515c-1.215.61-2.58.92-4.095.92-1.755.005-3.375-.5-4.87-1.51zM233.52 69.81l-3.81 2.755-1.905-2.89 6.835-4.93h2.62V88h-3.74V69.81z" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,5 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 48 48">
|
||||
<circle cx="24" cy="24" r="20" fill="#2196f3" />
|
||||
<path fill="#fff" d="M29,31H14c-1.657,0-3-1.343-3-3V17h15c1.657,0,3,1.343,3,3V31z" />
|
||||
<polygon fill="#fff" points="37,31 31,27 31,21 37,17" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 265 B |
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
export { gcalendarHomepagePlugin, CalendarCard } from './plugin';
|
||||
export * from './api';
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { gcalendarHomepagePlugin } from './plugin';
|
||||
|
||||
describe('gcalendar-homepage', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(gcalendarHomepagePlugin).toBeDefined();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import {
|
||||
createApiFactory,
|
||||
createComponentExtension,
|
||||
createPlugin,
|
||||
googleAuthApiRef,
|
||||
} from '@backstage/core-plugin-api';
|
||||
|
||||
import { GCalendarApiClient, gcalendarApiRef } from './api';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
export const gcalendarHomepagePlugin = createPlugin({
|
||||
id: 'gcalendar-homepage',
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
apis: [
|
||||
createApiFactory({
|
||||
api: gcalendarApiRef,
|
||||
deps: { authApi: googleAuthApiRef },
|
||||
factory(deps) {
|
||||
return new GCalendarApiClient(deps);
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
|
||||
export const CalendarCard = gcalendarHomepagePlugin.provide(
|
||||
createComponentExtension({
|
||||
name: 'CalendarCard',
|
||||
component: {
|
||||
lazy: () =>
|
||||
import('./components/CalendarCard').then(m => m.CalendarCardContainer),
|
||||
},
|
||||
}),
|
||||
);
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'gcalendar-homepage',
|
||||
});
|
||||
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright 2022 The Backstage Authors
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import '@testing-library/jest-dom';
|
||||
import 'cross-fetch/polyfill';
|
||||
@@ -1302,6 +1302,13 @@
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/runtime@^7.17.0", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2":
|
||||
version "7.17.2"
|
||||
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz#66f68591605e59da47523c631416b18508779941"
|
||||
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
|
||||
dependencies:
|
||||
regenerator-runtime "^0.13.4"
|
||||
|
||||
"@babel/template@^7.16.7", "@babel/template@^7.3.3":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.npmjs.org/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155"
|
||||
@@ -1793,11 +1800,49 @@
|
||||
ms "^2.1.3"
|
||||
secure-json-parse "^2.4.0"
|
||||
|
||||
"@emotion/cache@^11.7.1":
|
||||
version "11.7.1"
|
||||
resolved "https://registry.npmjs.org/@emotion/cache/-/cache-11.7.1.tgz#08d080e396a42e0037848214e8aa7bf879065539"
|
||||
integrity sha512-r65Zy4Iljb8oyjtLeCuBH8Qjiy107dOYC6SJq7g7GV5UCQWMObY4SJDPGFjiiVpPrOJ2hmJOoBiYTC7hwx9E2A==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
"@emotion/sheet" "^1.1.0"
|
||||
"@emotion/utils" "^1.0.0"
|
||||
"@emotion/weak-memoize" "^0.2.5"
|
||||
stylis "4.0.13"
|
||||
|
||||
"@emotion/hash@^0.8.0":
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz#bbbff68978fefdbe68ccb533bc8cbe1d1afb5413"
|
||||
integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==
|
||||
|
||||
"@emotion/is-prop-valid@^1.1.1":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.1.2.tgz#34ad6e98e871aa6f7a20469b602911b8b11b3a95"
|
||||
integrity sha512-3QnhqeL+WW88YjYbQL5gUIkthuMw7a0NGbZ7wfFVk2kg/CK5w8w5FFa0RzWjyY1+sujN0NWbtSHH6OJmWHtJpQ==
|
||||
dependencies:
|
||||
"@emotion/memoize" "^0.7.4"
|
||||
|
||||
"@emotion/memoize@^0.7.4":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.5.tgz#2c40f81449a4e554e9fc6396910ed4843ec2be50"
|
||||
integrity sha512-igX9a37DR2ZPGYtV6suZ6whr8pTFtyHL3K/oLUotxpSVO2ASaprmAe2Dkq7tBo7CRY7MMDrAa9nuQP9/YG8FxQ==
|
||||
|
||||
"@emotion/sheet@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.1.0.tgz#56d99c41f0a1cda2726a05aa6a20afd4c63e58d2"
|
||||
integrity sha512-u0AX4aSo25sMAygCuQTzS+HsImZFuS8llY8O7b9MDRzbJM0kVJlAz6KNDqcG7pOuQZJmj/8X/rAW+66kMnMW+g==
|
||||
|
||||
"@emotion/utils@^1.0.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/@emotion/utils/-/utils-1.1.0.tgz#86b0b297f3f1a0f2bdb08eeac9a2f49afd40d0cf"
|
||||
integrity sha512-iRLa/Y4Rs5H/f2nimczYmS5kFJEbpiVvgN3XVfZ022IYhuNA1IRSHEizcof88LtCTXtl9S2Cxt32KgaXEu72JQ==
|
||||
|
||||
"@emotion/weak-memoize@^0.2.5":
|
||||
version "0.2.5"
|
||||
resolved "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.2.5.tgz#8eed982e2ee6f7f4e44c253e12962980791efd46"
|
||||
integrity sha512-6U71C2Wp7r5XtFtQzYrW5iKFT67OixrSxjI4MptCHzdSVlgabczzqLe0ZSgnub/5Kp4hSbpDB1tMytZY9pwxxA==
|
||||
|
||||
"@endemolshinegroup/cosmiconfig-typescript-loader@3.0.2":
|
||||
version "3.0.2"
|
||||
resolved "https://registry.npmjs.org/@endemolshinegroup/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-3.0.2.tgz#eea4635828dde372838b0909693ebd9aafeec22d"
|
||||
@@ -4004,6 +4049,13 @@
|
||||
prop-types "^15.7.2"
|
||||
react-is "^16.8.0 || ^17.0.0"
|
||||
|
||||
"@maxim_mazurok/gapi.client.calendar@latest":
|
||||
version "3.0.20220211"
|
||||
resolved "https://registry.npmjs.org/@maxim_mazurok/gapi.client.calendar/-/gapi.client.calendar-3.0.20220211.tgz#20125706c761c75219de15788cee100b7933fec6"
|
||||
integrity sha512-ehK4pF4pJXjKFEXp+uiteq1hLAcf28ZbCxgzc6jL9XpNFDSpbhY2iPGU4lkWLS3ae4qrlScFCnmExem5Hg1gZQ==
|
||||
dependencies:
|
||||
"@types/gapi.client" "*"
|
||||
|
||||
"@microsoft/api-documenter@^7.15.0":
|
||||
version "7.15.0"
|
||||
resolved "https://registry.npmjs.org/@microsoft/api-documenter/-/api-documenter-7.15.0.tgz#e6cf24fc0e2f18a71dcf4c5c8100cc083167a81e"
|
||||
@@ -4089,6 +4141,115 @@
|
||||
outvariant "^1.2.0"
|
||||
strict-event-emitter "^0.2.0"
|
||||
|
||||
"@mui/base@5.0.0-alpha.69":
|
||||
version "5.0.0-alpha.69"
|
||||
resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.69.tgz#8511198d760de0795870f5ec63e53db73ba801ec"
|
||||
integrity sha512-IxUUj/lkilCTNBIybQxyQGW/zpxFp490G0QBQJgRp9TJkW2PWSTLvAH7gcH0YHd0L2TAf1TRgfdemoRseMzqQA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@emotion/is-prop-valid" "^1.1.1"
|
||||
"@mui/utils" "^5.4.2"
|
||||
"@popperjs/core" "^2.4.4"
|
||||
clsx "^1.1.1"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^17.0.2"
|
||||
|
||||
"@mui/icons-material@^5.0.0":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.4.2.tgz#b2fd2c6c81d2d275e17ce40bd50c63cb197d324b"
|
||||
integrity sha512-7c+G3jBT+e+pN0a9DJ0Bd8Kr1Vy6os5Q1yd2aXcwuhlRI3uzJBLJ8sX6FSWoh5DSEBchb7Bsk1uHz6U0YN9l+Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
|
||||
"@mui/material@^5.0.0":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/material/-/material-5.4.2.tgz#04ea6632d7ca600a2ae528f6f140ef0af9c01434"
|
||||
integrity sha512-jmeLWEO6AA6g7HErhI3MXVGaMZtqDZjDwcHCg24WY954wO38Xn0zJ53VfpFc44ZTJLV9Ejd7ci9fLlG/HmJCeg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@mui/base" "5.0.0-alpha.69"
|
||||
"@mui/system" "^5.4.2"
|
||||
"@mui/types" "^7.1.2"
|
||||
"@mui/utils" "^5.4.2"
|
||||
"@types/react-transition-group" "^4.4.4"
|
||||
clsx "^1.1.1"
|
||||
csstype "^3.0.10"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^17.0.2"
|
||||
react-transition-group "^4.4.2"
|
||||
|
||||
"@mui/private-theming@^5.4.2":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.4.2.tgz#f0a05f908456a2f7b87ccb6fc3b6e1faae9d89e6"
|
||||
integrity sha512-mlPDYYko4wIcwXjCPEmOWbNTT4DZ6h9YHdnRtQPnWM28+TRUHEo7SbydnnmVDQLRXUfaH4Y6XtEHIfBNPE/SLg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@mui/utils" "^5.4.2"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@mui/styled-engine@^5.4.2":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.4.2.tgz#e04903e06bd49fd10072a44ff38e13f5481bb64d"
|
||||
integrity sha512-tz9p3aRtzXHKAg7x3BgP0hVQEoGKaxNCFxsJ+d/iqEHYvywWFSs6oxqYAvDHIRpvMlUZyPNoTrkcNnbdMmH/ng==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@emotion/cache" "^11.7.1"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@mui/styles@^5.0.0":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/styles/-/styles-5.4.2.tgz#e0dadfc5de8255605f23c2f909f3669f0911bb88"
|
||||
integrity sha512-BX75fNHmRF51yove9dBkH28gpSFjClOPDEnUwLTghPYN913OsqViS/iuCd61dxzygtEEmmeYuWfQjxu/F6vF5g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@emotion/hash" "^0.8.0"
|
||||
"@mui/private-theming" "^5.4.2"
|
||||
"@mui/types" "^7.1.2"
|
||||
"@mui/utils" "^5.4.2"
|
||||
clsx "^1.1.1"
|
||||
csstype "^3.0.10"
|
||||
hoist-non-react-statics "^3.3.2"
|
||||
jss "^10.8.2"
|
||||
jss-plugin-camel-case "^10.8.2"
|
||||
jss-plugin-default-unit "^10.8.2"
|
||||
jss-plugin-global "^10.8.2"
|
||||
jss-plugin-nested "^10.8.2"
|
||||
jss-plugin-props-sort "^10.8.2"
|
||||
jss-plugin-rule-value-function "^10.8.2"
|
||||
jss-plugin-vendor-prefixer "^10.8.2"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@mui/system@^5.4.2":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/system/-/system-5.4.2.tgz#8166e406ba4628950bd79cec8159de25d5aef162"
|
||||
integrity sha512-QegBVu6fxUNov1X9bWc1MZUTeV3A5g9PIpli7d0kzkGfq6JzrJWuPlhSPZ+6hlWmWky+bbAXhU65Qz8atWxDGw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@mui/private-theming" "^5.4.2"
|
||||
"@mui/styled-engine" "^5.4.2"
|
||||
"@mui/types" "^7.1.2"
|
||||
"@mui/utils" "^5.4.2"
|
||||
clsx "^1.1.1"
|
||||
csstype "^3.0.10"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
"@mui/types@^7.1.2":
|
||||
version "7.1.2"
|
||||
resolved "https://registry.npmjs.org/@mui/types/-/types-7.1.2.tgz#4f3678ae77a7a3efab73b6e040469cc6df2144ac"
|
||||
integrity sha512-SD7O1nVzqG+ckQpFjDhXPZjRceB8HQFHEvdLLrPhlJy4lLbwEBbxK74Tj4t6Jgk0fTvLJisuwOutrtYe9P/xBQ==
|
||||
|
||||
"@mui/utils@^5.4.2":
|
||||
version "5.4.2"
|
||||
resolved "https://registry.npmjs.org/@mui/utils/-/utils-5.4.2.tgz#3edda8f80de235418fff0424ee66e2a49793ec01"
|
||||
integrity sha512-646dBCC57MXTo/Gf3AnZSHRHznaTETQq5x7AWp5FRQ4jPeyT4WSs18cpJVwkV01cAHKh06pNQTIufIALIWCL5g==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.17.0"
|
||||
"@types/prop-types" "^15.7.4"
|
||||
"@types/react-is" "^16.7.1 || ^17.0.0"
|
||||
prop-types "^15.7.2"
|
||||
react-is "^17.0.2"
|
||||
|
||||
"@n1ru4l/graphql-live-query@0.9.0", "@n1ru4l/graphql-live-query@^0.9.0":
|
||||
version "0.9.0"
|
||||
resolved "https://registry.npmjs.org/@n1ru4l/graphql-live-query/-/graphql-live-query-0.9.0.tgz#defaebdd31f625bee49e6745934f36312532b2bc"
|
||||
@@ -4677,6 +4838,11 @@
|
||||
resolved "https://registry.npmjs.org/@panva/asn1.js/-/asn1.js-1.0.0.tgz#dd55ae7b8129e02049f009408b97c61ccf9032f6"
|
||||
integrity sha512-UdkG3mLEqXgnlKsWanWcgb6dOjUzJ+XC5f+aWw30qrtjxeNUSfKX1cd5FBzOaXQumoe9nIqeZUvrRJS03HCCtw==
|
||||
|
||||
"@popperjs/core@^2.4.4":
|
||||
version "2.11.2"
|
||||
resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.2.tgz#830beaec4b4091a9e9398ac50f865ddea52186b9"
|
||||
integrity sha512-92FRmppjjqz29VMJ2dn+xdyXZBrMlE42AV6Kq6BwjWV7CNUW1hs2FtxSNLQE+gJhaZ6AAmYuO9y8dshhcBl7vA==
|
||||
|
||||
"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf"
|
||||
@@ -5737,6 +5903,30 @@
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/gapi.auth2@^0.0.56":
|
||||
version "0.0.56"
|
||||
resolved "https://registry.npmjs.org/@types/gapi.auth2/-/gapi.auth2-0.0.56.tgz#2f7031f79390b8401e7950d8277ada874fd2731c"
|
||||
integrity sha512-kGaBtGVCqGS3Y05L56dGVlBpJflxLfwA0zpMQnQgGRFk1tsMPbQnogG51UQjt1vCuYfRO0Jd9/K5KDtzjAbMkA==
|
||||
dependencies:
|
||||
"@types/gapi" "*"
|
||||
|
||||
"@types/gapi.client.calendar@^3.0.10":
|
||||
version "3.0.10"
|
||||
resolved "https://registry.npmjs.org/@types/gapi.client.calendar/-/gapi.client.calendar-3.0.10.tgz#4b089d9af2753a07cf1d46adc83b1a7cedb8e355"
|
||||
integrity sha512-NUStEVbHPOhFsw4cWE2CThe5eKpTlmz+fSu8mvEc7j+IDVNgk1kS4C6hZzBCdlIjFfOzdQM3Cyqkt5kt7ze3kA==
|
||||
dependencies:
|
||||
"@maxim_mazurok/gapi.client.calendar" latest
|
||||
|
||||
"@types/gapi.client@*":
|
||||
version "1.0.5"
|
||||
resolved "https://registry.npmjs.org/@types/gapi.client/-/gapi.client-1.0.5.tgz#a6eb97e664fe51656c5b52258bd0afef28c76308"
|
||||
integrity sha512-OTpbBMuzfC4lkvaomxqskI/iWRGW3zOZbDXZLNSyiuswTiSSGgILRLkg0POuZ4EgzEdaYaTlXpnXiCp07ri/Yw==
|
||||
|
||||
"@types/gapi@*", "@types/gapi@^0.0.41":
|
||||
version "0.0.41"
|
||||
resolved "https://registry.npmjs.org/@types/gapi/-/gapi-0.0.41.tgz#c477ee4f0951c005869219fd10b456ae2bba437e"
|
||||
integrity sha512-tmHO66z/f91JZCDqinj/nNvQEszsz/hBT4+MvCSKT5sDzl5Ld/oXZ8WaecCBjRLw2uWKUInUHM9MhEXWkOiNjw==
|
||||
|
||||
"@types/git-url-parse@^9.0.0":
|
||||
version "9.0.1"
|
||||
resolved "https://registry.npmjs.org/@types/git-url-parse/-/git-url-parse-9.0.1.tgz#1c7cc89527ca8b5afcf260ead3b0e4e373c43938"
|
||||
@@ -6189,7 +6379,7 @@
|
||||
resolved "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.0.tgz#dc85454b953178cc6043df5208b9e949b54a3bc4"
|
||||
integrity sha512-/rM+sWiuOZ5dvuVzV37sUuklsbg+JPOP8d+nNFlo2ZtfpzPiPvh1/gc8liWOLBqe+sR+ZM7guPaIcTt6UZTo7Q==
|
||||
|
||||
"@types/prop-types@*", "@types/prop-types@^15.7.3":
|
||||
"@types/prop-types@*", "@types/prop-types@^15.7.3", "@types/prop-types@^15.7.4":
|
||||
version "15.7.4"
|
||||
resolved "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.4.tgz#fcf7205c25dff795ee79af1e30da2c9790808f11"
|
||||
integrity sha512-rZ5drC/jWjrArrS8BR6SIr4cWpW09RNTYt9AMZo3Jwwif+iacXAqgVjm0B0Bv/S1jhDXKHqRVNCbACkJ89RAnQ==
|
||||
@@ -6225,6 +6415,13 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-is@^16.7.1 || ^17.0.0":
|
||||
version "17.0.3"
|
||||
resolved "https://registry.npmjs.org/@types/react-is/-/react-is-17.0.3.tgz#2d855ba575f2fc8d17ef9861f084acc4b90a137a"
|
||||
integrity sha512-aBTIWg1emtu95bLTLx0cpkxwGW3ueZv71nE2YFBpL8k/z5czEW8yYpOo8Dp+UUAFAtKwNaOsh/ioSeQnWlZcfw==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-redux@^7.1.16":
|
||||
version "7.1.19"
|
||||
resolved "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.19.tgz#477bd0a9b01bae6d6bf809418cdfa7d3c16d4c62"
|
||||
@@ -6270,6 +6467,13 @@
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-transition-group@^4.4.4":
|
||||
version "4.4.4"
|
||||
resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.4.tgz#acd4cceaa2be6b757db61ed7b432e103242d163e"
|
||||
integrity sha512-7gAPz7anVK5xzbeQW9wFBDg7G++aPLAFY0QaSMOou9rJZpbuI58WAuJrgu+qR92l61grlnCUe7AFX8KGahAgug==
|
||||
dependencies:
|
||||
"@types/react" "*"
|
||||
|
||||
"@types/react-virtualized-auto-sizer@^1.0.1":
|
||||
version "1.0.1"
|
||||
resolved "https://registry.npmjs.org/@types/react-virtualized-auto-sizer/-/react-virtualized-auto-sizer-1.0.1.tgz#b3187dae1dfc4c15880c9cfc5b45f2719ea6ebd4"
|
||||
@@ -6362,6 +6566,13 @@
|
||||
dependencies:
|
||||
rollup-plugin-postcss "*"
|
||||
|
||||
"@types/sanitize-html@^2.6.2":
|
||||
version "2.6.2"
|
||||
resolved "https://registry.npmjs.org/@types/sanitize-html/-/sanitize-html-2.6.2.tgz#9c47960841b9def1e4c9dfebaaab010a3f6e97b9"
|
||||
integrity sha512-7Lu2zMQnmHHQGKXVvCOhSziQMpa+R2hMHFefzbYoYMHeaXR0uXqNeOc3JeQQQ8/6Xa2Br/P1IQTLzV09xxAiUQ==
|
||||
dependencies:
|
||||
htmlparser2 "^6.0.0"
|
||||
|
||||
"@types/scheduler@*":
|
||||
version "0.16.1"
|
||||
resolved "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.1.tgz#18845205e86ff0038517aab7a18a62a6b9f71275"
|
||||
@@ -7760,6 +7971,13 @@ axios@^0.21.1, axios@^0.21.4:
|
||||
dependencies:
|
||||
follow-redirects "^1.14.0"
|
||||
|
||||
axios@^0.26.0:
|
||||
version "0.26.0"
|
||||
resolved "https://registry.npmjs.org/axios/-/axios-0.26.0.tgz#9a318f1c69ec108f8cd5f3c3d390366635e13928"
|
||||
integrity sha512-lKoGLMYtHvFrPVt3r+RBMp9nh34N0M8zEfCWqdWZx6phynIEhQqAdydpyBAAG211zlhX9Rgu08cOamy6XjE5Og==
|
||||
dependencies:
|
||||
follow-redirects "^1.14.8"
|
||||
|
||||
axobject-query@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/axobject-query/-/axobject-query-2.2.0.tgz#943d47e10c0b704aa42275e20edf3722648989be"
|
||||
@@ -8044,6 +8262,11 @@ bfj@^7.0.2:
|
||||
hoopy "^0.1.4"
|
||||
tryer "^1.0.1"
|
||||
|
||||
big-integer@^1.6.16:
|
||||
version "1.6.51"
|
||||
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.51.tgz#0df92a5d9880560d3ff2d5fd20245c889d130686"
|
||||
integrity sha512-GPEid2Y9QU1Exl1rpO9B2IPJGHPSupF5GnVIP0blYvNOMer2bTvSWs1jGOUg04hTmu67nmLsQ9TBo1puaotBHg==
|
||||
|
||||
big-integer@^1.6.17:
|
||||
version "1.6.48"
|
||||
resolved "https://registry.npmjs.org/big-integer/-/big-integer-1.6.48.tgz#8fd88bd1632cba4a1c8c3e3d7159f08bb95b4b9e"
|
||||
@@ -8235,6 +8458,20 @@ breakword@^1.0.5:
|
||||
dependencies:
|
||||
wcwidth "^1.0.1"
|
||||
|
||||
broadcast-channel@^3.4.1:
|
||||
version "3.7.0"
|
||||
resolved "https://registry.npmjs.org/broadcast-channel/-/broadcast-channel-3.7.0.tgz#2dfa5c7b4289547ac3f6705f9c00af8723889937"
|
||||
integrity sha512-cIAKJXAxGJceNZGTZSBzMxzyOn72cVgPnKx4dc6LRjQgbaJUQqhy5rzL3zbMxkMWsGKkv2hSFkPRMEXfoMZ2Mg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.7.2"
|
||||
detect-node "^2.1.0"
|
||||
js-sha3 "0.8.0"
|
||||
microseconds "0.2.0"
|
||||
nano-time "1.0.0"
|
||||
oblivious-set "1.0.0"
|
||||
rimraf "3.0.2"
|
||||
unload "2.2.0"
|
||||
|
||||
brorand@^1.0.1, brorand@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
@@ -9009,7 +9246,7 @@ cloneable-readable@^1.0.0:
|
||||
process-nextick-args "^2.0.0"
|
||||
readable-stream "^2.3.5"
|
||||
|
||||
clsx@^1.0.2, clsx@^1.0.4:
|
||||
clsx@^1.0.2, clsx@^1.0.4, clsx@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188"
|
||||
integrity sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==
|
||||
@@ -10008,6 +10245,11 @@ csstype@^2.5.2, csstype@^2.6.7:
|
||||
resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.17.tgz#4cf30eb87e1d1a005d8b6510f95292413f6a1c0e"
|
||||
integrity sha512-u1wmTI1jJGzCJzWndZo8mk4wnPTZd1eOIYTYvuEyOQGfmDl3TrabCCfKnOC86FZwW/9djqTl933UF/cS425i9A==
|
||||
|
||||
csstype@^3.0.10:
|
||||
version "3.0.10"
|
||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.10.tgz#2ad3a7bed70f35b965707c092e5f30b327c290e5"
|
||||
integrity sha512-2u44ZG2OcNUO9HDp/Jl8C07x6pU/eTR3ncV91SiK3dhG9TWvRVsCoJw14Ckx5DgWkzGA3waZWO3d7pgqpUI/XA==
|
||||
|
||||
csstype@^3.0.2, csstype@^3.0.6:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.npmjs.org/csstype/-/csstype-3.0.7.tgz#2a5fb75e1015e84dd15692f71e89a1450290950b"
|
||||
@@ -10711,6 +10953,11 @@ detect-node@^2.0.4:
|
||||
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.0.4.tgz#014ee8f8f669c5c58023da64b8179c083a28c46c"
|
||||
integrity sha512-ZIzRpLJrOj7jjP2miAtgqIfmzbxa4ZOr5jJc601zklsfEx9oTzmmj2nVpIPRpNlRTIh8lc1kyViIY7BWSGNmKw==
|
||||
|
||||
detect-node@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz#c9c70775a49c3d03bc2c06d9a73be550f978f8b1"
|
||||
integrity sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==
|
||||
|
||||
detect-port-alt@^1.1.6:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.npmjs.org/detect-port-alt/-/detect-port-alt-1.1.6.tgz#24707deabe932d4a3cf621302027c2b266568275"
|
||||
@@ -12588,6 +12835,11 @@ follow-redirects@^1.0.0, follow-redirects@^1.14.0:
|
||||
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.8.tgz#016996fb9a11a100566398b1c6839337d7bfa8fc"
|
||||
integrity sha512-1x0S9UVJHsQprFcEC/qnNzBLcIxsjAV905f/UkQxbclCsoTWlacCNOpQa/anodLl2uaEKFhfWOvM2Qg77+15zA==
|
||||
|
||||
follow-redirects@^1.14.8:
|
||||
version "1.14.9"
|
||||
resolved "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.9.tgz#dd4ea157de7bfaf9ea9b3fbd85aa16951f78d8d7"
|
||||
integrity sha512-MQDfihBQYMcyy5dhRDJUHcw7lb2Pv/TuE6xP1vyraLukNDHKbDxDNaOE3NbCAdKQApno+GPRyo1YAp89yCjK4w==
|
||||
|
||||
for-in@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80"
|
||||
@@ -13800,7 +14052,7 @@ html-webpack-plugin@^5.3.1:
|
||||
pretty-error "^4.0.0"
|
||||
tapable "^2.0.0"
|
||||
|
||||
htmlparser2@^6.1.0:
|
||||
htmlparser2@^6.0.0, htmlparser2@^6.1.0:
|
||||
version "6.1.0"
|
||||
resolved "https://registry.npmjs.org/htmlparser2/-/htmlparser2-6.1.0.tgz#c4d762b6c3371a05dbe65e94ae43a9f845fb8fb7"
|
||||
integrity sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==
|
||||
@@ -15588,6 +15840,11 @@ js-levenshtein@^1.1.6:
|
||||
resolved "https://registry.npmjs.org/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d"
|
||||
integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==
|
||||
|
||||
js-sha3@0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==
|
||||
|
||||
"js-tokens@^3.0.0 || ^4.0.0", js-tokens@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499"
|
||||
@@ -15972,6 +16229,15 @@ jss-plugin-camel-case@^10.5.1:
|
||||
hyphenate-style-name "^1.0.3"
|
||||
jss "10.6.0"
|
||||
|
||||
jss-plugin-camel-case@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.9.0.tgz#4921b568b38d893f39736ee8c4c5f1c64670aaf7"
|
||||
integrity sha512-UH6uPpnDk413/r/2Olmw4+y54yEF2lRIV8XIZyuYpgPYTITLlPOsq6XB9qeqv+75SQSg3KLocq5jUBXW8qWWww==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
hyphenate-style-name "^1.0.3"
|
||||
jss "10.9.0"
|
||||
|
||||
jss-plugin-default-unit@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.6.0.tgz#af47972486819b375f0f3a9e0213403a84b5ef3b"
|
||||
@@ -15980,6 +16246,14 @@ jss-plugin-default-unit@^10.5.1:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.6.0"
|
||||
|
||||
jss-plugin-default-unit@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.9.0.tgz#bb23a48f075bc0ce852b4b4d3f7582bc002df991"
|
||||
integrity sha512-7Ju4Q9wJ/MZPsxfu4T84mzdn7pLHWeqoGd/D8O3eDNNJ93Xc8PxnLmV8s8ZPNRYkLdxZqKtm1nPQ0BM4JRlq2w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.9.0"
|
||||
|
||||
jss-plugin-global@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.6.0.tgz#3e8011f760f399cbadcca7f10a485b729c50e3ed"
|
||||
@@ -15988,6 +16262,14 @@ jss-plugin-global@^10.5.1:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.6.0"
|
||||
|
||||
jss-plugin-global@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.9.0.tgz#fc07a0086ac97aca174e37edb480b69277f3931f"
|
||||
integrity sha512-4G8PHNJ0x6nwAFsEzcuVDiBlyMsj2y3VjmFAx/uHk/R/gzJV+yRHICjT4MKGGu1cJq2hfowFWCyrr/Gg37FbgQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.9.0"
|
||||
|
||||
jss-plugin-nested@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.6.0.tgz#5f83c5c337d3b38004834e8426957715a0251641"
|
||||
@@ -15997,6 +16279,15 @@ jss-plugin-nested@^10.5.1:
|
||||
jss "10.6.0"
|
||||
tiny-warning "^1.0.2"
|
||||
|
||||
jss-plugin-nested@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.9.0.tgz#cc1c7d63ad542c3ccc6e2c66c8328c6b6b00f4b3"
|
||||
integrity sha512-2UJnDrfCZpMYcpPYR16oZB7VAC6b/1QLsRiAutOt7wJaaqwCBvNsosLEu/fUyKNQNGdvg2PPJFDO5AX7dwxtoA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.9.0"
|
||||
tiny-warning "^1.0.2"
|
||||
|
||||
jss-plugin-props-sort@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.6.0.tgz#297879f35f9fe21196448579fee37bcde28ce6bc"
|
||||
@@ -16005,6 +16296,14 @@ jss-plugin-props-sort@^10.5.1:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.6.0"
|
||||
|
||||
jss-plugin-props-sort@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.9.0.tgz#30e9567ef9479043feb6e5e59db09b4de687c47d"
|
||||
integrity sha512-7A76HI8bzwqrsMOJTWKx/uD5v+U8piLnp5bvru7g/3ZEQOu1+PjHvv7bFdNO3DwNPC9oM0a//KwIJsIcDCjDzw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.9.0"
|
||||
|
||||
jss-plugin-rule-value-function@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.6.0.tgz#3c1a557236a139d0151e70a82c810ccce1c1c5ea"
|
||||
@@ -16014,6 +16313,15 @@ jss-plugin-rule-value-function@^10.5.1:
|
||||
jss "10.6.0"
|
||||
tiny-warning "^1.0.2"
|
||||
|
||||
jss-plugin-rule-value-function@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.9.0.tgz#379fd2732c0746fe45168011fe25544c1a295d67"
|
||||
integrity sha512-IHJv6YrEf8pRzkY207cPmdbBstBaE+z8pazhPShfz0tZSDtRdQua5jjg6NMz3IbTasVx9FdnmptxPqSWL5tyJg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
jss "10.9.0"
|
||||
tiny-warning "^1.0.2"
|
||||
|
||||
jss-plugin-vendor-prefixer@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.6.0.tgz#e1fcd499352846890c38085b11dbd7aa1c4f2c78"
|
||||
@@ -16023,6 +16331,15 @@ jss-plugin-vendor-prefixer@^10.5.1:
|
||||
css-vendor "^2.0.8"
|
||||
jss "10.6.0"
|
||||
|
||||
jss-plugin-vendor-prefixer@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.9.0.tgz#aa9df98abfb3f75f7ed59a3ec50a5452461a206a"
|
||||
integrity sha512-MbvsaXP7iiVdYVSEoi+blrW+AYnTDvHTW6I6zqi7JcwXdc6I9Kbm234nEblayhF38EftoenbM+5218pidmC5gA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
css-vendor "^2.0.8"
|
||||
jss "10.9.0"
|
||||
|
||||
jss@10.6.0, jss@^10.5.1:
|
||||
version "10.6.0"
|
||||
resolved "https://registry.npmjs.org/jss/-/jss-10.6.0.tgz#d92ff9d0f214f65ca1718591b68e107be4774149"
|
||||
@@ -16034,6 +16351,16 @@ jss@10.6.0, jss@^10.5.1:
|
||||
is-in-browser "^1.1.3"
|
||||
tiny-warning "^1.0.2"
|
||||
|
||||
jss@10.9.0, jss@^10.8.2:
|
||||
version "10.9.0"
|
||||
resolved "https://registry.npmjs.org/jss/-/jss-10.9.0.tgz#7583ee2cdc904a83c872ba695d1baab4b59c141b"
|
||||
integrity sha512-YpzpreB6kUunQBbrlArlsMpXYyndt9JATbt95tajx0t4MTJJcCJdd4hdNpHmOIDiUJrF/oX5wtVFrS3uofWfGw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.3.1"
|
||||
csstype "^3.0.2"
|
||||
is-in-browser "^1.1.3"
|
||||
tiny-warning "^1.0.2"
|
||||
|
||||
"jsx-ast-utils@^2.4.1 || ^3.0.0":
|
||||
version "3.2.0"
|
||||
resolved "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.2.0.tgz#41108d2cec408c3453c1bbe8a4aae9e1e2bd8f82"
|
||||
@@ -16910,7 +17237,7 @@ lunr@^2.3.9:
|
||||
resolved "https://registry.npmjs.org/lunr/-/lunr-2.3.9.tgz#18b123142832337dd6e964df1a5a7707b25d35e1"
|
||||
integrity sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==
|
||||
|
||||
luxon@^2.0.2:
|
||||
luxon@^2.0.2, luxon@^2.3.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.npmjs.org/luxon/-/luxon-2.3.0.tgz#bf16a7e642513c2a20a6230a6a41b0ab446d0045"
|
||||
integrity sha512-gv6jZCV+gGIrVKhO90yrsn8qXPKD8HYZJtrUDSfEbow8Tkw84T9OnCyJhWvnJIaIF/tBuiAjZuQHUt1LddX2mg==
|
||||
@@ -17079,6 +17406,26 @@ marked@^4.0.10:
|
||||
resolved "https://registry.npmjs.org/marked/-/marked-4.0.10.tgz#423e295385cc0c3a70fa495e0df68b007b879423"
|
||||
integrity sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==
|
||||
|
||||
match-sorter@^6.0.2:
|
||||
version "6.3.1"
|
||||
resolved "https://registry.npmjs.org/match-sorter/-/match-sorter-6.3.1.tgz#98cc37fda756093424ddf3cbc62bfe9c75b92bda"
|
||||
integrity sha512-mxybbo3pPNuA+ZuCUhm5bwNkXrJTbsk5VWbR5wiwz/GC6LIiegBGn2w3O08UG/jdbYLinw51fSQ5xNU1U3MgBw==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
remove-accents "0.4.2"
|
||||
|
||||
material-ui-popup-state@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.npmjs.org/material-ui-popup-state/-/material-ui-popup-state-2.0.0.tgz#8050421a9d00df67d7f63e7b2b73ab5b331051ff"
|
||||
integrity sha512-1sbb9xpMs7OxG0SOGfGO0ZnwiLtqZSoXda0/AqqJkpouT4e0nADXutQtDJFMa9GUMNAODVDlYnNmfqM+MhFjsg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.12.5"
|
||||
"@mui/icons-material" "^5.0.0"
|
||||
"@mui/material" "^5.0.0"
|
||||
"@mui/styles" "^5.0.0"
|
||||
classnames "^2.2.6"
|
||||
prop-types "^15.7.2"
|
||||
|
||||
material-ui-search-bar@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/material-ui-search-bar/-/material-ui-search-bar-1.0.0.tgz#2652dd5bdc4cb043cffb7144d9c296c120702e62"
|
||||
@@ -17684,6 +18031,11 @@ micromatch@^4.0.2, micromatch@^4.0.4:
|
||||
braces "^3.0.1"
|
||||
picomatch "^2.2.3"
|
||||
|
||||
microseconds@0.2.0:
|
||||
version "0.2.0"
|
||||
resolved "https://registry.npmjs.org/microseconds/-/microseconds-0.2.0.tgz#233b25f50c62a65d861f978a4a4f8ec18797dc39"
|
||||
integrity sha512-n7DHHMjR1avBbSpsTBj6fmMGh2AGrifVV4e+WYc3Q9lO+xnSZ3NyhcBND3vzzatt05LFhoKFRxrIyklmLlUtyA==
|
||||
|
||||
miller-rabin@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
|
||||
@@ -18134,6 +18486,13 @@ nano-css@^5.3.1:
|
||||
stacktrace-js "^2.0.2"
|
||||
stylis "^4.0.6"
|
||||
|
||||
nano-time@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/nano-time/-/nano-time-1.0.0.tgz#b0554f69ad89e22d0907f7a12b0993a5d96137ef"
|
||||
integrity sha1-sFVPaa2J4i0JB/ehKwmTpdlhN+8=
|
||||
dependencies:
|
||||
big-integer "^1.6.16"
|
||||
|
||||
nanoclone@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
|
||||
@@ -18770,6 +19129,11 @@ object.values@^1.1.5:
|
||||
define-properties "^1.1.3"
|
||||
es-abstract "^1.19.1"
|
||||
|
||||
oblivious-set@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/oblivious-set/-/oblivious-set-1.0.0.tgz#c8316f2c2fb6ff7b11b6158db3234c49f733c566"
|
||||
integrity sha512-z+pI07qxo4c2CulUHCDf9lcqDlMSo72N/4rLUpRXf6fu+q8vjt8y0xS+Tlf8NTJDdTXHbdeO1n3MlbctwEoXZw==
|
||||
|
||||
obuf@^1.0.0, obuf@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e"
|
||||
@@ -19329,6 +19693,11 @@ parse-path@^4.0.0:
|
||||
is-ssh "^1.3.0"
|
||||
protocols "^1.4.0"
|
||||
|
||||
parse-srcset@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.npmjs.org/parse-srcset/-/parse-srcset-1.0.2.tgz#f2bd221f6cc970a938d88556abc589caaaa2bde1"
|
||||
integrity sha1-8r0iH2zJcKk42IVWq8WJyqqiveE=
|
||||
|
||||
parse-url@^5.0.0:
|
||||
version "5.0.1"
|
||||
resolved "https://registry.npmjs.org/parse-url/-/parse-url-5.0.1.tgz#99c4084fc11be14141efa41b3d117a96fcb9527f"
|
||||
@@ -20141,7 +20510,7 @@ postcss-value-parser@^4.0.2, postcss-value-parser@^4.1.0, postcss-value-parser@^
|
||||
resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514"
|
||||
integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==
|
||||
|
||||
postcss@^8.1.0, postcss@^8.4.5:
|
||||
postcss@^8.1.0, postcss@^8.3.11, postcss@^8.4.5:
|
||||
version "8.4.6"
|
||||
resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.6.tgz#c5ff3c3c457a23864f32cb45ac9b741498a09ae1"
|
||||
integrity sha512-OovjwIzs9Te46vlEx7+uXB0PLijpwjXGKXjVGGPIGubGpq7uh5Xgf6D6FiJ/SzJMBosHDp6a2hiXOS97iBXcaA==
|
||||
@@ -20842,7 +21211,7 @@ react-is@^16.10.2, react-is@^16.12.0, react-is@^16.13.1, react-is@^16.7.0, react
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
|
||||
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
|
||||
|
||||
"react-is@^16.8.0 || ^17.0.0", react-is@^17.0.0, react-is@^17.0.1:
|
||||
"react-is@^16.8.0 || ^17.0.0", react-is@^17.0.0, react-is@^17.0.1, react-is@^17.0.2:
|
||||
version "17.0.2"
|
||||
resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz#e691d4a8e9c789365655539ab372762b0efb54f0"
|
||||
integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==
|
||||
@@ -20872,6 +21241,15 @@ react-markdown@^8.0.0:
|
||||
unist-util-visit "^4.0.0"
|
||||
vfile "^5.0.0"
|
||||
|
||||
react-query@^3.34.16:
|
||||
version "3.34.16"
|
||||
resolved "https://registry.npmjs.org/react-query/-/react-query-3.34.16.tgz#279ea180bcaeaec49c7864b29d1711ee9f152594"
|
||||
integrity sha512-7FvBvjgEM4YQ8nPfmAr+lJfbW95uyW/TVjFoi2GwCkF33/S8ajx45tuPHPFGWs4qYwPy1mzwxD4IQfpUDrefNQ==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
broadcast-channel "^3.4.1"
|
||||
match-sorter "^6.0.2"
|
||||
|
||||
react-redux@^7.1.1, react-redux@^7.2.4:
|
||||
version "7.2.5"
|
||||
resolved "https://registry.npmjs.org/react-redux/-/react-redux-7.2.5.tgz#213c1b05aa1187d9c940ddfc0b29450957f6a3b8"
|
||||
@@ -21004,6 +21382,16 @@ react-transition-group@^4.0.0, react-transition-group@^4.4.0:
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-transition-group@^4.4.2:
|
||||
version "4.4.2"
|
||||
resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz#8b59a56f09ced7b55cbd53c36768b922890d5470"
|
||||
integrity sha512-/RNYfRAMlZwDSr6z4zNKV6xu53/e2BuaBbGhbyYIXTrmgu/bGHzmqOs7mJSJBHy9Ud+ApHx3QjrkKSp1pxvlFg==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.5.5"
|
||||
dom-helpers "^5.0.1"
|
||||
loose-envify "^1.4.0"
|
||||
prop-types "^15.6.2"
|
||||
|
||||
react-universal-interface@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.npmjs.org/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b"
|
||||
@@ -21608,6 +21996,11 @@ remedial@^1.0.7:
|
||||
resolved "https://registry.npmjs.org/remedial/-/remedial-1.0.8.tgz#a5e4fd52a0e4956adbaf62da63a5a46a78c578a0"
|
||||
integrity sha512-/62tYiOe6DzS5BqVsNpH/nkGlX45C/Sp6V+NtiN6JQNS1Viay7cWkazmRkrQrdFj2eshDe96SIQNIoMxqhzBOg==
|
||||
|
||||
remove-accents@0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.npmjs.org/remove-accents/-/remove-accents-0.4.2.tgz#0a43d3aaae1e80db919e07ae254b285d9e1c7bb5"
|
||||
integrity sha1-CkPTqq4egNuRngeuJUsoXZ4ce7U=
|
||||
|
||||
remove-trailing-separator@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef"
|
||||
@@ -23354,6 +23747,11 @@ stylehacks@^5.0.1:
|
||||
browserslist "^4.16.0"
|
||||
postcss-selector-parser "^6.0.4"
|
||||
|
||||
stylis@4.0.13:
|
||||
version "4.0.13"
|
||||
resolved "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91"
|
||||
integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag==
|
||||
|
||||
stylis@^4.0.6:
|
||||
version "4.0.7"
|
||||
resolved "https://registry.npmjs.org/stylis/-/stylis-4.0.7.tgz#412a90c28079417f3d27c028035095e4232d2904"
|
||||
@@ -24636,6 +25034,14 @@ unixify@1.0.0, unixify@^1.0.0:
|
||||
dependencies:
|
||||
normalize-path "^2.1.1"
|
||||
|
||||
unload@2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.npmjs.org/unload/-/unload-2.2.0.tgz#ccc88fdcad345faa06a92039ec0f80b488880ef7"
|
||||
integrity sha512-B60uB5TNBLtN6/LsgAf3udH9saB5p7gqJwcFfbOEZ8BcBHnGwCf6G/TGiEqkRAxX7zAFIUtzdrXQSdL3Q/wqNA==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.6.2"
|
||||
detect-node "^2.0.4"
|
||||
|
||||
unpipe@1.0.0, unpipe@~1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
|
||||
|
||||
Reference in New Issue
Block a user