import {css} from '@emotion/react';
import styled from '@emotion/styled';
import DateTime from 'sentry/components/dateTime';
import {Tooltip} from 'sentry/components/tooltip';
import {CheckInStatus} from 'sentry/views/monitors/types';
import {tickStyle} from 'sentry/views/monitors/utils';
import {getAggregateStatus} from 'sentry/views/monitors/utils/getAggregateStatus';
import {mergeBuckets} from 'sentry/views/monitors/utils/mergeBuckets';
import {JobTickTooltip} from './jobTickTooltip';
import type {MonitorBucketData, TimeWindowConfig} from './types';
interface TimelineProps {
timeWindowConfig: TimeWindowConfig;
width: number;
}
export interface CheckInTimelineProps extends TimelineProps {
bucketedData: MonitorBucketData;
environment: string;
}
function getBucketedCheckInsPosition(
timestamp: number,
timelineStart: Date,
msPerPixel: number
) {
const elapsedSinceStart = new Date(timestamp).getTime() - timelineStart.getTime();
return elapsedSinceStart / msPerPixel;
}
export function CheckInTimeline(props: CheckInTimelineProps) {
const {bucketedData, timeWindowConfig, width, environment} = props;
const {start, end} = timeWindowConfig;
const elapsedMs = end.getTime() - start.getTime();
const msPerPixel = elapsedMs / width;
const jobTicks = mergeBuckets(bucketedData, environment);
return (
{jobTicks.map(jobTick => {
const {
startTs,
width: tickWidth,
envMapping,
roundedLeft,
roundedRight,
} = jobTick;
const timestampMs = startTs * 1000;
const left = getBucketedCheckInsPosition(timestampMs, start, msPerPixel);
return (
);
})}
);
}
export interface MockCheckInTimelineProps extends TimelineProps {
mockTimestamps: Date[];
}
export function MockCheckInTimeline({
mockTimestamps,
timeWindowConfig,
width,
}: MockCheckInTimelineProps) {
const {start, end} = timeWindowConfig;
const elapsedMs = end.getTime() - start.getTime();
const msPerPixel = elapsedMs / width;
return (
{mockTimestamps.map(ts => {
const timestampMs = ts.getTime();
const left = getBucketedCheckInsPosition(timestampMs, start, msPerPixel);
return (
}
skipWrapper
>
);
})}
);
}
const TimelineContainer = styled('div')`
position: relative;
height: 100%;
`;
const JobTick = styled('div')<{
roundedLeft: boolean;
roundedRight: boolean;
status: CheckInStatus;
}>`
position: absolute;
top: calc(50% + 1px);
width: 4px;
height: 14px;
transform: translateY(-50%);
opacity: 0.7;
${p => {
const style = tickStyle[p.status];
if (style.hatchTick === undefined) {
return css`
background: ${p.theme[style.tickColor]};
`;
}
return css`
border: 1px solid ${p.theme[style.tickColor]};
${!p.roundedLeft && 'border-left-width: 0'};
${!p.roundedRight && 'border-right-width: 0'};
background-size: 3px 3px;
opacity: 0.5;
background-image: linear-gradient(
-45deg,
${p.theme[style.hatchTick]} 25%,
transparent 25%,
transparent 50%,
${p.theme[style.hatchTick]} 50%,
${p.theme[style.hatchTick]} 75%,
transparent 75%,
transparent
),
linear-gradient(
45deg,
${p.theme[style.hatchTick]} 25%,
transparent 25%,
transparent 50%,
${p.theme[style.hatchTick]} 50%,
${p.theme[style.hatchTick]} 75%,
transparent 75%,
transparent
);
`;
}};
${p =>
p.roundedLeft &&
`
border-top-left-radius: 2px;
border-bottom-left-radius: 2px;
`};
${p =>
p.roundedRight &&
`
border-top-right-radius: 2px;
border-bottom-right-radius: 2px;
`}
`;