Files
2024-02-15 17:01:50 +00:00

151 lines
4.6 KiB
TypeScript

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 {MockCheckInTimeline} from 'sentry/views/monitors/components/overviewTimeline/checkInTimeline';
import {
GridLineOverlay,
GridLineTimeLabels,
} from 'sentry/views/monitors/components/overviewTimeline/gridLines';
import {TimelinePlaceholder} from 'sentry/views/monitors/components/overviewTimeline/timelinePlaceholder';
import {getConfigFromTimeRange} from 'sentry/views/monitors/components/overviewTimeline/utils';
import {ScheduleType} from 'sentry/views/monitors/types';
interface ScheduleConfig {
cronSchedule?: FieldValue;
intervalFrequency?: FieldValue;
intervalUnit?: FieldValue;
scheduleType?: 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, intervalFrequency, intervalUnit} = schedule;
const organization = useOrganization();
const {form} = useContext(FormContext);
const query = {
num_ticks: NUM_SAMPLE_TICKS,
schedule_type: scheduleType,
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="40px" />
{errorMessage ? (
<Placeholder testId="error-placeholder" height="100px" />
) : (
<TimelinePlaceholder />
)}
</Fragment>
) : (
<Fragment>
<StyledGridLineTimeLabels
timeWindowConfig={timeWindowConfig}
start={start}
end={end}
width={timelineWidth}
/>
<StyledGridLineOverlay
showCursor={!isLoading}
timeWindowConfig={timeWindowConfig}
start={start}
end={end}
width={timelineWidth}
/>
<MockCheckInTimeline
width={timelineWidth}
mockTimestamps={mockTimestamps.slice(1, mockTimestamps.length - 1)}
start={start}
end={end}
timeWindowConfig={timeWindowConfig}
/>
</Fragment>
)}
</TimelineContainer>
);
}
const TimelineContainer = styled(Panel)`
display: grid;
grid-template-columns: 1fr;
grid-template-rows: 40px 100px;
align-items: center;
`;
const StyledGridLineTimeLabels = styled(GridLineTimeLabels)`
grid-column: 0;
`;
const StyledGridLineOverlay = styled(GridLineOverlay)`
grid-column: 0;
`;
const TimelineWidthTracker = styled('div')`
position: absolute;
width: 100%;
grid-row: 1;
grid-column: 0;
`;