Maintenance page is deprecated, info message was added (#2497)

# What this PR does
Maintenance page is deprecated, info message was added

closes https://github.com/grafana/oncall-private/issues/1737

---------

Co-authored-by: Joey Orlando <joey.orlando@grafana.com>
Co-authored-by: Joey Orlando <joseph.t.orlando@gmail.com>
This commit is contained in:
Yulia Shanyrova 2023-07-12 12:11:42 +02:00 committed by GitHub
parent 01986bb8c9
commit df0ce35ae3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 29 additions and 201 deletions

View file

@ -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

View file

@ -10,3 +10,7 @@
.title {
margin-bottom: var(--title-marginBottom);
}
.info-box {
width: 100%;
}

View file

@ -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;

View file

@ -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 />