Browse Source

feat(crons): New recent check-ins table (#48648)

Before:
<img width="905" alt="image"
src="https://user-images.githubusercontent.com/9372512/236338779-8c0ea337-25b0-4e58-be19-fa5cc55861b6.png">

After:
<img width="904" alt="image"
src="https://user-images.githubusercontent.com/9372512/236544462-47eaee24-dadf-49b6-bf49-c5278f363624.png">


Fixes: https://github.com/getsentry/sentry/issues/48582
David Wang 1 year ago
parent
commit
54b8bba280

+ 78 - 66
static/app/views/monitors/components/monitorCheckIns.tsx

@@ -2,27 +2,24 @@ import React from 'react';
 import styled from '@emotion/styled';
 
 import {Button} from 'sentry/components/button';
+import {SectionHeading} from 'sentry/components/charts/styles';
+import DateTime from 'sentry/components/dateTime';
 import Duration from 'sentry/components/duration';
 import Pagination from 'sentry/components/pagination';
-import {Panel, PanelBody, PanelHeader, PanelItem} from 'sentry/components/panels';
-import TimeSince from 'sentry/components/timeSince';
-import {Tooltip} from 'sentry/components/tooltip';
+import {PanelTable} from 'sentry/components/panels';
+import StatusIndicator from 'sentry/components/statusIndicator';
+import Text from 'sentry/components/text';
 import {IconDownload} from 'sentry/icons';
 import {t, tct} from 'sentry/locale';
 import {space} from 'sentry/styles/space';
 import {defined} from 'sentry/utils';
 import useApiRequests from 'sentry/utils/useApiRequests';
-import {CheckInStatus, Monitor, MonitorEnvironment} from 'sentry/views/monitors/types';
-
-import CheckInIcon from './checkInIcon';
-
-type CheckIn = {
-  dateCreated: string;
-  duration: number;
-  id: string;
-  status: CheckInStatus;
-  attachmentId?: number;
-};
+import {
+  CheckIn,
+  CheckInStatus,
+  Monitor,
+  MonitorEnvironment,
+} from 'sentry/views/monitors/types';
 
 type Props = {
   monitor: Monitor;
@@ -34,6 +31,22 @@ type State = {
   checkInList: CheckIn[];
 };
 
+const checkStatusToIndicatorStatus: Record<CheckInStatus, string> = {
+  [CheckInStatus.OK]: 'success',
+  [CheckInStatus.ERROR]: 'error',
+  [CheckInStatus.IN_PROGRESS]: 'muted',
+  [CheckInStatus.MISSED]: 'warning',
+  [CheckInStatus.TIMEOUT]: 'error',
+};
+
+const statusToText = {
+  [CheckInStatus.OK]: t('Okay'),
+  [CheckInStatus.ERROR]: t('Failed'),
+  [CheckInStatus.IN_PROGRESS]: t('In Progress'),
+  [CheckInStatus.MISSED]: t('Missed'),
+  [CheckInStatus.TIMEOUT]: t('Timed Out'),
+};
+
 function MonitorCheckIns({monitor, monitorEnv, orgId}: Props) {
   const {data, hasError, renderComponent} = useApiRequests<State>({
     endpoints: [
@@ -49,45 +62,56 @@ function MonitorCheckIns({monitor, monitorEnv, orgId}: Props) {
   const generateDownloadUrl = (checkin: CheckIn) =>
     `/api/0/organizations/${orgId}/monitors/${monitor.slug}/checkins/${checkin.id}/attachment/`;
 
+  const emptyCell = <Text>{'\u2014'}</Text>;
+
   const renderedComponent = renderComponent(
     <React.Fragment>
-      <Panel>
-        <PanelHeader>{t('Recent Check-ins')}</PanelHeader>
-        <PanelBody>
-          {data.checkInList?.map(checkIn => (
-            <PanelItem key={checkIn.id}>
-              <CheckInIconWrapper>
-                <Tooltip
-                  title={tct('Check In Status: [status]', {
-                    status: checkIn.status,
-                  })}
-                >
-                  <CheckInIcon status={checkIn.status} size={16} />
-                </Tooltip>
-              </CheckInIconWrapper>
-              <TimeSinceWrapper>
-                <TimeSince date={checkIn.dateCreated} />
-              </TimeSinceWrapper>
-              <DurationWrapper>
-                {defined(checkIn.duration) && (
-                  <Duration seconds={checkIn.duration / 1000} />
-                )}
-              </DurationWrapper>
-              <AttachmentWrapper>
-                {checkIn.attachmentId && (
-                  <Button
-                    size="xs"
-                    icon={<IconDownload size="xs" />}
-                    href={generateDownloadUrl(checkIn)}
-                  >
-                    Attachment
-                  </Button>
-                )}
-              </AttachmentWrapper>
-            </PanelItem>
-          ))}
-        </PanelBody>
-      </Panel>
+      <SectionHeading>{t('Recent Check-Ins')}</SectionHeading>
+      <PanelTable
+        headers={[
+          t('Status'),
+          t('Started'),
+          t('Duration'),
+          t('Attachment'),
+          t('Timestamp'),
+        ]}
+      >
+        {data.checkInList?.map(checkIn => (
+          <React.Fragment key={checkIn.id}>
+            <Status>
+              <StatusIndicator
+                status={checkStatusToIndicatorStatus[checkIn.status]}
+                tooltipTitle={tct('Check In Status: [status]', {
+                  status: statusToText[checkIn.status],
+                })}
+              />
+              <Text>{statusToText[checkIn.status]}</Text>
+            </Status>
+            {checkIn.status !== CheckInStatus.MISSED ? (
+              <DateTime date={checkIn.dateCreated} timeOnly />
+            ) : (
+              emptyCell
+            )}
+            {defined(checkIn.duration) ? (
+              <Duration seconds={checkIn.duration / 1000} />
+            ) : (
+              emptyCell
+            )}
+            {checkIn.attachmentId ? (
+              <Button
+                size="xs"
+                icon={<IconDownload size="xs" />}
+                href={generateDownloadUrl(checkIn)}
+              >
+                Attachment
+              </Button>
+            ) : (
+              emptyCell
+            )}
+            <Timestamp date={checkIn.dateCreated} />
+          </React.Fragment>
+        ))}
+      </PanelTable>
       <Pagination pageLinks={data.checkInListPageLinks} />
     </React.Fragment>
   );
@@ -97,27 +121,15 @@ function MonitorCheckIns({monitor, monitorEnv, orgId}: Props) {
 
 export default MonitorCheckIns;
 
-const DivMargin = styled('div')`
-  margin-right: ${space(2)};
-`;
-
-const CheckInIconWrapper = styled(DivMargin)`
+const Status = styled('div')`
   display: flex;
   align-items: center;
 `;
 
-const TimeSinceWrapper = styled(DivMargin)`
-  font-variant-numeric: tabular-nums;
-`;
-
-const DurationWrapper = styled('div')`
-  font-variant-numeric: tabular-nums;
+const Timestamp = styled(DateTime)`
+  color: ${p => p.theme.subText};
 `;
 
 const ErrorWrapper = styled('div')`
   margin: ${space(3)} ${space(3)} 0;
 `;
-
-const AttachmentWrapper = styled('div')`
-  margin-left: auto;
-`;

+ 8 - 0
static/app/views/monitors/types.tsx

@@ -96,3 +96,11 @@ export interface MonitorStat {
   timeout: number;
   ts: number;
 }
+
+export interface CheckIn {
+  dateCreated: string;
+  duration: number;
+  id: string;
+  status: CheckInStatus;
+  attachmentId?: number;
+}