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