Browse Source

feat(workflow): Remove alert issue redesign flag (#29061)

removes old alert details page, but leaves a redirect is still required.
Scott Cooper 3 years ago
parent
commit
5e2848b1b5

+ 13 - 20
static/app/components/sidebar/index.tsx

@@ -327,26 +327,19 @@ class Sidebar extends React.Component<Props, State> {
     );
 
     const alerts = hasOrganization && (
-      <Feature features={['incidents', 'alert-details-redesign']} requireAll={false}>
-        {({features}) => {
-          const hasIncidents = features.includes('incidents');
-          const hasAlertList = features.includes('alert-details-redesign');
-          const alertsPath =
-            hasIncidents && !hasAlertList
-              ? `/organizations/${organization.slug}/alerts/`
-              : `/organizations/${organization.slug}/alerts/rules/`;
-          return (
-            <SidebarItem
-              {...sidebarItemProps}
-              onClick={(_id, evt) => this.navigateWithGlobalSelection(alertsPath, evt)}
-              icon={<IconSiren size="md" />}
-              label={t('Alerts')}
-              to={alertsPath}
-              id="alerts"
-            />
-          );
-        }}
-      </Feature>
+      <SidebarItem
+        {...sidebarItemProps}
+        onClick={(_id, evt) =>
+          this.navigateWithGlobalSelection(
+            `/organizations/${organization.slug}/alerts/rules/`,
+            evt
+          )
+        }
+        icon={<IconSiren size="md" />}
+        label={t('Alerts')}
+        to={`/organizations/${organization.slug}/alerts/rules/`}
+        id="alerts"
+      />
     );
 
     const monitors = hasOrganization && (

+ 0 - 179
static/app/views/alerts/details/activity/activity.tsx

@@ -1,179 +0,0 @@
-import * as React from 'react';
-import styled from '@emotion/styled';
-import groupBy from 'lodash/groupBy';
-import moment from 'moment';
-
-import {Client} from 'app/api';
-import ActivityItem from 'app/components/activity/item';
-import Note from 'app/components/activity/note';
-import NoteInputWithStorage from 'app/components/activity/note/inputWithStorage';
-import {CreateError} from 'app/components/activity/note/types';
-import ErrorBoundary from 'app/components/errorBoundary';
-import LoadingError from 'app/components/loadingError';
-import TimeSince from 'app/components/timeSince';
-import {t} from 'app/locale';
-import space from 'app/styles/space';
-import {User} from 'app/types';
-import {NoteType} from 'app/types/alerts';
-
-import {ActivityType, Incident, IncidentActivityType} from '../../types';
-
-import ActivityPlaceholder from './activityPlaceholder';
-import DateDivider from './dateDivider';
-import StatusItem from './statusItem';
-
-type NoteProps = React.ComponentProps<typeof Note>;
-
-type Props = {
-  api: Client;
-  alertId: string;
-  incident?: Incident;
-  loading: boolean;
-  error: boolean;
-  me: User;
-  activities: null | ActivityType[];
-  noteInputId: string;
-  noteInputProps?: object;
-
-  createError: boolean;
-  createBusy: boolean;
-  createErrorJSON: null | CreateError;
-  onCreateNote: (note: NoteType) => void;
-  onUpdateNote: (note: NoteType, activity: ActivityType) => void;
-  onDeleteNote: (activity: ActivityType) => void;
-};
-
-/**
- * Activity component on Incident Details view
- * Allows user to leave a comment on an alertId as well as
- * fetch and render existing activity items.
- */
-class Activity extends React.Component<Props> {
-  handleUpdateNote = (note: NoteType, {activity}: NoteProps) => {
-    const {onUpdateNote} = this.props;
-    onUpdateNote(note, activity as ActivityType);
-  };
-
-  handleDeleteNote = ({activity}: NoteProps) => {
-    const {onDeleteNote} = this.props;
-    onDeleteNote(activity as ActivityType);
-  };
-
-  render() {
-    const {
-      loading,
-      error,
-      me,
-      alertId,
-      incident,
-      activities,
-      noteInputId,
-      createBusy,
-      createError,
-      createErrorJSON,
-      onCreateNote,
-    } = this.props;
-
-    const noteProps = {
-      minHeight: 80,
-      projectSlugs: (incident && incident.projects) || [],
-      ...this.props.noteInputProps,
-    };
-    const activitiesByDate = groupBy(activities, ({dateCreated}) =>
-      moment(dateCreated).format('ll')
-    );
-    const today = moment().format('ll');
-
-    return (
-      <div>
-        <ActivityItem author={{type: 'user', user: me}}>
-          {() => (
-            <NoteInputWithStorage
-              key={noteInputId}
-              storageKey="incidentIdinput"
-              itemKey={alertId}
-              onCreate={onCreateNote}
-              busy={createBusy}
-              error={createError}
-              errorJSON={createErrorJSON}
-              placeholder={t(
-                'Leave a comment, paste a tweet, or link any other relevant information about this alert...'
-              )}
-              {...noteProps}
-            />
-          )}
-        </ActivityItem>
-
-        {error && <LoadingError message={t('There was a problem loading activities')} />}
-
-        {loading && (
-          <React.Fragment>
-            <ActivityPlaceholder />
-            <ActivityPlaceholder />
-            <ActivityPlaceholder />
-          </React.Fragment>
-        )}
-
-        {!loading &&
-          !error &&
-          Object.entries(activitiesByDate).map(([date, activitiesForDate]) => {
-            const title =
-              date === today ? (
-                t('Today')
-              ) : (
-                <React.Fragment>
-                  {date} <StyledTimeSince date={date} />
-                </React.Fragment>
-              );
-            return (
-              <React.Fragment key={date}>
-                <DateDivider>{title}</DateDivider>
-                {activitiesForDate &&
-                  activitiesForDate.map(activity => {
-                    const authorName = activity.user?.name ?? 'Sentry';
-
-                    if (activity.type === IncidentActivityType.COMMENT) {
-                      return (
-                        <ErrorBoundary mini key={`note-${activity.id}`}>
-                          <Note
-                            showTime
-                            user={activity.user as User}
-                            modelId={activity.id}
-                            text={activity.comment || ''}
-                            dateCreated={activity.dateCreated}
-                            activity={activity}
-                            authorName={authorName}
-                            onDelete={this.handleDeleteNote}
-                            onUpdate={this.handleUpdateNote}
-                            {...noteProps}
-                          />
-                        </ErrorBoundary>
-                      );
-                    } else {
-                      return (
-                        <ErrorBoundary mini key={`note-${activity.id}`}>
-                          <StatusItem
-                            showTime
-                            incident={incident}
-                            authorName={authorName}
-                            activity={activity}
-                          />
-                        </ErrorBoundary>
-                      );
-                    }
-                  })}
-              </React.Fragment>
-            );
-          })}
-      </div>
-    );
-  }
-}
-
-export default Activity;
-
-const StyledTimeSince = styled(TimeSince)`
-  color: ${p => p.theme.gray300};
-  font-size: ${p => p.theme.fontSizeSmall};
-  margin-left: ${space(0.5)};
-`;

+ 0 - 26
static/app/views/alerts/details/activity/activityPlaceholder.tsx

@@ -1,26 +0,0 @@
-import {useTheme} from '@emotion/react';
-import styled from '@emotion/styled';
-
-import ActivityItem from 'app/components/activity/item';
-import space from 'app/styles/space';
-
-function ActivityPlaceholder() {
-  const theme = useTheme();
-
-  return (
-    <ActivityItem
-      bubbleProps={{
-        backgroundColor: theme.backgroundSecondary,
-        borderColor: theme.backgroundSecondary,
-      }}
-    >
-      {() => <Placeholder />}
-    </ActivityItem>
-  );
-}
-
-export default ActivityPlaceholder;
-
-const Placeholder = styled('div')`
-  padding: ${space(4)};
-`;

+ 0 - 31
static/app/views/alerts/details/activity/dateDivider.tsx

@@ -1,31 +0,0 @@
-import styled from '@emotion/styled';
-
-import space from 'app/styles/space';
-
-const DateDivider = styled('div')`
-  font-size: ${p => p.theme.fontSizeMedium};
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  color: ${p => p.theme.subText};
-  margin: ${space(1.5)} 0;
-
-  &:before,
-  &:after {
-    content: '';
-    display: block;
-    flex-grow: 1;
-    height: 1px;
-    background-color: ${p => p.theme.gray200};
-  }
-
-  &:before {
-    margin-right: ${space(2)};
-  }
-
-  &:after {
-    margin-left: ${space(2)};
-  }
-`;
-
-export default DateDivider;

+ 0 - 231
static/app/views/alerts/details/activity/index.tsx

@@ -1,231 +0,0 @@
-import {PureComponent} from 'react';
-import {RouteComponentProps} from 'react-router';
-
-import {
-  createIncidentNote,
-  deleteIncidentNote,
-  fetchIncidentActivities,
-  updateIncidentNote,
-} from 'app/actionCreators/incident';
-import {Client} from 'app/api';
-import {CreateError} from 'app/components/activity/note/types';
-import {DEFAULT_ERROR_JSON} from 'app/constants';
-import ConfigStore from 'app/stores/configStore';
-import {NoteType} from 'app/types/alerts';
-import {uniqueId} from 'app/utils/guid';
-import {replaceAtArrayIndex} from 'app/utils/replaceAtArrayIndex';
-import withApi from 'app/utils/withApi';
-
-import {
-  ActivityType,
-  ActivityTypeDraft,
-  Incident,
-  IncidentActivityType,
-  IncidentStatus,
-} from '../../types';
-
-import Activity from './activity';
-
-type Activities = Array<ActivityType | ActivityType>;
-
-type Props = Pick<RouteComponentProps<{alertId: string; orgId: string}, {}>, 'params'> & {
-  api: Client;
-  incident?: Incident;
-  incidentStatus: IncidentStatus | null;
-};
-
-type State = {
-  loading: boolean;
-  error: boolean;
-  noteInputId: string;
-  noteInputText: string;
-  createBusy: boolean;
-  createError: boolean;
-  createErrorJSON: null | CreateError;
-  activities: null | Activities;
-};
-
-/**
- * Activity component on Incident Details view
- * Allows user to leave a comment on an alertId as well as
- * fetch and render existing activity items.
- */
-class ActivityContainer extends PureComponent<Props, State> {
-  state: State = {
-    loading: true,
-    error: false,
-    noteInputId: uniqueId(),
-    noteInputText: '',
-    createBusy: false,
-    createError: false,
-    createErrorJSON: null,
-    activities: null,
-  };
-
-  componentDidMount() {
-    this.fetchData();
-  }
-
-  componentDidUpdate(prevProps: Props) {
-    // Only refetch if incidentStatus changes.
-    //
-    // This component can mount before incident details is fully loaded.
-    // In which case, `incidentStatus` is null and we will be fetching via `cDM`
-    // There's no need to fetch this gets updated due to incident details being loaded
-    if (
-      prevProps.incidentStatus !== null &&
-      prevProps.incidentStatus !== this.props.incidentStatus
-    ) {
-      this.fetchData();
-    }
-  }
-
-  async fetchData() {
-    const {api, params} = this.props;
-    const {alertId, orgId} = params;
-
-    try {
-      const activities = await fetchIncidentActivities(api, orgId, alertId);
-      this.setState({activities, loading: false});
-    } catch (err) {
-      this.setState({loading: false, error: !!err});
-    }
-  }
-
-  handleCreateNote = async (note: NoteType) => {
-    const {api, params} = this.props;
-    const {alertId, orgId} = params;
-
-    const newActivity: ActivityTypeDraft = {
-      comment: note.text,
-      type: IncidentActivityType.COMMENT,
-      dateCreated: new Date().toISOString(),
-      user: ConfigStore.get('user'),
-      id: uniqueId(),
-      incidentIdentifier: alertId,
-    };
-
-    this.setState(state => ({
-      createBusy: true,
-      // This is passed as a key to NoteInput that re-mounts
-      // (basically so we can reset text input to empty string)
-      noteInputId: uniqueId(),
-      activities: [newActivity, ...(state.activities || [])] as Activities,
-      noteInputText: '',
-    }));
-
-    try {
-      const newNote = await createIncidentNote(api, orgId, alertId, note);
-
-      this.setState(state => {
-        // Update activities to replace our fake new activity with activity object from server
-        const activities = [
-          newNote,
-          ...(state.activities!.filter(
-            activity => activity !== newActivity
-          ) as ActivityType[]),
-        ];
-
-        return {
-          createBusy: false,
-          activities,
-        };
-      });
-    } catch (error) {
-      this.setState(state => {
-        const activities = state.activities!.filter(activity => activity !== newActivity);
-
-        return {
-          // We clear the textarea immediately when submitting, restore
-          // value when there has been an error
-          noteInputText: note.text,
-          activities,
-          createBusy: false,
-          createError: true,
-          createErrorJSON: error.responseJSON || DEFAULT_ERROR_JSON,
-        };
-      });
-    }
-  };
-
-  getIndexAndActivityFromState = (activity: ActivityType | ActivityTypeDraft) => {
-    // `index` should probably be found, if not let error hit Sentry
-    const index =
-      this.state.activities !== null
-        ? this.state.activities.findIndex(({id}) => id === activity.id)
-        : '';
-    return [index, this.state.activities![index]];
-  };
-
-  handleDeleteNote = async (activity: ActivityType | ActivityTypeDraft) => {
-    const {api, params} = this.props;
-    const {alertId, orgId} = params;
-
-    const [index, oldActivity] = this.getIndexAndActivityFromState(activity);
-
-    this.setState(state => ({
-      activities: removeFromArrayIndex(state.activities!, index),
-    }));
-
-    try {
-      await deleteIncidentNote(api, orgId, alertId, activity.id);
-    } catch (error) {
-      this.setState(state => ({
-        activities: replaceAtArrayIndex(state.activities!, index, oldActivity),
-      }));
-    }
-  };
-
-  handleUpdateNote = async (
-    note: NoteType,
-    activity: ActivityType | ActivityTypeDraft
-  ) => {
-    const {api, params} = this.props;
-    const {alertId, orgId} = params;
-
-    const [index, oldActivity] = this.getIndexAndActivityFromState(activity);
-
-    this.setState(state => ({
-      activities: replaceAtArrayIndex(state.activities!, index, {
-        ...oldActivity,
-        comment: note.text,
-      }),
-    }));
-
-    try {
-      await updateIncidentNote(api, orgId, alertId, activity.id, note);
-    } catch (error) {
-      this.setState(state => ({
-        activities: replaceAtArrayIndex(state.activities!, index, oldActivity),
-      }));
-    }
-  };
-
-  render() {
-    const {api, params, incident, ...props} = this.props;
-    const {alertId} = params;
-    const me = ConfigStore.get('user');
-
-    return (
-      <Activity
-        alertId={alertId}
-        me={me}
-        api={api}
-        {...this.state}
-        loading={this.state.loading || !incident}
-        incident={incident}
-        onCreateNote={this.handleCreateNote}
-        onUpdateNote={this.handleUpdateNote}
-        onDeleteNote={this.handleDeleteNote}
-        {...props}
-      />
-    );
-  }
-}
-export default withApi(ActivityContainer);
-
-function removeFromArrayIndex<T>(array: T[], index: number): T[] {
-  const newArray = [...array];
-  newArray.splice(index, 1);
-  return newArray;
-}

+ 0 - 118
static/app/views/alerts/details/activity/statusItem.tsx

@@ -1,118 +0,0 @@
-import {Component} from 'react';
-import styled from '@emotion/styled';
-
-import ActivityItem from 'app/components/activity/item';
-import {t, tct} from 'app/locale';
-import getDynamicText from 'app/utils/getDynamicText';
-
-import {
-  ActivityType,
-  Incident,
-  IncidentActivityType,
-  IncidentStatus,
-  IncidentStatusMethod,
-} from '../../types';
-
-type Props = {
-  activity: ActivityType;
-  showTime: boolean;
-
-  /**
-   * Author name can be undefined if there is no author, e.g. if it's a system activity
-   */
-  authorName?: string;
-  incident?: Incident;
-};
-
-/**
- * StatusItem renders status changes for Alerts
- *
- * For example: incident detected, or closed
- *
- * Note `activity.dateCreated` refers to when the activity was created vs.
- * `incident.dateStarted` which is when an incident was first detected or created
- */
-class StatusItem extends Component<Props> {
-  render() {
-    const {activity, authorName, incident, showTime} = this.props;
-
-    const isDetected = activity.type === IncidentActivityType.DETECTED;
-    const isStarted = activity.type === IncidentActivityType.STARTED;
-    const isClosed =
-      activity.type === IncidentActivityType.STATUS_CHANGE &&
-      activity.value === `${IncidentStatus.CLOSED}`;
-    const isTriggerChange =
-      activity.type === IncidentActivityType.STATUS_CHANGE && !isClosed;
-
-    // Unknown activity, don't render anything
-    if (!isStarted && !isDetected && !isClosed && !isTriggerChange) {
-      return null;
-    }
-
-    const currentTrigger = getTriggerName(activity.value);
-    const previousTrigger = getTriggerName(activity.previousValue);
-
-    return (
-      <ActivityItem
-        showTime={showTime}
-        author={{
-          type: activity.user ? 'user' : 'system',
-          user: activity.user || undefined,
-        }}
-        header={
-          <div>
-            {isTriggerChange &&
-              previousTrigger &&
-              tct('Alert status changed from [previousTrigger] to [currentTrigger]', {
-                previousTrigger,
-                currentTrigger: <StatusValue>{currentTrigger}</StatusValue>,
-              })}
-            {isTriggerChange &&
-              !previousTrigger &&
-              tct('Alert status changed to [currentTrigger]', {
-                currentTrigger: <StatusValue>{currentTrigger}</StatusValue>,
-              })}
-            {isClosed &&
-              incident?.statusMethod === IncidentStatusMethod.RULE_UPDATED &&
-              t(
-                'This alert has been auto-resolved because the rule that triggered it has been modified or deleted.'
-              )}
-            {isClosed &&
-              incident?.statusMethod !== IncidentStatusMethod.RULE_UPDATED &&
-              tct('[user] resolved the alert', {
-                user: <StatusValue>{authorName}</StatusValue>,
-              })}
-            {isDetected &&
-              (incident?.alertRule
-                ? t('Alert was created')
-                : tct('[user] created an alert', {
-                    user: <StatusValue>{authorName}</StatusValue>,
-                  }))}
-            {isStarted && t('Trigger conditions were met for the interval')}
-          </div>
-        }
-        date={getDynamicText({value: activity.dateCreated, fixed: new Date(0)})}
-        interval={isStarted ? incident?.alertRule.timeWindow : undefined}
-      />
-    );
-  }
-}
-
-export default StatusItem;
-
-const StatusValue = styled('span')`
-  font-weight: bold;
-`;
-
-export function getTriggerName(value: string | null) {
-  if (value === `${IncidentStatus.WARNING}`) {
-    return t('Warning');
-  }
-
-  if (value === `${IncidentStatus.CRITICAL}`) {
-    return t('Critical');
-  }
-
-  // Otherwise, activity type is not status change
-  return '';
-}

+ 0 - 412
static/app/views/alerts/details/body.tsx

@@ -1,412 +0,0 @@
-import {Component} from 'react';
-import {RouteComponentProps} from 'react-router';
-import styled from '@emotion/styled';
-
-import Feature from 'app/components/acl/feature';
-import Alert from 'app/components/alert';
-import Button from 'app/components/button';
-import {SectionHeading} from 'app/components/charts/styles';
-import Duration from 'app/components/duration';
-import {KeyValueTable, KeyValueTableRow} from 'app/components/keyValueTable';
-import Link from 'app/components/links/link';
-import NavTabs from 'app/components/navTabs';
-import {Panel, PanelBody, PanelFooter} from 'app/components/panels';
-import Placeholder from 'app/components/placeholder';
-import SeenByList from 'app/components/seenByList';
-import {IconWarning} from 'app/icons';
-import {t, tct} from 'app/locale';
-import {PageContent} from 'app/styles/organization';
-import space from 'app/styles/space';
-import {Organization, Project} from 'app/types';
-import {defined} from 'app/utils';
-import Projects from 'app/utils/projects';
-import theme from 'app/utils/theme';
-import {alertDetailsLink} from 'app/views/alerts/details/index';
-import {DATASET_EVENT_TYPE_FILTERS} from 'app/views/alerts/incidentRules/constants';
-import {makeDefaultCta} from 'app/views/alerts/incidentRules/presets';
-import {AlertRuleThresholdType} from 'app/views/alerts/incidentRules/types';
-
-import {
-  AlertRuleStatus,
-  Incident,
-  IncidentStats,
-  IncidentStatus,
-  IncidentStatusMethod,
-} from '../types';
-import {DATA_SOURCE_LABELS, getIncidentMetricPreset, isIssueAlert} from '../utils';
-
-import Activity from './activity';
-import Chart from './chart';
-
-type Props = {
-  incident?: Incident;
-  organization: Organization;
-  stats?: IncidentStats;
-} & RouteComponentProps<{alertId: string; orgId: string}, {}>;
-
-export default class DetailsBody extends Component<Props> {
-  get metricPreset() {
-    const {incident} = this.props;
-    return incident ? getIncidentMetricPreset(incident) : undefined;
-  }
-
-  /**
-   * Return a string describing the threshold based on the threshold and the type
-   */
-  getThresholdText(
-    value: number | '' | null | undefined,
-    thresholdType: AlertRuleThresholdType,
-    isAlert: boolean = false
-  ) {
-    if (!defined(value)) {
-      return '';
-    }
-
-    const isAbove = thresholdType === AlertRuleThresholdType.ABOVE;
-    const direction = isAbove === isAlert ? '>' : '<';
-
-    return `${direction} ${value}`;
-  }
-
-  renderRuleDetails() {
-    const {incident} = this.props;
-
-    if (incident === undefined) {
-      return <Placeholder height="200px" />;
-    }
-
-    const criticalTrigger = incident?.alertRule.triggers.find(
-      ({label}) => label === 'critical'
-    );
-    const warningTrigger = incident?.alertRule.triggers.find(
-      ({label}) => label === 'warning'
-    );
-
-    return (
-      <KeyValueTable>
-        <KeyValueTableRow
-          keyName={t('Data Source')}
-          value={DATA_SOURCE_LABELS[incident.alertRule?.dataset]}
-        />
-        <KeyValueTableRow keyName={t('Metric')} value={incident.alertRule?.aggregate} />
-        <KeyValueTableRow
-          keyName={t('Time Window')}
-          value={incident && <Duration seconds={incident.alertRule.timeWindow * 60} />}
-        />
-        {incident.alertRule?.query && (
-          <KeyValueTableRow
-            keyName={t('Filter')}
-            value={
-              <span title={incident.alertRule?.query}>{incident.alertRule?.query}</span>
-            }
-          />
-        )}
-        <KeyValueTableRow
-          keyName={t('Critical Trigger')}
-          value={this.getThresholdText(
-            criticalTrigger?.alertThreshold,
-            incident.alertRule?.thresholdType,
-            true
-          )}
-        />
-        {defined(warningTrigger) && (
-          <KeyValueTableRow
-            keyName={t('Warning Trigger')}
-            value={this.getThresholdText(
-              warningTrigger?.alertThreshold,
-              incident.alertRule?.thresholdType,
-              true
-            )}
-          />
-        )}
-
-        {defined(incident.alertRule?.resolveThreshold) && (
-          <KeyValueTableRow
-            keyName={t('Resolution')}
-            value={this.getThresholdText(
-              incident.alertRule?.resolveThreshold,
-              incident.alertRule?.thresholdType
-            )}
-          />
-        )}
-      </KeyValueTable>
-    );
-  }
-
-  renderChartHeader() {
-    const {incident} = this.props;
-    const alertRule = incident?.alertRule;
-
-    return (
-      <ChartHeader>
-        <div>
-          {this.metricPreset?.name ?? t('Custom metric')}
-          <ChartParameters>
-            {tct('Metric: [metric] over [window]', {
-              metric: <code>{alertRule?.aggregate ?? '\u2026'}</code>,
-              window: (
-                <code>
-                  {incident ? (
-                    <Duration seconds={incident.alertRule.timeWindow * 60} />
-                  ) : (
-                    '\u2026'
-                  )}
-                </code>
-              ),
-            })}
-            {(alertRule?.query || incident?.alertRule?.dataset) &&
-              tct('Filter: [datasetType] [filter]', {
-                datasetType: incident?.alertRule?.dataset && (
-                  <code>{DATASET_EVENT_TYPE_FILTERS[incident.alertRule.dataset]}</code>
-                ),
-                filter: alertRule?.query && <code>{alertRule.query}</code>,
-              })}
-          </ChartParameters>
-        </div>
-      </ChartHeader>
-    );
-  }
-
-  renderChartActions() {
-    const {incident, params, stats} = this.props;
-
-    return (
-      // Currently only one button in pannel, hide panel if not available
-      <Feature features={['discover-basic']}>
-        <ChartActions>
-          <Projects slugs={incident?.projects} orgId={params.orgId}>
-            {({initiallyLoaded, fetching, projects}) => {
-              const preset = this.metricPreset;
-              const ctaOpts = {
-                orgSlug: params.orgId,
-                projects: (initiallyLoaded ? projects : []) as Project[],
-                incident,
-                stats,
-              };
-
-              const {buttonText, ...props} = preset
-                ? preset.makeCtaParams(ctaOpts)
-                : makeDefaultCta(ctaOpts);
-
-              return (
-                <Button
-                  size="small"
-                  priority="primary"
-                  disabled={!incident || fetching || !initiallyLoaded}
-                  {...props}
-                >
-                  {buttonText}
-                </Button>
-              );
-            }}
-          </Projects>
-        </ChartActions>
-      </Feature>
-    );
-  }
-
-  render() {
-    const {params, incident, organization, stats} = this.props;
-
-    const hasRedesign =
-      incident?.alertRule &&
-      !isIssueAlert(incident?.alertRule) &&
-      organization.features.includes('alert-details-redesign');
-    const alertRuleLink =
-      hasRedesign && incident
-        ? alertDetailsLink(organization, incident)
-        : `/organizations/${params.orgId}/alerts/metric-rules/${incident?.projects[0]}/${incident?.alertRule?.id}/`;
-
-    return (
-      <StyledPageContent>
-        <Main>
-          {incident &&
-            incident.status === IncidentStatus.CLOSED &&
-            incident.statusMethod === IncidentStatusMethod.RULE_UPDATED && (
-              <AlertWrapper>
-                <Alert type="warning" icon={<IconWarning size="sm" />}>
-                  {t(
-                    'This alert has been auto-resolved because the rule that triggered it has been modified or deleted'
-                  )}
-                </Alert>
-              </AlertWrapper>
-            )}
-          <PageContent>
-            <ChartPanel>
-              <PanelBody withPadding>
-                {this.renderChartHeader()}
-                {incident && stats ? (
-                  <Chart
-                    triggers={incident.alertRule.triggers}
-                    resolveThreshold={incident.alertRule.resolveThreshold}
-                    aggregate={incident.alertRule.aggregate}
-                    data={stats.eventStats.data}
-                    started={incident.dateStarted}
-                    closed={incident.dateClosed || undefined}
-                  />
-                ) : (
-                  <Placeholder height="200px" />
-                )}
-              </PanelBody>
-              {this.renderChartActions()}
-            </ChartPanel>
-          </PageContent>
-          <DetailWrapper>
-            <ActivityPageContent>
-              <StyledNavTabs underlined>
-                <li className="active">
-                  <Link to="">{t('Activity')}</Link>
-                </li>
-
-                <SeenByTab>
-                  {incident && (
-                    <StyledSeenByList
-                      iconPosition="right"
-                      seenBy={incident.seenBy}
-                      iconTooltip={t('People who have viewed this alert')}
-                    />
-                  )}
-                </SeenByTab>
-              </StyledNavTabs>
-              <Activity
-                incident={incident}
-                params={params}
-                incidentStatus={!!incident ? incident.status : null}
-              />
-            </ActivityPageContent>
-            <Sidebar>
-              <SidebarHeading>
-                <span>{t('Alert Rule')}</span>
-                {(incident?.alertRule?.status !== AlertRuleStatus.SNAPSHOT ||
-                  hasRedesign) && (
-                  <SideHeaderLink
-                    disabled={!!incident?.id}
-                    to={
-                      incident?.id
-                        ? {
-                            pathname: alertRuleLink,
-                          }
-                        : ''
-                    }
-                  >
-                    {t('View Alert Rule')}
-                  </SideHeaderLink>
-                )}
-              </SidebarHeading>
-              {this.renderRuleDetails()}
-            </Sidebar>
-          </DetailWrapper>
-        </Main>
-      </StyledPageContent>
-    );
-  }
-}
-
-const Main = styled('div')`
-  background-color: ${p => p.theme.background};
-  padding-top: ${space(3)};
-  flex-grow: 1;
-`;
-
-const DetailWrapper = styled('div')`
-  display: flex;
-  flex: 1;
-
-  @media (max-width: ${p => p.theme.breakpoints[0]}) {
-    flex-direction: column-reverse;
-  }
-`;
-
-const ActivityPageContent = styled(PageContent)`
-  @media (max-width: ${theme.breakpoints[0]}) {
-    width: 100%;
-    margin-bottom: 0;
-  }
-`;
-
-const Sidebar = styled(PageContent)`
-  width: 400px;
-  flex: none;
-  padding-top: ${space(3)};
-
-  @media (max-width: ${theme.breakpoints[0]}) {
-    width: 100%;
-    padding-top: ${space(3)};
-    margin-bottom: 0;
-    border-bottom: 1px solid ${p => p.theme.border};
-  }
-`;
-
-const SidebarHeading = styled(SectionHeading)`
-  display: flex;
-  justify-content: space-between;
-`;
-
-const SideHeaderLink = styled(Link)`
-  font-weight: normal;
-`;
-
-const StyledPageContent = styled(PageContent)`
-  padding: 0;
-  flex-direction: column;
-`;
-
-const ChartPanel = styled(Panel)``;
-
-const ChartHeader = styled('header')`
-  margin-bottom: ${space(1)};
-`;
-
-const ChartActions = styled(PanelFooter)`
-  display: flex;
-  justify-content: flex-end;
-  padding: ${space(2)};
-`;
-
-const ChartParameters = styled('div')`
-  color: ${p => p.theme.subText};
-  font-size: ${p => p.theme.fontSizeMedium};
-  display: grid;
-  grid-auto-flow: column;
-  grid-auto-columns: max-content;
-  grid-gap: ${space(4)};
-  align-items: center;
-  overflow-x: auto;
-
-  > * {
-    position: relative;
-  }
-
-  > *:not(:last-of-type):after {
-    content: '';
-    display: block;
-    height: 70%;
-    width: 1px;
-    background: ${p => p.theme.gray200};
-    position: absolute;
-    right: -${space(2)};
-    top: 15%;
-  }
-`;
-
-const AlertWrapper = styled('div')`
-  padding: ${space(2)} ${space(4)} 0;
-`;
-
-const StyledNavTabs = styled(NavTabs)`
-  display: flex;
-`;
-
-const SeenByTab = styled('li')`
-  flex: 1;
-  margin-left: ${space(2)};
-  margin-right: 0;
-
-  .nav-tabs > & {
-    margin-right: 0;
-  }
-`;
-
-const StyledSeenByList = styled(SeenByList)`
-  margin-top: 0;
-`;

+ 0 - 222
static/app/views/alerts/details/chart.tsx

@@ -1,222 +0,0 @@
-import moment from 'moment';
-
-import MarkLine from 'app/components/charts/components/markLine';
-import MarkPoint from 'app/components/charts/components/markPoint';
-import LineChart, {LineChartSeries} from 'app/components/charts/lineChart';
-import {t} from 'app/locale';
-import space from 'app/styles/space';
-import theme from 'app/utils/theme';
-import {Trigger} from 'app/views/alerts/incidentRules/types';
-
-import closedSymbol from './closedSymbol';
-import startedSymbol from './startedSymbol';
-
-type Truthy<T> = T extends false | '' | 0 | null | undefined ? never : T;
-
-function truthy<T>(value: T): value is Truthy<T> {
-  return !!value;
-}
-
-type Data = [number, {count: number}[]];
-/**
- * So we'll have to see how this looks with real data, but echarts requires
- * an explicit (x,y) value to draw a symbol (incident started/closed bubble).
- *
- * This uses the closest date *without* going over.
- *
- * AFAICT we can't give it an x-axis value and have it draw on the line,
- * so we probably need to calculate the y-axis value ourselves if we want it placed
- * at the exact time.
- *
- * @param data Data array
- * @param needle the target timestamp
- */
-function getNearbyIndex(data: Data[], needle: number) {
-  // `data` is sorted, return the first index whose value (timestamp) is > `needle`
-  const index = data.findIndex(([ts]) => ts > needle);
-
-  // this shouldn't happen, as we try to buffer dates before start/end dates
-  if (index === 0) {
-    return 0;
-  }
-
-  return index !== -1 ? index - 1 : data.length - 1;
-}
-
-type Props = {
-  data: Data[];
-  aggregate: string;
-  started: string;
-  closed?: string;
-  triggers?: Trigger[];
-  resolveThreshold?: number | '' | null;
-};
-
-const Chart = (props: Props) => {
-  const {aggregate, data, started, closed, triggers, resolveThreshold} = props;
-  const startedTs = started && moment.utc(started).unix();
-  const closedTs = closed && moment.utc(closed).unix();
-  const chartData = data.map(([ts, val]) => [
-    ts * 1000,
-    val.length ? val.reduce((acc, {count} = {count: 0}) => acc + count, 0) : 0,
-  ]);
-
-  const startedCoordinate = startedTs
-    ? chartData[getNearbyIndex(data, startedTs)]
-    : undefined;
-  const showClosedMarker =
-    data && closedTs && data[data.length - 1] && data[data.length - 1][0] >= closedTs
-      ? true
-      : false;
-  const closedCoordinate =
-    closedTs && showClosedMarker ? chartData[getNearbyIndex(data, closedTs)] : undefined;
-
-  const seriesName = aggregate;
-
-  const warningTrigger = triggers?.find(trig => trig.label === 'warning');
-  const criticalTrigger = triggers?.find(trig => trig.label === 'critical');
-  const warningTriggerAlertThreshold =
-    typeof warningTrigger?.alertThreshold === 'number'
-      ? warningTrigger?.alertThreshold
-      : undefined;
-  const criticalTriggerAlertThreshold =
-    typeof criticalTrigger?.alertThreshold === 'number'
-      ? criticalTrigger?.alertThreshold
-      : undefined;
-  const alertResolveThreshold =
-    typeof resolveThreshold === 'number' ? resolveThreshold : undefined;
-
-  const marklinePrecision = Math.max(
-    ...[
-      warningTriggerAlertThreshold,
-      criticalTriggerAlertThreshold,
-      alertResolveThreshold,
-    ].map(decimal => {
-      if (!decimal || !isFinite(decimal)) return 0;
-      let e = 1;
-      let p = 0;
-      while (Math.round(decimal * e) / e !== decimal) {
-        e *= 10;
-        p += 1;
-      }
-      return p;
-    })
-  );
-
-  const lineSeries: LineChartSeries[] = [
-    {
-      // e.g. Events or Users
-      seriesName,
-      dataArray: chartData,
-      data: [],
-      markPoint: MarkPoint({
-        data: [
-          {
-            labelForValue: seriesName,
-            seriesName,
-            symbol: `image://${startedSymbol}`,
-            name: t('Alert Triggered'),
-            coord: startedCoordinate,
-          },
-          ...(closedTs
-            ? [
-                {
-                  labelForValue: seriesName,
-                  seriesName,
-                  symbol: `image://${closedSymbol}`,
-                  symbolSize: 24,
-                  name: t('Alert Resolved'),
-                  coord: closedCoordinate,
-                },
-              ]
-            : []),
-        ] as any, // TODO(ts): data on this type is likely incomplete (needs @types/echarts@4.6.2)
-      }),
-    },
-    warningTrigger &&
-      warningTriggerAlertThreshold && {
-        seriesName: 'Warning Alert',
-        type: 'line',
-        markLine: MarkLine({
-          silent: true,
-          lineStyle: {color: theme.yellow300},
-          data: [
-            {
-              yAxis: warningTriggerAlertThreshold,
-            } as any, // TODO(ts): data on this type is likely incomplete (needs @types/echarts@4.6.2)
-          ],
-          precision: marklinePrecision,
-          label: {
-            show: true,
-            position: 'insideEndTop',
-            formatter: 'WARNING',
-            color: theme.yellow300,
-            fontSize: 10,
-          } as any, // TODO(ts): Color is not an exposed option for label,
-        }),
-        data: [],
-      },
-    criticalTrigger &&
-      criticalTriggerAlertThreshold && {
-        seriesName: 'Critical Alert',
-        type: 'line',
-        markLine: MarkLine({
-          silent: true,
-          lineStyle: {color: theme.red200},
-          data: [
-            {
-              yAxis: criticalTriggerAlertThreshold,
-            } as any, // TODO(ts): data on this type is likely incomplete (needs @types/echarts@4.6.2)
-          ],
-          precision: marklinePrecision,
-          label: {
-            show: true,
-            position: 'insideEndTop',
-            formatter: 'CRITICAL',
-            color: theme.red300,
-            fontSize: 10,
-          } as any, // TODO(ts): Color is not an exposed option for label,
-        }),
-        data: [],
-      },
-    criticalTrigger &&
-      alertResolveThreshold && {
-        seriesName: 'Critical Resolve',
-        type: 'line',
-        markLine: MarkLine({
-          silent: true,
-          lineStyle: {color: theme.gray200},
-          data: [
-            {
-              yAxis: alertResolveThreshold,
-            } as any, // TODO(ts): data on this type is likely incomplete (needs @types/echarts@4.6.2)
-          ],
-          precision: marklinePrecision,
-          label: {
-            show: true,
-            position: 'insideEndBottom',
-            formatter: 'CRITICAL RESOLUTION',
-            color: theme.gray200,
-            fontSize: 10,
-          } as any, // TODO(ts): Color is not an option for label,
-        }),
-        data: [],
-      },
-  ].filter(truthy);
-
-  return (
-    <LineChart
-      isGroupedByDate
-      showTimeInTooltip
-      grid={{
-        left: 0,
-        right: 0,
-        top: space(2),
-        bottom: 0,
-      }}
-      series={lineSeries}
-    />
-  );
-};
-
-export default Chart;

+ 0 - 4
static/app/views/alerts/details/closedSymbol.tsx

@@ -1,4 +0,0 @@
-const closedSymbol =
-  'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGEAAABgCAMAAAA6hOw0AAABKVBMVEUAAABOsYlOxIlSuI9cwo9aupFawYpawZFRvI1XvYpXvY9Vuo5ZvopZvo5VvotVvo9YvotYvo9XvIpXv41Wwo1ZwpBVv49Yv4xYv41Xv4tXv45av4tXwYxWvItWv41Yv45YwYtXvYxXv4xXv49YvYtWv41Yv41WvoxYvoxYwIxWv45Zv4xZv45Yvo5YwI5Xv41Yvo9YwI1VvoxXvIxXvoxWvo1YvYxYv4xXvoxXwIxZvoxZwIxZwI5WvYtWv41XvoxXvo1ZvoxXv41WvYxWv4xYvYxYv4xWvY1Yvo1YwI5XvoxYvoxWv41Yv41Zv41XvoxYvo5YwI5XvY1Xvo1Xv41Xvo1Yvo1Wvo1Yvo1YwI1Xvo1Yvo1YwI5Xvo1Yvo1Yv41Xvo1Zvo1Yvo2junXyAAAAY3RSTlMADQ0ZGSUlJSYyMj8/P0tLS0tMTFNTVFRXWFhYW1xcY2NkZGRla2txcXFzc3N6ent9fX5+foKDg4qKioqKi4uSkpKTl5eXl5qamqKio6OjpKmpqqqvsLCxsbG5ubm8vLzBwckgUECPAAADb0lEQVR4AdXXBYLjOgwGYKfDDBpmpi7vMGPeMqeMuv8dlqG/67aOrUffAaLql+ykylLQOby9+zZV5K+KqTDcnuoJlJigf/stG7zdHW6TePzUHTcRTnkW6TkscivhsHI2csdWIscaAxEj4Ro9+PvFawRbHNtuW5wGMCD5NrbY0W5gl9AdO4tskmqL2EO5V7XSUWSzzF8Hyfl5+mpieePg8h03MOtUoHK5MUG6jcscm6zFL/BugxpYflYxl4hToHI8QU0sPDQ0MhpnyJcT1MLCMdfptS6Q3iALC89YU26wtIesOSZLDyuMokAZrDHKbZC1BX0aT00Z6QktUAyTL1ofi0grMEHxHOujaJHRMwIOJcLmGaXJwVXTlX2MQ54gF7i1Eb5ysMACOZnEjVpRNUKutUOOFiow7KBRC2fk7KBRE0eQEXl4D03gImFGMjn1GhfpGQGvUxEaj/MCeZmEJgKYM7Qg1MS0IaRlIsEmwvqQPpC3i7qYurnGQ/K2wTUG1VdTOGd/ef5jF4+bSEgYU6SPYY8EbGiDCAQ3ybBNvXgaKiTiPZ6IERyD9CC2lFrhPy5JxAP+4wRP9B6JSPIfb/D1do9ELMC6QoUN+QplqQqIayj1if+Y/59WeP03p/S3zGESJg1X630SsYTbKn/i4HK9/VtujX2oADffx7/l5uv+229v+TcQ6R+Wn/7utyis60fpMbyp+5oZl/+awVHznnBIw+qbomxMz7hG+/cKmyx5Nc0xnLfvelmyiVM9JIzJv4m5vBbSdyuCTZzqIX3XzmJNzDGEZP7Dnh+XWqSU+g5n7XmHP9RbwCb8c5qrmlowNFGZIydjeb0FbMJ/FC8ZWwDtRa6VHXdcVGwBrDB45l1gV2mCFPt1cc0g1a50XYzyc7GG/JyNGYEZvcSi622EGeE+oXOydFBllEooAKOIn9TcR9aU2pVZe4p1z1rWGDuvsq5XNdJVZPsa8Hwwo1SsEvxsgxpYv6xyvVWl4pbg/LOdMdKMrV8WmO0KYIkUm2WfHWwsztNX8/M7Dy4/VtlsSJnBuD2UelVr7SE7S7UrK6vs6FFCWepNOSU0o+wFmxzbDSYkPvBUr4ptJEaN0ipOwL+G1/NB72OL/HuVl/aR26aPd/35INH3+BUbRE9GEkpMondq+/A2Kn2PPXp1uD3Va/n0Lz+J4DdmmdHlAAAAAElFTkSuQmCC';
-
-export default closedSymbol;

+ 0 - 284
static/app/views/alerts/details/header.tsx

@@ -1,284 +0,0 @@
-import * as React from 'react';
-import {RouteComponentProps} from 'react-router';
-import isPropValid from '@emotion/is-prop-valid';
-import styled from '@emotion/styled';
-import moment from 'moment';
-
-import Breadcrumbs from 'app/components/breadcrumbs';
-import Count from 'app/components/count';
-import DropdownControl from 'app/components/dropdownControl';
-import Duration from 'app/components/duration';
-import ProjectBadge from 'app/components/idBadge/projectBadge';
-import LoadingError from 'app/components/loadingError';
-import MenuItem from 'app/components/menuItem';
-import PageHeading from 'app/components/pageHeading';
-import Placeholder from 'app/components/placeholder';
-import SubscribeButton from 'app/components/subscribeButton';
-import {IconCheckmark} from 'app/icons';
-import {t} from 'app/locale';
-import {PageHeader} from 'app/styles/organization';
-import space from 'app/styles/space';
-import {use24Hours} from 'app/utils/dates';
-import getDynamicText from 'app/utils/getDynamicText';
-import Projects from 'app/utils/projects';
-import {Dataset} from 'app/views/alerts/incidentRules/types';
-
-import Status from '../status';
-import {Incident, IncidentStats} from '../types';
-import {isOpen} from '../utils';
-
-type Props = Pick<RouteComponentProps<{orgId: string}, {}>, 'params'> & {
-  className?: string;
-  hasIncidentDetailsError: boolean;
-  incident?: Incident;
-  stats?: IncidentStats;
-  onSubscriptionChange: (event: React.MouseEvent) => void;
-  onStatusChange: (eventKey: any) => void;
-};
-
-export default class DetailsHeader extends React.Component<Props> {
-  renderStatus() {
-    const {incident, onStatusChange} = this.props;
-
-    const isIncidentOpen = incident && isOpen(incident);
-    const statusLabel = incident ? <StyledStatus incident={incident} /> : null;
-
-    return (
-      <DropdownControl
-        data-test-id="status-dropdown"
-        label={statusLabel}
-        alignRight
-        blendWithActor={false}
-        buttonProps={{
-          size: 'small',
-          disabled: !incident || !isIncidentOpen,
-          hideBottomBorder: false,
-        }}
-      >
-        <StatusMenuItem isActive>
-          {incident && <Status disableIconColor incident={incident} />}
-        </StatusMenuItem>
-        <StatusMenuItem onSelect={onStatusChange}>
-          <IconCheckmark color="green300" />
-          {t('Resolved')}
-        </StatusMenuItem>
-      </DropdownControl>
-    );
-  }
-
-  render() {
-    const {hasIncidentDetailsError, incident, params, stats, onSubscriptionChange} =
-      this.props;
-    const isIncidentReady = !!incident && !hasIncidentDetailsError;
-    // ex - Wed, May 27, 2020 11:09 AM
-    const dateFormat = use24Hours() ? 'ddd, MMM D, YYYY HH:mm' : 'llll';
-    const dateStarted =
-      incident && moment(new Date(incident.dateStarted)).format(dateFormat);
-    const duration =
-      incident &&
-      moment(incident.dateClosed ? new Date(incident.dateClosed) : new Date()).diff(
-        moment(new Date(incident.dateStarted)),
-        'seconds'
-      );
-    const isErrorDataset = incident?.alertRule?.dataset === Dataset.ERRORS;
-    const environmentLabel = incident?.alertRule?.environment ?? t('All Environments');
-
-    const project = incident && incident.projects && incident.projects[0];
-
-    return (
-      <Header>
-        <BreadCrumbBar>
-          <AlertBreadcrumbs
-            crumbs={[
-              {label: t('Alerts'), to: `/organizations/${params.orgId}/alerts/`},
-              {label: incident && `#${incident.id}`},
-            ]}
-          />
-          <Controls>
-            <SubscribeButton
-              disabled={!isIncidentReady}
-              isSubscribed={incident?.isSubscribed}
-              onClick={onSubscriptionChange}
-              size="small"
-            />
-            {this.renderStatus()}
-          </Controls>
-        </BreadCrumbBar>
-        <Details columns={isErrorDataset ? 5 : 3}>
-          <div>
-            <IncidentTitle data-test-id="incident-title" loading={!isIncidentReady}>
-              {incident && !hasIncidentDetailsError ? incident.title : 'Loading'}
-            </IncidentTitle>
-            <IncidentSubTitle loading={!isIncidentReady}>
-              {t('Triggered: ')}
-              {dateStarted}
-            </IncidentSubTitle>
-          </div>
-
-          {hasIncidentDetailsError ? (
-            <StyledLoadingError />
-          ) : (
-            <GroupedHeaderItems columns={isErrorDataset ? 5 : 3}>
-              <ItemTitle>{t('Environment')}</ItemTitle>
-              <ItemTitle>{t('Project')}</ItemTitle>
-              {isErrorDataset && <ItemTitle>{t('Users affected')}</ItemTitle>}
-              {isErrorDataset && <ItemTitle>{t('Total events')}</ItemTitle>}
-              <ItemTitle>{t('Active For')}</ItemTitle>
-              <ItemValue>{environmentLabel}</ItemValue>
-              <ItemValue>
-                {project ? (
-                  <Projects slugs={[project]} orgId={params.orgId}>
-                    {({projects}) =>
-                      projects?.length && (
-                        <ProjectBadge avatarSize={18} project={projects[0]} />
-                      )
-                    }
-                  </Projects>
-                ) : (
-                  <Placeholder height="25px" />
-                )}
-              </ItemValue>
-              {isErrorDataset && (
-                <ItemValue>
-                  {stats ? (
-                    <Count value={stats.uniqueUsers} />
-                  ) : (
-                    <Placeholder height="25px" />
-                  )}
-                </ItemValue>
-              )}
-              {isErrorDataset && (
-                <ItemValue>
-                  {stats ? (
-                    <Count value={stats.totalEvents} />
-                  ) : (
-                    <Placeholder height="25px" />
-                  )}
-                </ItemValue>
-              )}
-              <ItemValue>
-                {incident ? (
-                  <Duration
-                    seconds={getDynamicText({value: duration || 0, fixed: 1200})}
-                  />
-                ) : (
-                  <Placeholder height="25px" />
-                )}
-              </ItemValue>
-            </GroupedHeaderItems>
-          )}
-        </Details>
-      </Header>
-    );
-  }
-}
-
-const Header = styled('div')`
-  background-color: ${p => p.theme.backgroundSecondary};
-  border-bottom: 1px solid ${p => p.theme.border};
-`;
-
-const BreadCrumbBar = styled('div')`
-  display: flex;
-  margin-bottom: 0;
-  padding: ${space(2)} ${space(4)} ${space(1)};
-`;
-
-const AlertBreadcrumbs = styled(Breadcrumbs)`
-  flex-grow: 1;
-  font-size: ${p => p.theme.fontSizeExtraLarge};
-  padding: 0;
-`;
-
-const Controls = styled('div')`
-  display: grid;
-  grid-auto-flow: column;
-  grid-gap: ${space(1)};
-`;
-
-const Details = styled(PageHeader, {
-  shouldForwardProp: p => typeof p === 'string' && isPropValid(p) && p !== 'columns',
-})<{columns: 3 | 5}>`
-  margin-bottom: 0;
-  padding: ${space(1.5)} ${space(4)} ${space(2)};
-
-  grid-template-columns: max-content auto;
-  display: grid;
-  grid-gap: ${space(3)};
-  grid-auto-flow: column;
-
-  @media (max-width: ${p => p.theme.breakpoints[p.columns === 3 ? 1 : 2]}) {
-    grid-template-columns: auto;
-    grid-auto-flow: row;
-  }
-`;
-
-const StyledLoadingError = styled(LoadingError)`
-  flex: 1;
-
-  &.alert.alert-block {
-    margin: 0 20px;
-  }
-`;
-
-const GroupedHeaderItems = styled('div', {
-  shouldForwardProp: p => typeof p === 'string' && isPropValid(p) && p !== 'columns',
-})<{columns: 3 | 5}>`
-  display: grid;
-  grid-template-columns: repeat(${p => p.columns}, max-content);
-  grid-gap: ${space(1)} ${space(4)};
-  text-align: right;
-  margin-top: ${space(1)};
-
-  @media (max-width: ${p => p.theme.breakpoints[p.columns === 3 ? 1 : 2]}) {
-    text-align: left;
-  }
-`;
-
-const ItemTitle = styled('h6')`
-  font-size: ${p => p.theme.fontSizeSmall};
-  margin-bottom: 0;
-  text-transform: uppercase;
-  color: ${p => p.theme.gray300};
-  letter-spacing: 0.1px;
-`;
-
-const ItemValue = styled('div')`
-  display: flex;
-  justify-content: flex-end;
-  align-items: center;
-  font-size: ${p => p.theme.fontSizeExtraLarge};
-`;
-
-const IncidentTitle = styled(PageHeading, {
-  shouldForwardProp: p => typeof p === 'string' && isPropValid(p) && p !== 'loading',
-})<{loading: boolean}>`
-  ${p => p.loading && 'opacity: 0'};
-  line-height: 1.5;
-`;
-
-const IncidentSubTitle = styled('div', {
-  shouldForwardProp: p => typeof p === 'string' && isPropValid(p) && p !== 'loading',
-})<{loading: boolean}>`
-  ${p => p.loading && 'opacity: 0'};
-  font-size: ${p => p.theme.fontSizeLarge};
-  color: ${p => p.theme.gray300};
-`;
-
-const StyledStatus = styled(Status)`
-  margin-right: ${space(2)};
-`;
-
-const StatusMenuItem = styled(MenuItem)`
-  > span {
-    padding: ${space(1)} ${space(1.5)};
-    font-size: ${p => p.theme.fontSizeSmall};
-    font-weight: 600;
-    line-height: 1;
-    text-align: left;
-    display: grid;
-    grid-template-columns: max-content 1fr;
-    grid-gap: ${space(0.75)};
-    align-items: center;
-  }
-`;

Some files were not shown because too many files changed in this diff