import {Fragment} from 'react'; import styled from '@emotion/styled'; import {LinkButton} from 'sentry/components/button'; import {SectionHeading} from 'sentry/components/charts/styles'; import {DateTime} from 'sentry/components/dateTime'; import Duration from 'sentry/components/duration'; import ProjectBadge from 'sentry/components/idBadge/projectBadge'; import LoadingError from 'sentry/components/loadingError'; import Pagination from 'sentry/components/pagination'; import {PanelTable} from 'sentry/components/panels/panelTable'; import Placeholder from 'sentry/components/placeholder'; import ShortId from 'sentry/components/shortId'; import { StatusIndicator, type StatusIndicatorProps, } from 'sentry/components/statusIndicator'; import Text from 'sentry/components/text'; import {Tooltip} from 'sentry/components/tooltip'; import {IconDownload} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {defined} from 'sentry/utils'; import {useApiQuery} from 'sentry/utils/queryClient'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import {useUser} from 'sentry/utils/useUser'; import {QuickContextHovercard} from 'sentry/views/discover/table/quickContext/quickContextHovercard'; import {ContextType} from 'sentry/views/discover/table/quickContext/utils'; import type {CheckIn, Monitor, MonitorEnvironment} from 'sentry/views/monitors/types'; import {CheckInStatus} from 'sentry/views/monitors/types'; import {statusToText} from 'sentry/views/monitors/utils'; type Props = { monitor: Monitor; monitorEnvs: MonitorEnvironment[]; orgSlug: string; }; export const checkStatusToIndicatorStatus: Record< CheckInStatus, StatusIndicatorProps['status'] > = { [CheckInStatus.OK]: 'success', [CheckInStatus.ERROR]: 'error', [CheckInStatus.IN_PROGRESS]: 'muted', [CheckInStatus.MISSED]: 'warning', [CheckInStatus.TIMEOUT]: 'error', }; function MonitorCheckIns({monitor, monitorEnvs, orgSlug}: Props) { const user = useUser(); const location = useLocation(); const organization = useOrganization(); const queryKey = [ `/projects/${orgSlug}/${monitor.project.slug}/monitors/${monitor.slug}/checkins/`, { query: { per_page: '10', environment: monitorEnvs.map(e => e.name), expand: 'groups', ...location.query, }, }, ] as const; const { data: checkInList, getResponseHeader, isPending, isError, } = useApiQuery(queryKey, {staleTime: 0}); if (isError) { return ; } const generateDownloadUrl = (checkin: CheckIn) => `/api/0/organizations/${orgSlug}/monitors/${monitor.slug}/checkins/${checkin.id}/attachment/`; const emptyCell = {'\u2014'}; // XXX(epurkhiser): Attachmnets are still experimental and may not exist in // the future. For now hide these if they're not being used. const hasAttachments = checkInList?.some(checkin => checkin.attachmentId !== null); const hasMultiEnv = monitorEnvs.length > 1; const headers = [ t('Status'), t('Started'), t('Duration'), t('Issues'), ...(hasAttachments ? [t('Attachment')] : []), ...(hasMultiEnv ? [t('Environment')] : []), t('Expected At'), ]; const customTimezone = monitor.config.timezone && monitor.config.timezone !== user.options.timezone; return ( {t('Recent Check-Ins')} {isPending ? [...new Array(headers.length)].map((_, i) => ( )) : checkInList.map(checkIn => ( {statusToText[checkIn.status]} {checkIn.status !== CheckInStatus.MISSED ? (
} >
) : ( emptyCell )} {defined(checkIn.duration) ? (
}>
) : ( emptyCell )} {checkIn.groups && checkIn.groups.length > 0 ? ( {checkIn.groups.map(({id, shortId}) => ( } to={`/issues/${id}`} /> ))} ) : ( emptyCell )} {!hasAttachments ? null : checkIn.attachmentId ? (
} href={generateDownloadUrl(checkIn)} > {t('Attachment')}
) : ( emptyCell )} {!hasMultiEnv ? null :
{checkIn.environment}
}
{checkIn.expectedTime ? ( } > ) : ( emptyCell )}
))}
); } export default MonitorCheckIns; const Status = styled('div')` line-height: 1.1; `; const IssuesContainer = styled('div')` display: flex; flex-direction: column; `; const Timestamp = styled(DateTime)` color: ${p => p.theme.subText}; `; const StyledShortId = styled(ShortId)` justify-content: flex-start; `; const RowPlaceholder = styled('div')` grid-column: 1 / -1; padding: ${space(1)}; &:not(:last-child) { border-bottom: solid 1px ${p => p.theme.innerBorder}; } `;