Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 24 additions & 14 deletions static/app/views/detectors/components/detectorTypeForm.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {useTheme} from '@emotion/react';
import styled from '@emotion/styled';
import {parseAsStringEnum, useQueryState} from 'nuqs';

import {Flex, Stack} from 'sentry/components/core/layout';
import {ExternalLink, Link} from 'sentry/components/core/link';
Expand All @@ -9,8 +10,6 @@ import Hook from 'sentry/components/hook';
import {t, tct} from 'sentry/locale';
import HookStore from 'sentry/stores/hookStore';
import type {DetectorType} from 'sentry/types/workflowEngine/detectors';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
import useOrganization from 'sentry/utils/useOrganization';
import {
makeAutomationBasePathname,
Expand Down Expand Up @@ -44,33 +43,44 @@ export function DetectorTypeForm() {
);
}

type SelectableDetectorType = Extract<
DetectorType,
'metric_issue' | 'monitor_check_in_failure' | 'uptime_domain_failure'
>;

const ALLOWED_DETECTOR_TYPES = [
'metric_issue',
'monitor_check_in_failure',
'uptime_domain_failure',
] as const satisfies SelectableDetectorType[];

const detectorTypeParser = parseAsStringEnum(ALLOWED_DETECTOR_TYPES)
.withOptions({history: 'replace', clearOnDefault: false})
.withDefault(ALLOWED_DETECTOR_TYPES[0]);

export function useDetectorTypeQueryState() {
return useQueryState('detectorType', detectorTypeParser);
}

interface DetectorTypeOption {
description: string;
id: DetectorType;
id: SelectableDetectorType;
name: string;
visualization: React.ReactNode;
disabled?: boolean;
infoBanner?: React.ReactNode;
}

function MonitorTypeField() {
const location = useLocation();
const navigate = useNavigate();
const selectedDetectorType = location.query.detectorType as DetectorType;
const [selectedDetectorType, setDetectorType] = useDetectorTypeQueryState();

const useMetricDetectorLimit =
HookStore.get('react-hook:use-metric-detector-limit')[0] ?? (() => null);
const quota = useMetricDetectorLimit();
const canCreateMetricDetector = !quota?.hasReachedLimit;

const handleChange = (value: DetectorType) => {
navigate({
pathname: location.pathname,
query: {
...location.query,
detectorType: value,
},
});
const handleChange = (value: SelectableDetectorType) => {
setDetectorType(value);
};

const options: DetectorTypeOption[] = [
Expand Down
15 changes: 8 additions & 7 deletions static/app/views/detectors/new-settings.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import {parseAsString, useQueryState} from 'nuqs';

import * as Layout from 'sentry/components/layouts/thirds';
import LoadingError from 'sentry/components/loadingError';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import {useWorkflowEngineFeatureGate} from 'sentry/components/workflowEngine/useWorkflowEngineFeatureGate';
import {t} from 'sentry/locale';
import type {DetectorType} from 'sentry/types/workflowEngine/detectors';
import {useLocation} from 'sentry/utils/useLocation';
import useProjects from 'sentry/utils/useProjects';
import {useDetectorTypeQueryState} from 'sentry/views/detectors/components/detectorTypeForm';
import {NewDetectorForm} from 'sentry/views/detectors/components/forms';
import {DetectorFormProvider} from 'sentry/views/detectors/components/forms/context';
import {
Expand All @@ -15,13 +16,13 @@ import {
} from 'sentry/views/detectors/utils/detectorTypeConfig';

export default function DetectorNewSettings() {
const location = useLocation();
const {projects, fetching: isFetchingProjects} = useProjects();
const detectorType = location.query.detectorType as DetectorType;
const [detectorType] = useDetectorTypeQueryState();
const [projectId] = useQueryState('project', parseAsString);
useWorkflowEngineFeatureGate({redirect: true});

if (!isValidDetectorType(detectorType)) {
return <LoadingError message={t('Invalid detector type: %s', detectorType)} />;
if (!detectorType || !isValidDetectorType(detectorType)) {
return <LoadingError message={t('Invalid detector type: %s', detectorType ?? '')} />;
}

if (isFetchingProjects) {
Expand All @@ -36,7 +37,7 @@ export default function DetectorNewSettings() {
);
}

const project = projects.find(p => p.id === (location.query.project as string));
const project = projectId ? projects.find(p => p.id === projectId) : undefined;
if (!project) {
return <LoadingError message={t('Project not found')} />;
}
Expand Down
3 changes: 0 additions & 3 deletions static/app/views/detectors/new.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,6 @@ describe('DetectorNew', () => {
it('sets query parameters for project, environment, and detectorType', async () => {
const {router} = render(<DetectorNew />);

// Next button should be disabled if no detectorType is selected
expect(screen.getByRole('button', {name: 'Next'})).toBeDisabled();

// Set detectorType
await userEvent.click(screen.getByRole('radio', {name: 'Uptime'}));

Expand Down
29 changes: 11 additions & 18 deletions static/app/views/detectors/new.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import {useTheme} from '@emotion/react';
import {parseAsString, useQueryState} from 'nuqs';

import {Breadcrumbs} from 'sentry/components/breadcrumbs';
import {Button} from 'sentry/components/core/button';
import {LinkButton} from 'sentry/components/core/button/linkButton';
import {Tooltip} from 'sentry/components/core/tooltip';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
import EditLayout from 'sentry/components/workflowEngine/layout/edit';
import {useWorkflowEngineFeatureGate} from 'sentry/components/workflowEngine/useWorkflowEngineFeatureGate';
import {t} from 'sentry/locale';
import type {DetectorType} from 'sentry/types/workflowEngine/detectors';
import {useLocation} from 'sentry/utils/useLocation';
import {useNavigate} from 'sentry/utils/useNavigate';
import useOrganization from 'sentry/utils/useOrganization';
import useProjects from 'sentry/utils/useProjects';
import {DetectorTypeForm} from 'sentry/views/detectors/components/detectorTypeForm';
import {
DetectorTypeForm,
useDetectorTypeQueryState,
} from 'sentry/views/detectors/components/detectorTypeForm';
import {MonitorFeedbackButton} from 'sentry/views/detectors/components/monitorFeedbackButton';
import {makeMonitorBasePathname} from 'sentry/views/detectors/pathnames';

Expand Down Expand Up @@ -43,14 +45,11 @@ export default function DetectorNew() {
const navigate = useNavigate();
const organization = useOrganization();
useWorkflowEngineFeatureGate({redirect: true});
const location = useLocation();
const {projects} = useProjects();
const theme = useTheme();
const maxWidth = theme.breakpoints.xl;
const detectorType = location.query.detectorType as DetectorType;

const projectIdFromLocation =
typeof location.query.project === 'string' ? location.query.project : undefined;
const [detectorType] = useDetectorTypeQueryState();
const [projectIdFromLocation] = useQueryState('project', parseAsString);
const defaultProject = projects.find(p => p.isMember) ?? projects[0];

const newMonitorName = t('New Monitor');
Expand All @@ -62,7 +61,7 @@ export default function DetectorNew() {
navigate({
pathname: `${makeMonitorBasePathname(organization.slug)}new/settings/`,
query: {
detectorType: location.query.detectorType as DetectorType,
detectorType,
project: data.project,
},
});
Expand Down Expand Up @@ -95,15 +94,9 @@ export default function DetectorNew() {
<LinkButton priority="default" to={makeMonitorBasePathname(organization.slug)}>
{t('Cancel')}
</LinkButton>
<Tooltip
title={t('Select a monitor type to continue')}
disabled={!!detectorType}
skipWrapper
>
<Button priority="primary" type="submit" disabled={!detectorType}>
{t('Next')}
</Button>
</Tooltip>
<Button priority="primary" type="submit">
{t('Next')}
</Button>
</EditLayout.Footer>
</EditLayout>
);
Expand Down
Loading