import {Fragment} from 'react'; import {useTheme} from '@emotion/react'; import styled from '@emotion/styled'; import moment from 'moment-timezone'; import Duration from 'sentry/components/duration'; import GridEditable, {type GridColumnOrder} from 'sentry/components/gridEditable'; import LoadingError from 'sentry/components/loadingError'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import {Tooltip} from 'sentry/components/tooltip'; import {IconInfo} from 'sentry/icons'; import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {User} from 'sentry/types/user'; import {defined} from 'sentry/utils'; import {FIELD_FORMATTERS} from 'sentry/utils/discover/fieldRenderers'; import parseLinkHeader from 'sentry/utils/parseLinkHeader'; import {decodeScalar} from 'sentry/utils/queryString'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; import {useParams} from 'sentry/utils/useParams'; import {useUser} from 'sentry/utils/useUser'; import {EventListTable} from 'sentry/views/issueDetails/streamline/eventListTable'; import {useCronIssueAlertId} from 'sentry/views/issueDetails/streamline/issueCronCheckTimeline'; import {useGroup} from 'sentry/views/issueDetails/useGroup'; import {type CheckIn, CheckInStatus} from 'sentry/views/monitors/types'; import {statusToText, tickStyle} from 'sentry/views/monitors/utils'; import {scheduleAsText} from 'sentry/views/monitors/utils/scheduleAsText'; import {useMonitorCheckIns} from 'sentry/views/monitors/utils/useMonitorCheckIns'; export default function GroupCheckIns() { const organization = useOrganization(); const {groupId} = useParams<{groupId: string}>(); const location = useLocation(); const user = useUser(); const cronAlertId = useCronIssueAlertId({groupId}); const { data: group, isPending: isGroupPending, isError: isGroupError, refetch: refetchGroup, } = useGroup({groupId}); const canFetchMonitorCheckIns = Boolean(organization.slug) && Boolean(group?.project.slug) && Boolean(cronAlertId); const {cursor, ...locationQuery} = location.query; const { data: cronData = [], isPending: isDataPending, getResponseHeader, } = useMonitorCheckIns( { orgSlug: organization.slug, projectSlug: group?.project.slug ?? '', monitorIdOrSlug: cronAlertId ?? '', limit: 50, cursor: decodeScalar(cursor), queryParams: locationQuery, }, {enabled: canFetchMonitorCheckIns} ); if (isGroupError) { return ; } if (isGroupPending) { return ; } const links = parseLinkHeader(getResponseHeader?.('Link') ?? ''); const previousDisabled = links?.previous?.results === false; const nextDisabled = links?.next?.results === false; const pageCount = cronData.length; return ( , renderBodyCell: (column, dataRow) => ( ), }} /> ); } function CheckInHeader({column}: {column: GridColumnOrder}) { if (column.key === 'monitorConfig') { return ( {t('Monitor Config')} ); } return {column.name}; } function CheckInCell({ dataRow, column, userOptions, }: { column: GridColumnOrder; dataRow: CheckIn; userOptions: User['options']; }) { const theme = useTheme(); const columnKey = column.key as keyof CheckIn; if (!dataRow[columnKey]) { return ; } switch (columnKey) { case 'dateCreated': { const format = userOptions.clock24Hours ? 'MMM D, YYYY HH:mm:ss z' : 'MMM D, YYYY h:mm:ss A z'; return ( {dataRow.expectedTime && (
{t('Expected at')}
{moment .tz(dataRow.expectedTime, userOptions?.timezone ?? '') .format(format)}
)}
{t('Received at')}
{moment .tz(dataRow[columnKey], userOptions?.timezone ?? '') .format(format)}
} > {FIELD_FORMATTERS.date.renderFunc('dateCreated', dataRow)}
); } case 'duration': { const cellData = dataRow[columnKey]; if (typeof cellData === 'number') { return ( ); } return {cellData}; } case 'status': { const status = dataRow[columnKey]; let checkResult = {status}; if (Object.values(CheckInStatus).includes(status)) { const colorKey = tickStyle[status].labelColor ?? 'textColor'; checkResult = ( {statusToText[status]} ); } return checkResult; } case 'monitorConfig': { const config = dataRow[columnKey]; return (
{t('Schedule')}
{scheduleAsText(config)}
{defined(config.schedule_type) && (
{t('Schedule Type')}
{config.schedule_type}
)} {defined(config.checkin_margin) && (
{t('Check-in Margin')}
{config.checkin_margin}
)} {defined(config.max_runtime) && (
{t('Max Runtime')}
{config.max_runtime}
)} {defined(config.timezone) && (
{t('Timezone')}
{config.timezone}
)} {defined(config.failure_issue_threshold) && (
{t('Failure Threshold')}
{config.failure_issue_threshold}
)} {defined(config.recovery_threshold) && (
{t('Recovery Threshold')}
{config.recovery_threshold}
)} } > {t('View Config')}
); } // We don't query groups for this table yet case 'groups': return ; default: return {dataRow[columnKey]}; } } const Cell = styled('div')` display: flex; align-items: center; text-align: left; gap: ${space(1)}; `; const HoverableCell = styled(Cell)` color: ${p => p.theme.subText}; text-decoration: underline; text-decoration-style: dotted; `; const LabelledTooltip = styled('div')` display: grid; grid-template-columns: max-content 1fr; gap: ${space(0.5)} ${space(1)}; text-align: left; margin: 0; `;