123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143 |
- import {Fragment, useContext, useEffect, useRef} from 'react';
- import styled from '@emotion/styled';
- import FormContext from 'sentry/components/forms/formContext';
- import type {FieldValue} from 'sentry/components/forms/model';
- import Panel from 'sentry/components/panels/panel';
- import Placeholder from 'sentry/components/placeholder';
- import {t} from 'sentry/locale';
- import {useApiQuery} from 'sentry/utils/queryClient';
- import {useDimensions} from 'sentry/utils/useDimensions';
- import useOrganization from 'sentry/utils/useOrganization';
- import {ScheduleType} from 'sentry/views/monitors/types';
- import {CheckInPlaceholder} from './timeline/checkInPlaceholder';
- import {MockCheckInTimeline} from './timeline/checkInTimeline';
- import {GridLineLabels, GridLineOverlay} from './timeline/gridLines';
- import {getConfigFromTimeRange} from './timeline/utils/getConfigFromTimeRange';
- interface ScheduleConfig {
- cronSchedule?: FieldValue;
- intervalFrequency?: FieldValue;
- intervalUnit?: FieldValue;
- scheduleType?: FieldValue;
- timezone?: FieldValue;
- }
- const NUM_SAMPLE_TICKS = 9;
- function isValidConfig(schedule: ScheduleConfig) {
- const {scheduleType, cronSchedule, intervalFrequency, intervalUnit} = schedule;
- return !!(
- (scheduleType === ScheduleType.CRONTAB && cronSchedule) ||
- (scheduleType === ScheduleType.INTERVAL && intervalFrequency && intervalUnit)
- );
- }
- interface Props {
- schedule: ScheduleConfig;
- }
- export function MockTimelineVisualization({schedule}: Props) {
- const {scheduleType, cronSchedule, timezone, intervalFrequency, intervalUnit} =
- schedule;
- const organization = useOrganization();
- const {form} = useContext(FormContext);
- const query = {
- num_ticks: NUM_SAMPLE_TICKS,
- schedule_type: scheduleType,
- timezone,
- schedule:
- scheduleType === 'interval' ? [intervalFrequency, intervalUnit] : cronSchedule,
- };
- const elementRef = useRef<HTMLDivElement>(null);
- const {width: timelineWidth} = useDimensions<HTMLDivElement>({elementRef});
- const sampleDataQueryKey = [
- `/organizations/${organization.slug}/monitors-schedule-data/`,
- {query},
- ] as const;
- const {data, isLoading, isError, error} = useApiQuery<number[]>(sampleDataQueryKey, {
- staleTime: 0,
- enabled: isValidConfig(schedule),
- retry: false,
- });
- const errorMessage =
- isError || !isValidConfig(schedule)
- ? error?.responseJSON?.schedule?.[0] ?? t('Invalid Schedule')
- : null;
- useEffect(() => {
- if (!form) {
- return;
- }
- if (scheduleType === ScheduleType.INTERVAL) {
- form.setError('config.schedule.frequency', errorMessage);
- } else if (scheduleType === ScheduleType.CRONTAB) {
- form.setError('config.schedule', errorMessage);
- }
- }, [errorMessage, form, scheduleType]);
- const mockTimestamps = data?.map(ts => new Date(ts * 1000));
- const start = mockTimestamps?.[0];
- const end = mockTimestamps?.[mockTimestamps.length - 1];
- const timeWindowConfig =
- start && end ? getConfigFromTimeRange(start, end, timelineWidth) : undefined;
- return (
- <TimelineContainer>
- <TimelineWidthTracker ref={elementRef} />
- {isLoading || !start || !end || !timeWindowConfig || !mockTimestamps ? (
- <Fragment>
- <Placeholder height="50px" />
- {errorMessage ? null : <CheckInPlaceholder />}
- </Fragment>
- ) : (
- <Fragment>
- <AlignedGridLineLabels
- timeWindowConfig={timeWindowConfig}
- width={timelineWidth}
- />
- <AlignedGridLineOverlay
- showCursor={!isLoading}
- timeWindowConfig={timeWindowConfig}
- width={timelineWidth}
- />
- <MockCheckInTimeline
- width={timelineWidth}
- mockTimestamps={mockTimestamps.slice(1, mockTimestamps.length - 1)}
- timeWindowConfig={timeWindowConfig}
- />
- </Fragment>
- )}
- </TimelineContainer>
- );
- }
- const TimelineContainer = styled(Panel)`
- display: grid;
- grid-template-columns: 1fr;
- grid-template-rows: auto 60px;
- align-items: center;
- `;
- const AlignedGridLineLabels = styled(GridLineLabels)`
- grid-column: 0;
- border-bottom: 1px solid ${p => p.theme.border};
- `;
- const AlignedGridLineOverlay = styled(GridLineOverlay)`
- grid-column: 0;
- `;
- const TimelineWidthTracker = styled('div')`
- position: absolute;
- width: 100%;
- grid-row: 1;
- grid-column: 0;
- `;
|