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