@@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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 { OAuthApi, createApiRef, FetchApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { GCalendar, GCalendarEvent } from './types';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
|
||||
type Options = {
|
||||
authApi: OAuthApi;
|
||||
fetchApi: FetchApi;
|
||||
};
|
||||
|
||||
export const gcalendarApiRef = createApiRef<GCalendarApiClient>({
|
||||
id: 'plugin.gcalendar.service',
|
||||
});
|
||||
|
||||
export class GCalendarApiClient {
|
||||
private readonly authApi: OAuthApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.authApi = options.authApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
}
|
||||
|
||||
private async get<T>(
|
||||
path: string,
|
||||
params: { [key in string]: any },
|
||||
): Promise<T> {
|
||||
const query = new URLSearchParams(params);
|
||||
const url = new URL(
|
||||
`${path}?${query.toString()}`,
|
||||
'https://www.googleapis.com',
|
||||
);
|
||||
const token = await this.authApi.getAccessToken();
|
||||
const response = await this.fetchApi.fetch(url.toString(), {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await ResponseError.fromResponse(response);
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
public async getCalendars(params?: any) {
|
||||
return this.get<{ items: GCalendar[] }>(
|
||||
'/calendar/v3/users/me/calendarList',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
public async getEvents(calendarId: string, params?: any) {
|
||||
return this.get<{ items: GCalendarEvent[] }>(
|
||||
`/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -13,61 +13,5 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { OAuthApi, createApiRef, FetchApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { GCalendar, GCalendarEvent } from '../components/CalendarCard/types';
|
||||
import { ResponseError } from '@backstage/errors';
|
||||
|
||||
type Options = {
|
||||
authApi: OAuthApi;
|
||||
fetchApi: FetchApi;
|
||||
};
|
||||
|
||||
export const gcalendarApiRef = createApiRef<GCalendarApiClient>({
|
||||
id: 'plugin.gcalendar.service',
|
||||
});
|
||||
|
||||
export class GCalendarApiClient {
|
||||
private readonly authApi: OAuthApi;
|
||||
private readonly fetchApi: FetchApi;
|
||||
|
||||
constructor(options: Options) {
|
||||
this.authApi = options.authApi;
|
||||
this.fetchApi = options.fetchApi;
|
||||
}
|
||||
|
||||
private async get<T>(
|
||||
path: string,
|
||||
params: { [key in string]: any },
|
||||
): Promise<T> {
|
||||
const query = new URLSearchParams(params);
|
||||
const url = new URL(
|
||||
`${path}?${query.toString()}`,
|
||||
'https://www.googleapis.com',
|
||||
);
|
||||
const token = await this.authApi.getAccessToken();
|
||||
const response = await this.fetchApi.fetch(url.toString(), {
|
||||
headers: token ? { Authorization: `Bearer ${token}` } : {},
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw await ResponseError.fromResponse(response);
|
||||
}
|
||||
|
||||
return response.json() as Promise<T>;
|
||||
}
|
||||
|
||||
public async getCalendars(params?: any) {
|
||||
return this.get<{ items: GCalendar[] }>(
|
||||
'/calendar/v3/users/me/calendarList',
|
||||
params,
|
||||
);
|
||||
}
|
||||
|
||||
public async getEvents(calendarId: string, params?: any) {
|
||||
return this.get<{ items: GCalendarEvent[] }>(
|
||||
`/calendar/v3/calendars/${encodeURIComponent(calendarId)}/events`,
|
||||
params,
|
||||
);
|
||||
}
|
||||
}
|
||||
export * from './client';
|
||||
export * from './types';
|
||||
|
||||
@@ -18,7 +18,7 @@ import React from 'react';
|
||||
import { renderInTestApp } from '@backstage/test-utils';
|
||||
|
||||
import { AttendeeChip } from './AttendeeChip';
|
||||
import { EventAttendee, ResponseStatus } from './types';
|
||||
import { EventAttendee, ResponseStatus } from '../../api';
|
||||
|
||||
describe('<AttendeeChip />', () => {
|
||||
it('renders attendee email', async () => {
|
||||
|
||||
@@ -21,7 +21,7 @@ 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';
|
||||
import { EventAttendee, ResponseStatus } from '../../api';
|
||||
|
||||
const useStyles = makeStyles((theme: BackstageTheme) => {
|
||||
const getIconColor = (responseStatus?: string) => {
|
||||
|
||||
@@ -35,46 +35,51 @@ import {
|
||||
|
||||
import zoomIcon from '../../icons/zoomIcon.svg';
|
||||
import { CalendarEventPopoverContent } from './CalendarEventPopoverContent';
|
||||
import { GCalendarEvent, ResponseStatus } from './types';
|
||||
import { GCalendarEvent, ResponseStatus } from '../../api';
|
||||
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,
|
||||
const useStyles = makeStyles(
|
||||
theme => ({
|
||||
event: {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
marginBottom: theme.spacing(1),
|
||||
cursor: 'pointer',
|
||||
paddingRight: 12,
|
||||
},
|
||||
},
|
||||
link: {
|
||||
width: 48,
|
||||
height: 48,
|
||||
display: 'inline-block',
|
||||
padding: 8,
|
||||
borderRadius: '50%',
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.grey[100],
|
||||
declined: {
|
||||
textDecoration: 'line-through',
|
||||
},
|
||||
},
|
||||
calendarColor: ({ event }: any) => ({
|
||||
width: 8,
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
backgroundColor: event.primary
|
||||
? theme.palette.primary.light
|
||||
: event.backgroundColor,
|
||||
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 }: { event: GCalendarEvent }) => ({
|
||||
width: 8,
|
||||
borderTopLeftRadius: 4,
|
||||
borderBottomLeftRadius: 4,
|
||||
backgroundColor: event.primary
|
||||
? theme.palette.primary.light
|
||||
: event.backgroundColor,
|
||||
}),
|
||||
}),
|
||||
}));
|
||||
{
|
||||
name: 'GCalendarEvent',
|
||||
},
|
||||
);
|
||||
|
||||
export const CalendarEvent = ({ event }: { event: GCalendarEvent }) => {
|
||||
const classes = useStyles({ event });
|
||||
|
||||
@@ -31,24 +31,29 @@ import {
|
||||
import ArrowForwardIcon from '@material-ui/icons/ArrowForward';
|
||||
|
||||
import { AttendeeChip } from './AttendeeChip';
|
||||
import { GCalendarEvent } from './types';
|
||||
import { GCalendarEvent } from '../../api';
|
||||
import { getTimePeriod, getZoomLink } from './util';
|
||||
|
||||
const useStyles = makeStyles(theme => {
|
||||
return {
|
||||
description: {
|
||||
wordBreak: 'break-word',
|
||||
'& a': {
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: 500,
|
||||
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),
|
||||
},
|
||||
};
|
||||
});
|
||||
divider: {
|
||||
marginTop: theme.spacing(2),
|
||||
marginBottom: theme.spacing(2),
|
||||
},
|
||||
};
|
||||
},
|
||||
{
|
||||
name: 'GCalendarEventPopoverContent',
|
||||
},
|
||||
);
|
||||
|
||||
type CalendarEventPopoverProps = {
|
||||
event: GCalendarEvent;
|
||||
|
||||
@@ -27,17 +27,22 @@ import {
|
||||
makeStyles,
|
||||
} from '@material-ui/core';
|
||||
|
||||
import { GCalendar } from './types';
|
||||
import { GCalendar } from '../../api';
|
||||
|
||||
const useStyles = makeStyles({
|
||||
formControl: {
|
||||
width: 120,
|
||||
const useStyles = makeStyles(
|
||||
{
|
||||
formControl: {
|
||||
width: 120,
|
||||
},
|
||||
selectedCalendars: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
},
|
||||
},
|
||||
selectedCalendars: {
|
||||
textOverflow: 'ellipsis',
|
||||
overflow: 'hidden',
|
||||
{
|
||||
name: 'GCalendarSelect',
|
||||
},
|
||||
});
|
||||
);
|
||||
|
||||
type CalendarSelectProps = {
|
||||
disabled: boolean;
|
||||
|
||||
@@ -18,10 +18,12 @@ import React from 'react';
|
||||
import { Box, Button, styled } from '@material-ui/core';
|
||||
|
||||
import { CalendarEvent } from './CalendarEvent';
|
||||
import { events } from './signInEventMock';
|
||||
import { eventsMock } from './signInEventMock';
|
||||
import { GCalendarEvent } from '../..';
|
||||
|
||||
type SignInContentProps = {
|
||||
type Props = {
|
||||
handleAuthClick: React.MouseEventHandler<HTMLElement>;
|
||||
events?: GCalendarEvent[];
|
||||
};
|
||||
|
||||
const TransparentBox = styled(Box)({
|
||||
@@ -29,7 +31,10 @@ const TransparentBox = styled(Box)({
|
||||
filter: 'blur(1.5px)',
|
||||
});
|
||||
|
||||
export const SignInContent = ({ handleAuthClick }: SignInContentProps) => {
|
||||
export const SignInContent = ({
|
||||
handleAuthClick,
|
||||
events = eventsMock,
|
||||
}: Props) => {
|
||||
return (
|
||||
<Box position="relative" height="100%" width="100%">
|
||||
<TransparentBox p={1}>
|
||||
|
||||
@@ -13,9 +13,9 @@
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
import { GCalendarEvent } from './types';
|
||||
import { GCalendarEvent } from '../../api';
|
||||
|
||||
export const events: GCalendarEvent[] = [
|
||||
export const eventsMock: GCalendarEvent[] = [
|
||||
{
|
||||
id: '1',
|
||||
htmlLink: 'https://www.google.com/calendar/',
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { DateTime } from 'luxon';
|
||||
|
||||
import { GCalendarEvent } from './types';
|
||||
import { GCalendarEvent } from '../../api';
|
||||
|
||||
export function getZoomLink(event: GCalendarEvent) {
|
||||
const videoEntrypoint = event.conferenceData?.entryPoints?.find(
|
||||
|
||||
@@ -20,7 +20,7 @@ import { useQueries } from 'react-query';
|
||||
import { useApi } from '@backstage/core-plugin-api';
|
||||
|
||||
import { gcalendarApiRef } from '../api';
|
||||
import { GCalendar, GCalendarEvent } from '../components/CalendarCard/types';
|
||||
import { GCalendar, GCalendarEvent } from '../api';
|
||||
|
||||
type Options = {
|
||||
selectedCalendars?: string[];
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
*/
|
||||
import { gcalendarPlugin } from './plugin';
|
||||
|
||||
describe('gcalendar-homepage', () => {
|
||||
describe('gcalendar', () => {
|
||||
it('should export plugin', () => {
|
||||
expect(gcalendarPlugin).toBeDefined();
|
||||
});
|
||||
|
||||
@@ -25,7 +25,7 @@ import { GCalendarApiClient, gcalendarApiRef } from './api';
|
||||
import { rootRouteRef } from './routes';
|
||||
|
||||
export const gcalendarPlugin = createPlugin({
|
||||
id: 'gcalendar-homepage',
|
||||
id: 'gcalendar',
|
||||
routes: {
|
||||
root: rootRouteRef,
|
||||
},
|
||||
|
||||
@@ -16,5 +16,5 @@
|
||||
import { createRouteRef } from '@backstage/core-plugin-api';
|
||||
|
||||
export const rootRouteRef = createRouteRef({
|
||||
id: 'gcalendar-homepage',
|
||||
id: 'gcalendar',
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user