diff --git a/plugins/pagerduty/src/components/About/AboutCard.tsx b/plugins/pagerduty/src/components/About/AboutCard.tsx new file mode 100644 index 0000000000..7d5419908a --- /dev/null +++ b/plugins/pagerduty/src/components/About/AboutCard.tsx @@ -0,0 +1,33 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { Card, CardContent, CardHeader, Divider } from '@material-ui/core'; +import { SubHeader } from './SubHeader'; +import { SubHeaderLink } from '../types'; + +type Props = { + title: string; + links: SubHeaderLink[]; + content: React.ReactNode; +}; + +export const AboutCard = ({ title, links, content }: Props) => ( + + } /> + + {content} + +); diff --git a/plugins/pagerduty/src/components/About/SubHeader.tsx b/plugins/pagerduty/src/components/About/SubHeader.tsx new file mode 100644 index 0000000000..5e4ea96fe0 --- /dev/null +++ b/plugins/pagerduty/src/components/About/SubHeader.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { VerticalIcon } from './VerticalIcon'; +import { SubHeaderLink } from '../types'; +import { makeStyles } from '@material-ui/core'; + +const useStyles = makeStyles(theme => ({ + links: { + margin: theme.spacing(2, 0), + display: 'grid', + gridAutoFlow: 'column', + gridAutoColumns: 'min-content', + gridGap: theme.spacing(3), + }, +})); + +type Props = { + links: SubHeaderLink[]; +}; + +export const SubHeader = ({ links }: Props) => { + const classes = useStyles(); + return ( + + ); +}; diff --git a/plugins/pagerduty/src/components/About/VerticalIcon.tsx b/plugins/pagerduty/src/components/About/VerticalIcon.tsx new file mode 100644 index 0000000000..5463372ac0 --- /dev/null +++ b/plugins/pagerduty/src/components/About/VerticalIcon.tsx @@ -0,0 +1,85 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 classnames from 'classnames'; +import { makeStyles, Link } from '@material-ui/core'; +import LinkIcon from '@material-ui/icons/Link'; +import { Link as RouterLink } from 'react-router-dom'; + +export type VerticalIconProps = { + icon?: React.ReactNode; + href?: string; + title?: string; + label: string; + action?: React.ReactNode; +}; + +const useIconStyles = makeStyles(theme => ({ + link: { + display: 'grid', + justifyItems: 'center', + gridGap: 4, + textAlign: 'center', + }, + label: { + fontSize: '0.7rem', + textTransform: 'uppercase', + fontWeight: 600, + letterSpacing: 1.2, + }, + linkStyle: { + color: theme.palette.secondary.main, + }, +})); + +export function VerticalIcon({ + icon = , + href = '#', + action, + ...props +}: VerticalIconProps) { + const classes = useIconStyles(); + + if (action) { + return ( + + {icon} + {action} + + ); + } + + // Absolute links should not be using RouterLink + if (href?.startsWith('//') || href?.includes('://')) { + return ( + + {icon} + {props.label} + + ); + } + + return ( + + {icon} + {props.label} + + ); +} diff --git a/plugins/pagerduty/src/components/About/index.ts b/plugins/pagerduty/src/components/About/index.ts new file mode 100644 index 0000000000..4e495daa54 --- /dev/null +++ b/plugins/pagerduty/src/components/About/index.ts @@ -0,0 +1,19 @@ +/* + * Copyright 2020 Spotify AB + * + * 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 { AboutCard } from './AboutCard'; +// export { SubHeader } from './SubHeader'; +// export { VerticalIcon } from './VerticalIcon'; diff --git a/plugins/pagerduty/src/components/Incident/Incidents.tsx b/plugins/pagerduty/src/components/Incident/Incidents.tsx index 4b229fb4bc..732a582cdb 100644 --- a/plugins/pagerduty/src/components/Incident/Incidents.tsx +++ b/plugins/pagerduty/src/components/Incident/Incidents.tsx @@ -25,16 +25,14 @@ import { Alert } from '@material-ui/lab'; type Props = { serviceId: string; - refreshIncidents: Boolean; + refreshIncidents: boolean; }; export const Incidents = ({ serviceId, refreshIncidents }: Props) => { const api = useApi(pagerDutyApiRef); const [{ value: incidents, loading, error }, getIncidents] = useAsyncFn( - async () => { - return await api.getIncidentsByServiceId(serviceId); - }, + async () => await api.getIncidentsByServiceId(serviceId), ); useEffect(() => { diff --git a/plugins/pagerduty/src/components/PagerDutyCard.test.tsx b/plugins/pagerduty/src/components/PagerDutyCard.test.tsx index 34ddb63f71..a6615aee82 100644 --- a/plugins/pagerduty/src/components/PagerDutyCard.test.tsx +++ b/plugins/pagerduty/src/components/PagerDutyCard.test.tsx @@ -85,11 +85,9 @@ describe('PageDutyCard', () => { ), ); await waitFor(() => !queryByTestId('progress')); - expect(getByText('View in PagerDuty')).toBeInTheDocument(); - expect(getByText('Trigger Alarm')).toBeInTheDocument(); - expect( - getByText('Nice! No incidents are assigned to you!'), - ).toBeInTheDocument(); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); + expect(getByText('Nice! No incidents found!')).toBeInTheDocument(); expect(getByText('Empty escalation policy')).toBeInTheDocument(); }); @@ -141,8 +139,8 @@ describe('PageDutyCard', () => { ), ); await waitFor(() => !queryByTestId('progress')); - expect(getByText('View in PagerDuty')).toBeInTheDocument(); - expect(getByText('Trigger Alarm')).toBeInTheDocument(); + expect(getByText('Service Directory')).toBeInTheDocument(); + expect(getByText('Create Incident')).toBeInTheDocument(); const triggerButton = getByTestId('trigger-button'); await act(async () => { fireEvent.click(triggerButton); diff --git a/plugins/pagerduty/src/components/PagerDutyCard.tsx b/plugins/pagerduty/src/components/PagerDutyCard.tsx index ac309e8a95..8fd20f37e8 100644 --- a/plugins/pagerduty/src/components/PagerDutyCard.tsx +++ b/plugins/pagerduty/src/components/PagerDutyCard.tsx @@ -13,36 +13,22 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React, { useState } from 'react'; +import React, { useState, useCallback } from 'react'; import { useApi, Progress } from '@backstage/core'; import { Entity } from '@backstage/catalog-model'; -import { - Button, - Card, - CardContent, - CardHeader, - Divider, - makeStyles, -} from '@material-ui/core'; +import { Button, makeStyles } from '@material-ui/core'; import { Incidents } from './Incident'; import { EscalationPolicy } from './Escalation'; import { useAsync } from 'react-use'; import { Alert } from '@material-ui/lab'; import { pagerDutyApiRef, UnauthorizedError } from '../api'; -import { IconLinkVertical } from '@backstage/plugin-catalog'; -import { PagerDutyIcon } from './PagerDutyIcon'; import AlarmAddIcon from '@material-ui/icons/AlarmAdd'; import { TriggerDialog } from './TriggerDialog'; -import { MissingTokenError } from './MissingTokenError'; +import { MissingTokenError } from './Errors/MissingTokenError'; +import WebIcon from '@material-ui/icons/Web'; +import { AboutCard } from './About/AboutCard'; -const useStyles = makeStyles(theme => ({ - links: { - margin: theme.spacing(2, 0), - display: 'grid', - gridAutoFlow: 'column', - gridAutoColumns: 'min-content', - gridGap: theme.spacing(3), - }, +const useStyles = makeStyles({ triggerAlarm: { paddingTop: 0, paddingBottom: 0, @@ -56,7 +42,7 @@ const useStyles = makeStyles(theme => ({ textDecoration: 'none', }, }, -})); +}); export const PAGERDUTY_INTEGRATION_KEY = 'pagerduty.com/integration-key'; @@ -76,6 +62,14 @@ export const PagerDutyCard = ({ entity }: Props) => { PAGERDUTY_INTEGRATION_KEY ]; + const handleRefresh = useCallback(() => { + setRefreshIncidents(x => !x); + }, []); + + const handleDialog = useCallback(() => { + setShowDialog(x => !x); + }, []); + const { value: service, loading, error } = useAsync(async () => { const services = await api.getServiceByIntegrationKey(integrationKey); @@ -87,10 +81,6 @@ export const PagerDutyCard = ({ entity }: Props) => { }; }); - const handleDialog = () => { - setShowDialog(!showDialog); - }; - if (error instanceof UnauthorizedError) { return ; } @@ -107,13 +97,14 @@ export const PagerDutyCard = ({ entity }: Props) => { return ; } - const pagerdutyLink = { - title: 'View in PagerDuty', + const serviceLink = { + title: 'Service Directory', href: service!.url, + icon: , }; - const triggerAlarm = { - title: 'Trigger Alarm', + const triggerLink = { + title: 'Create Incident', action: ( ), - }; - - const onTriggerRefresh = () => { - setRefreshIncidents(true); + icon: , }; return ( - - - } - /> - } - action={triggerAlarm.action} - /> - - } - /> - - - - - - - + + + + + + } + /> ); }; diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx index 7ef57d67b4..8073ab44b5 100644 --- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx +++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.test.tsx @@ -70,7 +70,7 @@ describe('TriggerDialog', () => { handleDialog={() => {}} name={entity.metadata.name} integrationKey="abc123" - onTriggerRefresh={() => {}} + onIncidentCreated={() => {}} /> , ), diff --git a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx index 4717b8fece..4fa88a1b9a 100644 --- a/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx +++ b/plugins/pagerduty/src/components/TriggerDialog/TriggerDialog.tsx @@ -35,7 +35,7 @@ type Props = { integrationKey: string; showDialog: boolean; handleDialog: () => void; - onTriggerRefresh: () => void; + onIncidentCreated: () => void; }; export const TriggerDialog = ({ @@ -43,7 +43,7 @@ export const TriggerDialog = ({ integrationKey, showDialog, handleDialog, - onTriggerRefresh, + onIncidentCreated: onIncidentCreated, }: Props) => { const alertApi = useApi(alertApiRef); const identityApi = useApi(identityApiRef); @@ -72,24 +72,20 @@ export const TriggerDialog = ({ alertApi.post({ message: `Alarm successfully triggered by ${userName}`, }); - onTriggerRefresh(); + onIncidentCreated(); handleDialog(); } + }, [value, alertApi, handleDialog, userName, onIncidentCreated]); - if (error) { - alertApi.post({ - message: `Failed to trigger alarm. ${error.message}`, - severity: 'error', - }); - } - }, [value, error, alertApi, handleDialog, userName, onTriggerRefresh]); - - if (!showDialog) { - return null; + if (error) { + alertApi.post({ + message: `Failed to trigger alarm. ${error.message}`, + severity: 'error', + }); } return ( - + This action will trigger an incident for "{name}". diff --git a/plugins/pagerduty/src/components/types.ts b/plugins/pagerduty/src/components/types.ts index a93791fe8e..371f4eea6d 100644 --- a/plugins/pagerduty/src/components/types.ts +++ b/plugins/pagerduty/src/components/types.ts @@ -56,3 +56,10 @@ export type User = { html_url: string; name: string; }; + +export type SubHeaderLink = { + title: string; + href?: string; + icon: React.ReactNode; + action?: React.ReactNode; +};