From 21d83eb2cc24b7595f7b561cba20fa963d2eca8c Mon Sep 17 00:00:00 2001 From: Marko Simon Date: Tue, 4 Oct 2022 11:42:39 +0200 Subject: [PATCH] add status pages Signed-off-by: Marko Simon --- plugins/ilert/src/api/client.ts | 33 ++++ plugins/ilert/src/api/index.ts | 3 +- plugins/ilert/src/api/types.ts | 11 ++ .../src/components/ILertPage/ILertPage.tsx | 4 + .../components/ServicesPage/StatusChip.tsx | 10 +- .../components/ServicesPage/TableTitle.tsx | 97 --------- .../StatusPage/StatusPageActionsMenu.tsx | 79 ++++++++ .../components/StatusPage/StatusPageLink.tsx | 50 +++++ .../StatusPage/StatusPageStatus.tsx | 61 ++++++ .../components/StatusPage/StatusPageURL.tsx | 49 +++++ .../StatusPage/StatusPageVisibility.tsx | 51 +++++ .../StatusPage/index.ts} | 19 +- .../components/StatusPagePage/StatusChip.tsx | 82 ++++++++ .../StatusPagePage/StatusPagesPage.tsx | 67 +++++++ .../StatusPagePage/StatusPagesTable.tsx | 184 ++++++++++++++++++ .../StatusPagePage/VisibilityChip.tsx | 48 +++++ .../src/components/StatusPagePage/index.ts | 17 ++ plugins/ilert/src/hooks/useStatusPages.ts | 93 +++++++++ plugins/ilert/src/types.ts | 18 ++ 19 files changed, 856 insertions(+), 120 deletions(-) delete mode 100644 plugins/ilert/src/components/ServicesPage/TableTitle.tsx create mode 100644 plugins/ilert/src/components/StatusPage/StatusPageActionsMenu.tsx create mode 100644 plugins/ilert/src/components/StatusPage/StatusPageLink.tsx create mode 100644 plugins/ilert/src/components/StatusPage/StatusPageStatus.tsx create mode 100644 plugins/ilert/src/components/StatusPage/StatusPageURL.tsx create mode 100644 plugins/ilert/src/components/StatusPage/StatusPageVisibility.tsx rename plugins/ilert/src/{hooks/useNewService.ts => components/StatusPage/index.ts} (67%) create mode 100644 plugins/ilert/src/components/StatusPagePage/StatusChip.tsx create mode 100644 plugins/ilert/src/components/StatusPagePage/StatusPagesPage.tsx create mode 100644 plugins/ilert/src/components/StatusPagePage/StatusPagesTable.tsx create mode 100644 plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx create mode 100644 plugins/ilert/src/components/StatusPagePage/index.ts create mode 100644 plugins/ilert/src/hooks/useStatusPages.ts diff --git a/plugins/ilert/src/api/client.ts b/plugins/ilert/src/api/client.ts index 4269ef583d..69b0a171f9 100644 --- a/plugins/ilert/src/api/client.ts +++ b/plugins/ilert/src/api/client.ts @@ -29,6 +29,7 @@ import { OnCall, Schedule, Service, + StatusPage, UptimeMonitor, User, } from '../types'; @@ -37,6 +38,7 @@ import { GetAlertsCountOpts, GetAlertsOpts, GetServicesOpts, + GetStatusPagesOpts, ILertApi, ServiceRequest, } from './types'; @@ -519,6 +521,27 @@ export class ILertClient implements ILertApi { return response; } + async fetchStatusPages(opts?: GetStatusPagesOpts): Promise { + const init = { + headers: JSON_HEADERS, + }; + const query = new URLSearchParams(); + if (opts?.maxResults !== undefined) { + query.append('max-results', String(opts.maxResults)); + } + if (opts?.startIndex !== undefined) { + query.append('start-index', String(opts.startIndex)); + } + + query.append('include', 'subscribed'); + + const response = await this.fetch( + `/api/status-pages?${query.toString()}`, + init, + ); + return response; + } + getAlertDetailsURL(alert: Alert): string { return `${this.baseUrl}/alert/view.jsf?id=${encodeURIComponent(alert.id)}`; } @@ -556,6 +579,16 @@ export class ILertClient implements ILertApi { )}`; } + getStatusPageDetailsURL(statusPage: StatusPage): string { + return `${this.baseUrl}/status-page/view.jsf?id=${encodeURIComponent( + statusPage.id, + )}`; + } + + getStatusPageURL(statusPage: StatusPage): string { + return statusPage.domain ? statusPage.domain : statusPage.subdomain; + } + getUserPhoneNumber(user: User | null) { return user?.mobile?.number || user?.landline?.number || ''; } diff --git a/plugins/ilert/src/api/index.ts b/plugins/ilert/src/api/index.ts index f0688e0f63..30120225a4 100644 --- a/plugins/ilert/src/api/index.ts +++ b/plugins/ilert/src/api/index.ts @@ -16,10 +16,11 @@ export { ilertApiRef, ILertClient } from './client'; export type { - AlertEventRequest as EventRequest, + EventRequest as EventRequest, GetAlertsCountOpts, GetAlertsOpts, GetServicesOpts, + GetStatusPagesOpts, ILertApi, TableState, } from './types'; diff --git a/plugins/ilert/src/api/types.ts b/plugins/ilert/src/api/types.ts index 39eb373ae2..4e6e56dfd5 100644 --- a/plugins/ilert/src/api/types.ts +++ b/plugins/ilert/src/api/types.ts @@ -24,6 +24,7 @@ import { OnCall, Schedule, Service, + StatusPage, UptimeMonitor, User, } from '../types'; @@ -53,6 +54,12 @@ export type GetServicesOpts = { startIndex?: number; }; +/** @public */ +export type GetStatusPagesOpts = { + maxResults?: number; + startIndex?: number; +}; + /** @public */ export type EventRequest = { integrationKey: string; @@ -109,12 +116,16 @@ export interface ILertApi { fetchServices(opts?: GetServicesOpts): Promise; createService(eventRequest: ServiceRequest): Promise; + fetchStatusPages(opts?: GetStatusPagesOpts): Promise; + getAlertDetailsURL(alert: Alert): string; getAlertSourceDetailsURL(alertSource: AlertSource | null): string; getEscalationPolicyDetailsURL(escalationPolicy: EscalationPolicy): string; getUptimeMonitorDetailsURL(uptimeMonitor: UptimeMonitor): string; getScheduleDetailsURL(schedule: Schedule): string; getServiceDetailsURL(service: Service): string; + getStatusPageDetailsURL(statusPage: StatusPage): string; + getStatusPageURL(statusPage: StatusPage): string; getUserPhoneNumber(user: User | null): string; getUserInitials(user: User | null): string; } diff --git a/plugins/ilert/src/components/ILertPage/ILertPage.tsx b/plugins/ilert/src/components/ILertPage/ILertPage.tsx index cc7527c316..45bd219d62 100644 --- a/plugins/ilert/src/components/ILertPage/ILertPage.tsx +++ b/plugins/ilert/src/components/ILertPage/ILertPage.tsx @@ -24,6 +24,7 @@ import React from 'react'; import { AlertsPage } from '../AlertsPage'; import { OnCallSchedulesPage } from '../OnCallSchedulesPage'; import { ServicesPage } from '../ServicesPage'; +import { StatusPagesPage } from '../StatusPagePage'; import { UptimeMonitorsPage } from '../UptimeMonitorsPage'; /** @public */ @@ -34,6 +35,7 @@ export const ILertPage = () => { { label: 'Alerts' }, { label: 'Uptime Monitors' }, { label: 'Services' }, + { label: 'Status pages' }, ]; const renderTab = () => { switch (selectedTab) { @@ -45,6 +47,8 @@ export const ILertPage = () => { return ; case 3: return ; + case 4: + return ; default: return null; } diff --git a/plugins/ilert/src/components/ServicesPage/StatusChip.tsx b/plugins/ilert/src/components/ServicesPage/StatusChip.tsx index 5eb31b8a94..f23c64450b 100644 --- a/plugins/ilert/src/components/ServicesPage/StatusChip.tsx +++ b/plugins/ilert/src/components/ServicesPage/StatusChip.tsx @@ -27,7 +27,7 @@ import { serviceStatusLabels } from '../Service/ServiceStatus'; const OperationalChip = withStyles({ root: { - backgroundColor: '#4caf50', + backgroundColor: '#388E3D', color: 'white', margin: 0, }, @@ -35,28 +35,28 @@ const OperationalChip = withStyles({ const UnderMaintenanceChip = withStyles({ root: { - backgroundColor: '#ffb74d', + backgroundColor: '#616161', color: 'white', margin: 0, }, })(Chip); const DegradedChip = withStyles({ root: { - backgroundColor: '#d32f2f', + backgroundColor: '#FBC02D', color: 'white', margin: 0, }, })(Chip); const PartialOutageChip = withStyles({ root: { - backgroundColor: '#d4a5bb', + backgroundColor: '#F57C02', color: 'white', margin: 0, }, })(Chip); const MajorOutageChip = withStyles({ root: { - backgroundColor: '#28c548', + backgroundColor: '#D22F2E', color: 'white', margin: 0, }, diff --git a/plugins/ilert/src/components/ServicesPage/TableTitle.tsx b/plugins/ilert/src/components/ServicesPage/TableTitle.tsx deleted file mode 100644 index c610e91d94..0000000000 --- a/plugins/ilert/src/components/ServicesPage/TableTitle.tsx +++ /dev/null @@ -1,97 +0,0 @@ -/* - * Copyright 2021 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 Checkbox from '@material-ui/core/Checkbox'; -import FormControl from '@material-ui/core/FormControl'; -import ListItemText from '@material-ui/core/ListItemText'; -import MenuItem from '@material-ui/core/MenuItem'; -import Select from '@material-ui/core/Select'; -import { makeStyles } from '@material-ui/core/styles'; -import Typography from '@material-ui/core/Typography'; -import React from 'react'; -import { ACCEPTED, AlertStatus, PENDING, RESOLVED } from '../../types'; -import { alertStatusLabels } from '../Alert/AlertStatus'; - -const ITEM_HEIGHT = 48; -const ITEM_PADDING_TOP = 8; -const MenuProps = { - PaperProps: { - style: { - maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, - width: 250, - }, - }, -}; - -const useStyles = makeStyles({ - root: { - display: 'flex', - }, - label: { - marginTop: 8, - marginRight: 4, - }, - formControl: { - minWidth: 120, - maxWidth: 300, - }, - grow: { - flexGrow: 1, - }, -}); - -export const TableTitle = ({ - alertStates, - onAlertStatesChange, -}: { - alertStates: AlertStatus[]; - onAlertStatesChange: (states: AlertStatus[]) => void; -}) => { - const classes = useStyles(); - const handleAlertStatusSelectChange = (event: any) => { - onAlertStatesChange(event.target.value); - }; - - return ( -
- - Status: - - - - -
- ); -}; diff --git a/plugins/ilert/src/components/StatusPage/StatusPageActionsMenu.tsx b/plugins/ilert/src/components/StatusPage/StatusPageActionsMenu.tsx new file mode 100644 index 0000000000..8fe94eb2e1 --- /dev/null +++ b/plugins/ilert/src/components/StatusPage/StatusPageActionsMenu.tsx @@ -0,0 +1,79 @@ +/* + * Copyright 2021 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 { IconButton, Menu, MenuItem, Typography } from '@material-ui/core'; +import MoreVertIcon from '@material-ui/icons/MoreVert'; +import React from 'react'; +import { ilertApiRef } from '../../api'; +import { StatusPage } from '../../types'; + +import { Link } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; + +export const StatusPageActionsMenu = ({ + statusPage, +}: { + statusPage: StatusPage; +}) => { + const ilertApi = useApi(ilertApiRef); + const [anchorEl, setAnchorEl] = React.useState(null); + + const handleClick = (event: React.MouseEvent) => { + setAnchorEl(event.currentTarget); + }; + + const handleCloseMenu = () => { + setAnchorEl(null); + }; + + return ( + <> + + + + + + + + View in iLert + + + + + + + View status page + + + + + + ); +}; diff --git a/plugins/ilert/src/components/StatusPage/StatusPageLink.tsx b/plugins/ilert/src/components/StatusPage/StatusPageLink.tsx new file mode 100644 index 0000000000..60daddf9df --- /dev/null +++ b/plugins/ilert/src/components/StatusPage/StatusPageLink.tsx @@ -0,0 +1,50 @@ +/* + * Copyright 2021 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 { makeStyles } from '@material-ui/core/styles'; +import React from 'react'; +import { ilertApiRef } from '../../api'; +import { StatusPage } from '../../types'; + +import { Link } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; + +const useStyles = makeStyles({ + link: { + lineHeight: '22px', + }, +}); + +export const StatusPageLink = ({ + statusPage, +}: { + statusPage: StatusPage | null; +}) => { + const ilertApi = useApi(ilertApiRef); + const classes = useStyles(); + + if (!statusPage) { + return null; + } + + return ( + + #{statusPage.id} + + ); +}; diff --git a/plugins/ilert/src/components/StatusPage/StatusPageStatus.tsx b/plugins/ilert/src/components/StatusPage/StatusPageStatus.tsx new file mode 100644 index 0000000000..b40db5d8f3 --- /dev/null +++ b/plugins/ilert/src/components/StatusPage/StatusPageStatus.tsx @@ -0,0 +1,61 @@ +/* + * Copyright 2021 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 { StatusError, StatusOK } from '@backstage/core-components'; +import { makeStyles } from '@material-ui/core/styles'; +import Tooltip from '@material-ui/core/Tooltip'; +import React from 'react'; +import { + DEGRADED, + MAJOR_OUTAGE, + OPERATIONAL, + PARTIAL_OUTAGE, + StatusPage, + UNDER_MAINTENANCE, +} from '../../types'; + +const useStyles = makeStyles({ + denseListIcon: { + marginRight: 0, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, +}); + +export const statusPageStatusLabels = { + [OPERATIONAL]: 'Operational', + [UNDER_MAINTENANCE]: 'Under maintenance', + [DEGRADED]: 'Degraded', + [PARTIAL_OUTAGE]: 'Partial outage', + [MAJOR_OUTAGE]: 'Major outage', +} as Record; + +export const StatusPageStatus = ({ + statusPage, +}: { + statusPage: StatusPage; +}) => { + const classes = useStyles(); + + return ( + +
+ {statusPage.status === 'OPERATIONAL' ? : } +
+
+ ); +}; diff --git a/plugins/ilert/src/components/StatusPage/StatusPageURL.tsx b/plugins/ilert/src/components/StatusPage/StatusPageURL.tsx new file mode 100644 index 0000000000..b94903333a --- /dev/null +++ b/plugins/ilert/src/components/StatusPage/StatusPageURL.tsx @@ -0,0 +1,49 @@ +/* + * Copyright 2021 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 { makeStyles } from '@material-ui/core/styles'; +import React from 'react'; +import { ilertApiRef } from '../../api'; +import { StatusPage } from '../../types'; + +import { Link } from '@backstage/core-components'; +import { useApi } from '@backstage/core-plugin-api'; + +const useStyles = makeStyles({ + link: { + lineHeight: '22px', + }, +}); + +export const StatusPageURL = ({ + statusPage, +}: { + statusPage: StatusPage | null; +}) => { + const ilertApi = useApi(ilertApiRef); + const classes = useStyles(); + + if (!statusPage) { + return null; + } + + const url = ilertApi.getStatusPageURL(statusPage); + + return ( + + {url} + + ); +}; diff --git a/plugins/ilert/src/components/StatusPage/StatusPageVisibility.tsx b/plugins/ilert/src/components/StatusPage/StatusPageVisibility.tsx new file mode 100644 index 0000000000..7c8f63dd35 --- /dev/null +++ b/plugins/ilert/src/components/StatusPage/StatusPageVisibility.tsx @@ -0,0 +1,51 @@ +/* + * Copyright 2021 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 { StatusError, StatusOK } from '@backstage/core-components'; +import { makeStyles } from '@material-ui/core/styles'; +import Tooltip from '@material-ui/core/Tooltip'; +import React from 'react'; +import { PRIVATE, PUBLIC, StatusPage } from '../../types'; + +const useStyles = makeStyles({ + denseListIcon: { + marginRight: 0, + display: 'flex', + flexDirection: 'column', + alignItems: 'center', + justifyContent: 'center', + }, +}); + +export const statusPageVisibilityLabels = { + [PUBLIC]: 'Public', + [PRIVATE]: 'Private', +} as Record; + +export const StatusPageVisibility = ({ + statusPage, +}: { + statusPage: StatusPage; +}) => { + const classes = useStyles(); + + return ( + +
+ {statusPage.visibility === 'PUBLIC' ? : } +
+
+ ); +}; diff --git a/plugins/ilert/src/hooks/useNewService.ts b/plugins/ilert/src/components/StatusPage/index.ts similarity index 67% rename from plugins/ilert/src/hooks/useNewService.ts rename to plugins/ilert/src/components/StatusPage/index.ts index 7d50d29a39..93ea9deaba 100644 --- a/plugins/ilert/src/hooks/useNewService.ts +++ b/plugins/ilert/src/components/StatusPage/index.ts @@ -13,20 +13,5 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -import React from 'react'; - -export const useNewService = () => { - const [name, setName] = React.useState(''); - const [isLoading, setIsLoading] = React.useState(false); - - return [ - { - name, - isLoading, - }, - { - setName, - setIsLoading, - }, - ] as const; -}; +export * from './StatusPageActionsMenu'; +export * from './StatusPageStatus'; diff --git a/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx b/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx new file mode 100644 index 0000000000..50a64de212 --- /dev/null +++ b/plugins/ilert/src/components/StatusPagePage/StatusChip.tsx @@ -0,0 +1,82 @@ +/* + * Copyright 2021 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 { Chip, withStyles } from '@material-ui/core'; +import React from 'react'; +import { + DEGRADED, + MAJOR_OUTAGE, + OPERATIONAL, + PARTIAL_OUTAGE, + StatusPage, + UNDER_MAINTENANCE, +} from '../../types'; +import { statusPageStatusLabels } from '../StatusPage/StatusPageStatus'; + +const OperationalChip = withStyles({ + root: { + backgroundColor: '#388E3D', + color: 'white', + margin: 0, + }, +})(Chip); + +const UnderMaintenanceChip = withStyles({ + root: { + backgroundColor: '#616161', + color: 'white', + margin: 0, + }, +})(Chip); +const DegradedChip = withStyles({ + root: { + backgroundColor: '#FBC02D', + color: 'white', + margin: 0, + }, +})(Chip); +const PartialOutageChip = withStyles({ + root: { + backgroundColor: '#F57C02', + color: 'white', + margin: 0, + }, +})(Chip); +const MajorOutageChip = withStyles({ + root: { + backgroundColor: '#D22F2E', + color: 'white', + margin: 0, + }, +})(Chip); + +export const StatusChip = ({ statusPage }: { statusPage: StatusPage }) => { + const label = `${statusPageStatusLabels[statusPage.status]}`; + + switch (statusPage.status) { + case OPERATIONAL: + return ; + case UNDER_MAINTENANCE: + return ; + case DEGRADED: + return ; + case PARTIAL_OUTAGE: + return ; + case MAJOR_OUTAGE: + return ; + default: + return ; + } +}; diff --git a/plugins/ilert/src/components/StatusPagePage/StatusPagesPage.tsx b/plugins/ilert/src/components/StatusPagePage/StatusPagesPage.tsx new file mode 100644 index 0000000000..6e4290c7c9 --- /dev/null +++ b/plugins/ilert/src/components/StatusPagePage/StatusPagesPage.tsx @@ -0,0 +1,67 @@ +/* + * Copyright 2021 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 { + Content, + ContentHeader, + ResponseErrorPanel, + SupportButton, +} from '@backstage/core-components'; +import { AuthenticationError } from '@backstage/errors'; +import React from 'react'; +import { useStatusPages } from '../../hooks/useStatusPages'; +import { MissingAuthorizationHeaderError } from '../Errors'; +import { StatusPagesTable } from './StatusPagesTable'; + +export const StatusPagesPage = () => { + const [ + { tableState, statusPages, isLoading, error }, + { onChangePage, onChangeRowsPerPage, setIsLoading }, + ] = useStatusPages(true); + + if (error) { + if (error instanceof AuthenticationError) { + return ( + + + + ); + } + + return ( + + + + ); + } + + return ( + + + + This helps you to bring iLert into your developer portal. + + + + + ); +}; diff --git a/plugins/ilert/src/components/StatusPagePage/StatusPagesTable.tsx b/plugins/ilert/src/components/StatusPagePage/StatusPagesTable.tsx new file mode 100644 index 0000000000..8b013fdb35 --- /dev/null +++ b/plugins/ilert/src/components/StatusPagePage/StatusPagesTable.tsx @@ -0,0 +1,184 @@ +/* + * Copyright 2021 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 { makeStyles } from '@material-ui/core/styles'; +import Typography from '@material-ui/core/Typography'; +import React from 'react'; +import { TableState } from '../../api'; +import { StatusPage } from '../../types'; +import { VisibilityChip } from './VisibilityChip'; + +import { Table, TableColumn } from '@backstage/core-components'; +import { StatusPageActionsMenu } from '../StatusPage/StatusPageActionsMenu'; +import { StatusPageLink } from '../StatusPage/StatusPageLink'; +import { StatusPageURL } from '../StatusPage/StatusPageURL'; +import { StatusChip } from './StatusChip'; + +const useStyles = makeStyles(theme => ({ + empty: { + padding: theme.spacing(2), + display: 'flex', + justifyContent: 'center', + }, +})); + +export const StatusPagesTable = ({ + statusPages, + tableState, + isLoading, + onChangePage, + onChangeRowsPerPage, + compact, +}: { + statusPages: StatusPage[]; + tableState: TableState; + isLoading: boolean; + setIsLoading: (isLoading: boolean) => void; + onChangePage: (page: number) => void; + onChangeRowsPerPage: (pageSize: number) => void; + compact?: boolean; +}) => { + const classes = useStyles(); + + // const xsColumnStyle = { + // width: '5%', + // maxWidth: '5%', + // }; + const smColumnStyle = { + width: '10%', + maxWidth: '10%', + }; + // const mdColumnStyle = { + // width: '15%', + // maxWidth: '15%', + // }; + // const lgColumnStyle = { + // width: '20%', + // maxWidth: '20%', + // }; + const xlColumnStyle = { + width: '30%', + maxWidth: '30%', + }; + + const idColumn: TableColumn = { + title: 'ID', + field: 'id', + highlight: true, + cellStyle: smColumnStyle, + headerStyle: smColumnStyle, + render: rowData => , + }; + const nameColumn: TableColumn = { + title: 'Name', + field: 'name', + cellStyle: !compact ? xlColumnStyle : undefined, + headerStyle: !compact ? xlColumnStyle : undefined, + render: rowData => {(rowData as StatusPage).name}, + }; + const urlColumn: TableColumn = { + title: 'URL', + field: 'url', + cellStyle: smColumnStyle, + headerStyle: smColumnStyle, + render: rowData => , + }; + const visibilityColumn: TableColumn = { + title: 'Visibility', + field: 'visibility', + cellStyle: smColumnStyle, + headerStyle: smColumnStyle, + render: rowData => , + }; + const statusColumn: TableColumn = { + title: 'Status', + field: 'status', + cellStyle: smColumnStyle, + headerStyle: smColumnStyle, + render: rowData => , + }; + const actionsColumn: TableColumn = { + title: '', + field: '', + cellStyle: smColumnStyle, + headerStyle: smColumnStyle, + render: rowData => ( + + ), + }; + + const columns: TableColumn[] = compact + ? [nameColumn, statusColumn, urlColumn, actionsColumn] + : [ + idColumn, + nameColumn, + statusColumn, + urlColumn, + visibilityColumn, + actionsColumn, + ]; + let tableStyle: React.CSSProperties = {}; + if (compact) { + tableStyle = { + width: '100%', + maxWidth: '100%', + minWidth: '0', + height: 'calc(100% - 10px)', + boxShadow: 'none !important', + borderRadius: 'none !important', + }; + } else { + tableStyle = { + width: '100%', + maxWidth: '100%', + }; + } + + return ( + + No status pages + + } + title={ + + STATUS PAGES + + } + page={tableState.page} + onPageChange={onChangePage} + onRowsPerPageChange={onChangeRowsPerPage} + // localization={{ header: { actions: undefined } }} + columns={columns} + data={statusPages} + isLoading={isLoading} + /> + ); +}; diff --git a/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx b/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx new file mode 100644 index 0000000000..fb72819216 --- /dev/null +++ b/plugins/ilert/src/components/StatusPagePage/VisibilityChip.tsx @@ -0,0 +1,48 @@ +/* + * Copyright 2021 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 { Chip, withStyles } from '@material-ui/core'; +import React from 'react'; +import { PRIVATE, PUBLIC, StatusPage } from '../../types'; +import { statusPageVisibilityLabels } from '../StatusPage/StatusPageVisibility'; + +const PrivateChip = withStyles({ + root: { + backgroundColor: '#4caf50', + color: 'white', + margin: 0, + }, +})(Chip); + +const PublicChip = withStyles({ + root: { + backgroundColor: '#ffb74d', + color: 'white', + margin: 0, + }, +})(Chip); + +export const VisibilityChip = ({ statusPage }: { statusPage: StatusPage }) => { + const label = `${statusPageVisibilityLabels[statusPage.visibility]}`; + + switch (statusPage.visibility) { + case PRIVATE: + return ; + case PUBLIC: + return ; + default: + return ; + } +}; diff --git a/plugins/ilert/src/components/StatusPagePage/index.ts b/plugins/ilert/src/components/StatusPagePage/index.ts new file mode 100644 index 0000000000..8bc923ffe1 --- /dev/null +++ b/plugins/ilert/src/components/StatusPagePage/index.ts @@ -0,0 +1,17 @@ +/* + * Copyright 2021 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 * from './StatusPagesPage'; +export * from './StatusPagesTable'; diff --git a/plugins/ilert/src/hooks/useStatusPages.ts b/plugins/ilert/src/hooks/useStatusPages.ts new file mode 100644 index 0000000000..740e0f515f --- /dev/null +++ b/plugins/ilert/src/hooks/useStatusPages.ts @@ -0,0 +1,93 @@ +/* + * Copyright 2021 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 { errorApiRef, useApi } from '@backstage/core-plugin-api'; +import { AuthenticationError } from '@backstage/errors'; +import React from 'react'; +import useAsyncRetry from 'react-use/lib/useAsyncRetry'; +import { GetStatusPagesOpts, ilertApiRef, TableState } from '../api'; +import { StatusPage } from '../types'; + +export const useStatusPages = (paging: boolean) => { + const ilertApi = useApi(ilertApiRef); + const errorApi = useApi(errorApiRef); + + const [tableState, setTableState] = React.useState({ + page: 0, + pageSize: 10, + }); + + const [statusPagesList, setStatusPagesList] = React.useState( + [], + ); + const [isLoading, setIsLoading] = React.useState(false); + + const fetchStatusPagesCall = async () => { + try { + setIsLoading(true); + const opts: GetStatusPagesOpts = {}; + if (paging) { + opts.maxResults = tableState.pageSize; + opts.startIndex = tableState.page * tableState.pageSize; + } + const data = await ilertApi.fetchStatusPages(opts); + setStatusPagesList(data || []); + setIsLoading(false); + } catch (e) { + if (!(e instanceof AuthenticationError)) { + errorApi.post(e); + } + setIsLoading(false); + throw e; + } + }; + + const fetchStatusPages = useAsyncRetry(fetchStatusPagesCall, [tableState]); + + const refetchStatusPages = () => { + setTableState({ ...tableState, page: 0 }); + Promise.all([fetchStatusPagesCall()]); + }; + + const error = fetchStatusPages.error; + const retry = () => { + fetchStatusPages.retry(); + }; + + const onChangePage = (page: number) => { + setTableState({ ...tableState, page }); + }; + const onChangeRowsPerPage = (p: number) => { + setTableState({ ...tableState, pageSize: p }); + }; + + return [ + { + tableState, + statusPages: statusPagesList, + isLoading, + error, + }, + { + setTableState, + setStatusPagesList, + setIsLoading, + retry, + refetchStatusPages, + onChangePage, + onChangeRowsPerPage, + }, + ] as const; +}; diff --git a/plugins/ilert/src/types.ts b/plugins/ilert/src/types.ts index ebe5c1b39d..28b8b7ca8a 100644 --- a/plugins/ilert/src/types.ts +++ b/plugins/ilert/src/types.ts @@ -251,6 +251,14 @@ export type ServiceStatus = | typeof PARTIAL_OUTAGE | typeof MAJOR_OUTAGE; +/** @public */ +export const PRIVATE = 'PRIVATE'; +/** @public */ +export const PUBLIC = 'PUBLIC'; + +/** @public */ +export type StatusPageVisibility = typeof PRIVATE | typeof PUBLIC; + /** @public */ export interface AlertSourceEmailPredicate { field: 'EMAIL_FROM' | 'EMAIL_SUBJECT' | 'EMAIL_BODY'; @@ -422,3 +430,13 @@ export interface Uptime { export interface UptimePercentage { p90: number; } + +/** @public */ +export interface StatusPage { + id: number; + name: string; + domain: string; + subdomain: string; + visibility: StatusPageVisibility; + status: ServiceStatus; +}