Skip to content

Commit

Permalink
Maintenance page is deprecated, info message was added (#2497)
Browse files Browse the repository at this point in the history
# What this PR does
Maintenance page is deprecated, info message was added

closes grafana/oncall-private#1737

---------

Co-authored-by: Joey Orlando <joey.orlando@grafana.com>
Co-authored-by: Joey Orlando <joseph.t.orlando@gmail.com>
  • Loading branch information
3 people authored Jul 12, 2023
1 parent 01986bb commit df0ce35
Show file tree
Hide file tree
Showing 4 changed files with 29 additions and 201 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## Unreleased

### Changed

- Deprecated `/maintenance` web UI page. Maintenance is now handled at the integration level and can be performed
within a single integration's page. by @Ukochka ([2497](https://github.com/grafana/oncall/issues/2497))

## v1.3.9 (2023-07-12)

### Added
Expand Down
4 changes: 4 additions & 0 deletions grafana-plugin/src/pages/maintenance/Maintenance.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,7 @@
.title {
margin-bottom: var(--title-marginBottom);
}

.info-box {
width: 100%;
}
217 changes: 17 additions & 200 deletions grafana-plugin/src/pages/maintenance/Maintenance.tsx
Original file line number Diff line number Diff line change
@@ -1,220 +1,37 @@
import React from 'react';

import { Button, VerticalGroup } from '@grafana/ui';
import { Alert } from '@grafana/ui';
import cn from 'classnames/bind';
import { observer } from 'mobx-react';
import moment from 'moment-timezone';
import LegacyNavHeading from 'navbar/LegacyNavHeading';
import Emoji from 'react-emoji-render';

import GTable from 'components/GTable/GTable';
import Text from 'components/Text/Text';
import WithConfirm from 'components/WithConfirm/WithConfirm';
import MaintenanceForm from 'containers/MaintenanceForm/MaintenanceForm';
import { WithPermissionControlTooltip } from 'containers/WithPermissionControl/WithPermissionControlTooltip';
import { getAlertReceiveChannelDisplayName } from 'models/alert_receive_channel/alert_receive_channel.helpers';
import { AlertReceiveChannel } from 'models/alert_receive_channel/alert_receive_channel.types';
import { Maintenance, MaintenanceMode, MaintenanceType } from 'models/maintenance/maintenance.types';
import { PageProps, WithStoreProps } from 'state/types';
import { withMobXProviderContext } from 'state/withStore';
import { UserActions } from 'utils/authorization';
import PluginLink from 'components/PluginLink/PluginLink';

import styles from './Maintenance.module.css';

const cx = cn.bind(styles);

interface MaintenancePageProps extends PageProps, WithStoreProps {}

interface MaintenancePageState {
maintenanceData?: {
type?: MaintenanceType;
alert_receive_channel_id?: AlertReceiveChannel['id'];
disabled?: boolean;
};
}
interface MaintenancePageProps {}

@observer
class MaintenancePage extends React.Component<MaintenancePageProps, MaintenancePageState> {
state: MaintenancePageState = {};

async componentDidMount() {
const {
store: { alertReceiveChannelStore },
} = this.props;

this.update().then(this.parseQueryParams);

alertReceiveChannelStore.updateItems().then(() => {
this.forceUpdate();
});
}

componentDidUpdate(prevProps: MaintenancePageProps) {
if (this.props.query.maintenance_type !== prevProps.query.maintenance_type) {
this.parseQueryParams();
}
}

parseQueryParams = () => {
const { query } = this.props;

if ('maintenance_type' in query) {
const preselectedMaintenanceType = query.maintenance_type as MaintenanceType;
const preselectedAlertReceiveChannel = query.alert_receive_channel as AlertReceiveChannel['id'];

this.setState({
maintenanceData: {
type: preselectedMaintenanceType,
alert_receive_channel_id: preselectedAlertReceiveChannel,
},
});
}
};

update = () => {
const { store } = this.props;
const { maintenanceStore } = store;

return maintenanceStore.updateMaintenances();
};

class MaintenancePage extends React.Component<MaintenancePageProps> {
render() {
const { store } = this.props;
const { maintenanceStore } = store;
const { maintenanceData } = this.state;

const data = maintenanceStore?.maintenances;

const columns = [
{
width: 300,
title: 'Integration',
render: this.renderTitle,
key: 'Title',
},
{
width: 200,
title: 'Mode',
render: this.renderMode,
key: 'mode',
},
{
title: 'Progress',
render: this.renderDuration,
key: 'progress',
},
{
title: 'Time limit',
render: this.renderTimer,
key: 'timer',
},
{
width: 100,
key: 'action',
render: this.renderActionButtons,
},
];

return (
<>
<div className={cx('root')}>
<GTable
emptyText={data ? 'No maintenances found' : 'Loading...'}
title={() => (
<div className={cx('header')}>
<div style={{ display: 'flex', alignItems: 'baseline' }}>
<VerticalGroup>
<LegacyNavHeading>
<Text.Title level={3}>Maintenance</Text.Title>
</LegacyNavHeading>
<Text type="secondary" className={cx('title')}>
Mute noisy sources or use for debugging and avoid bothering your colleagues.
</Text>
</VerticalGroup>
</div>
<WithPermissionControlTooltip userAction={UserActions.MaintenanceWrite}>
<Button
onClick={() => {
this.setState({ maintenanceData: {} });
}}
variant="primary"
icon="plus"
>
New maintenance
</Button>
</WithPermissionControlTooltip>
</div>
)}
rowKey="id"
columns={columns}
data={data}
/>
</div>
{maintenanceData && (
<MaintenanceForm
initialData={maintenanceData}
onUpdate={this.update}
onHide={() => {
this.setState({ maintenanceData: undefined });
}}
/>
)}
<Alert
severity="info"
className={cx('info-box')}
// @ts-ignore
title={
<>
Maintenance mode is now controlled at the{' '}
<PluginLink query={{ page: 'integrations' }}> Integration</PluginLink> level. This page will soon be
removed.
</>
}
/>
</>
);
}

renderTitle = (maintenance: Maintenance) => {
const { store } = this.props;
const { alertReceiveChannelStore } = store;
const alertReceiveChannel = alertReceiveChannelStore.items
? alertReceiveChannelStore.items[maintenance.alert_receive_channel_id]
: undefined;

switch (maintenance.type) {
case MaintenanceType.alert_receive_channel:
return <Emoji text={getAlertReceiveChannelDisplayName(alertReceiveChannel)} />;

case MaintenanceType.organization:
return `${store.teamStore.currentTeam?.name} Team`;
}
};

renderMode = (maintenance: Maintenance) => {
return maintenance.maintenance_mode === MaintenanceMode.Debug ? 'Debug' : 'Maintenance';
};

renderActionButtons = (maintenance: Maintenance) => {
return (
<div className={cx('buttons')}>
<WithPermissionControlTooltip userAction={UserActions.MaintenanceWrite}>
<WithConfirm title="Are you sure to stop?" confirmText="Stop">
<Button variant="destructive" fill="text" onClick={this.getStopMaintenanceHandler(maintenance)}>
Stop
</Button>
</WithConfirm>
</WithPermissionControlTooltip>
</div>
);
};

renderDuration = (maintenance: Maintenance) => {
const started = moment(maintenance.started_at_timestamp * 1000);
const ended = moment(maintenance.maintenance_till_timestamp * 1000);
return `${started.format('MMM DD, YYYY HH:mm')} - ${ended.format('MMM DD, YYYY hh:mm')}`;
};

renderTimer = (maintenance: Maintenance) => {
return `ends ${moment(maintenance.maintenance_till_timestamp * 1000).fromNow()}`;
};

getStopMaintenanceHandler = (maintenance: Maintenance) => {
const { store } = this.props;
const { maintenanceStore } = store;

return () => {
maintenanceStore.stopMaintenanceMode(maintenance.type, maintenance.alert_receive_channel_id).then(this.update);
};
};
}

export default withMobXProviderContext(MaintenancePage);
export default MaintenancePage;
2 changes: 1 addition & 1 deletion grafana-plugin/src/plugin/GrafanaPluginRootPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,7 @@ export const Root = observer((props: AppRootProps) => {
<OutgoingWebhooks2 query={query} />
</Route>
<Route path={getRoutesForPage('maintenance')} exact>
<Maintenance query={query} />
<Maintenance />
</Route>
<Route path={getRoutesForPage('settings')} exact>
<SettingsPage />
Expand Down

0 comments on commit df0ce35

Please sign in to comment.