mirror of
https://github.com/hanzoai/sentinel.git
synced 2026-10-30 14:49:14 +00:00
163 lines
5.4 KiB
TypeScript
163 lines
5.4 KiB
TypeScript
import {Fragment} from 'react';
|
|
import type {RouteComponentProps} from 'react-router';
|
|
import styled from '@emotion/styled';
|
|
import sortBy from 'lodash/sortBy';
|
|
|
|
import {updateMonitor} from 'sentry/actionCreators/monitors';
|
|
import Alert from 'sentry/components/alert';
|
|
import * as Layout from 'sentry/components/layouts/thirds';
|
|
import LoadingIndicator from 'sentry/components/loadingIndicator';
|
|
import {DatePageFilter} from 'sentry/components/organizations/datePageFilter';
|
|
import {EnvironmentPageFilter} from 'sentry/components/organizations/environmentPageFilter';
|
|
import PageFilterBar from 'sentry/components/organizations/pageFilterBar';
|
|
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
|
|
import {t} from 'sentry/locale';
|
|
import {space} from 'sentry/styles/space';
|
|
import {setApiQueryData, useApiQuery, useQueryClient} from 'sentry/utils/queryClient';
|
|
import useApi from 'sentry/utils/useApi';
|
|
import useOrganization from 'sentry/utils/useOrganization';
|
|
import {CronDetailsTimeline} from 'sentry/views/monitors/components/cronDetailsTimeline';
|
|
import DetailsSidebar from 'sentry/views/monitors/components/detailsSidebar';
|
|
import {makeMonitorDetailsQueryKey} from 'sentry/views/monitors/utils';
|
|
|
|
import MonitorCheckIns from './components/monitorCheckIns';
|
|
import MonitorHeader from './components/monitorHeader';
|
|
import MonitorIssues from './components/monitorIssues';
|
|
import MonitorStats from './components/monitorStats';
|
|
import MonitorOnboarding from './components/onboarding';
|
|
import {StatusToggleButton} from './components/statusToggleButton';
|
|
import type {Monitor} from './types';
|
|
|
|
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
|
|
type Props = RouteComponentProps<{monitorSlug: string}, {}>;
|
|
|
|
function hasLastCheckIn(monitor: Monitor) {
|
|
return monitor.environments.some(e => e.lastCheckIn);
|
|
}
|
|
|
|
function MonitorDetails({params, location}: Props) {
|
|
const api = useApi();
|
|
|
|
const organization = useOrganization();
|
|
const queryClient = useQueryClient();
|
|
|
|
const queryKey = makeMonitorDetailsQueryKey(organization, params.monitorSlug, {
|
|
...location.query,
|
|
});
|
|
|
|
const {data: monitor} = useApiQuery<Monitor>(queryKey, {
|
|
staleTime: 0,
|
|
refetchOnWindowFocus: true,
|
|
// Refetches while we are waiting for the user to send their first check-in
|
|
refetchInterval: data => {
|
|
if (!data) {
|
|
return false;
|
|
}
|
|
const [monitorData] = data;
|
|
return hasLastCheckIn(monitorData) ? false : DEFAULT_POLL_INTERVAL_MS;
|
|
},
|
|
});
|
|
|
|
function onUpdate(data: Monitor) {
|
|
const updatedMonitor = {
|
|
...data,
|
|
// TODO(davidenwang): This is a bit of a hack, due to the PUT request
|
|
// which pauses/unpauses a monitor not returning monitor environments
|
|
// we should reuse the environments retrieved from the initial request
|
|
environments: monitor?.environments,
|
|
};
|
|
setApiQueryData(queryClient, queryKey, updatedMonitor);
|
|
}
|
|
|
|
const handleUpdate = async (data: Partial<Monitor>) => {
|
|
if (monitor === undefined) {
|
|
return;
|
|
}
|
|
const resp = await updateMonitor(api, organization.slug, monitor.slug, data);
|
|
|
|
if (resp !== null) {
|
|
onUpdate(resp);
|
|
}
|
|
};
|
|
|
|
if (!monitor) {
|
|
return (
|
|
<Layout.Page>
|
|
<LoadingIndicator />
|
|
</Layout.Page>
|
|
);
|
|
}
|
|
|
|
const envsSortedByLastCheck = sortBy(monitor.environments, e => e.lastCheckIn);
|
|
|
|
return (
|
|
<SentryDocumentTitle title={`Crons — ${monitor.name}`}>
|
|
<Layout.Page>
|
|
<MonitorHeader monitor={monitor} orgId={organization.slug} onUpdate={onUpdate} />
|
|
<Layout.Body>
|
|
<Layout.Main>
|
|
<StyledPageFilterBar condensed>
|
|
<DatePageFilter />
|
|
<EnvironmentPageFilter />
|
|
</StyledPageFilterBar>
|
|
{monitor.status === 'disabled' && (
|
|
<Alert
|
|
type="muted"
|
|
showIcon
|
|
trailingItems={
|
|
<StatusToggleButton
|
|
monitor={monitor}
|
|
size="xs"
|
|
onToggleStatus={status => handleUpdate({status})}
|
|
>
|
|
{t('Enable')}
|
|
</StatusToggleButton>
|
|
}
|
|
>
|
|
{t('This monitor is disabled and is not accepting check-ins.')}
|
|
</Alert>
|
|
)}
|
|
{!hasLastCheckIn(monitor) ? (
|
|
<MonitorOnboarding monitor={monitor} />
|
|
) : (
|
|
<Fragment>
|
|
<CronDetailsTimeline organization={organization} monitor={monitor} />
|
|
<MonitorStats
|
|
orgSlug={organization.slug}
|
|
monitor={monitor}
|
|
monitorEnvs={monitor.environments}
|
|
/>
|
|
|
|
<MonitorIssues
|
|
orgSlug={organization.slug}
|
|
monitor={monitor}
|
|
monitorEnvs={monitor.environments}
|
|
/>
|
|
|
|
<MonitorCheckIns
|
|
orgSlug={organization.slug}
|
|
monitor={monitor}
|
|
monitorEnvs={monitor.environments}
|
|
/>
|
|
</Fragment>
|
|
)}
|
|
</Layout.Main>
|
|
<Layout.Side>
|
|
<DetailsSidebar
|
|
monitorEnv={envsSortedByLastCheck[envsSortedByLastCheck.length - 1]}
|
|
monitor={monitor}
|
|
/>
|
|
</Layout.Side>
|
|
</Layout.Body>
|
|
</Layout.Page>
|
|
</SentryDocumentTitle>
|
|
);
|
|
}
|
|
|
|
const StyledPageFilterBar = styled(PageFilterBar)`
|
|
margin-bottom: ${space(2)};
|
|
`;
|
|
|
|
export default MonitorDetails;
|