From 6bae8366c01ee5e47f80c07c1fada679a61f2fbf Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Fri, 21 Nov 2025 10:38:18 -0800 Subject: [PATCH 1/3] fix(aci): prevent deletionof system-created monitors in UI --- .../components/detectorListTable/actions.tsx | 6 ++++-- .../components/detectorListTable/index.tsx | 7 ++++++- .../views/detectors/list/allMonitors.spec.tsx | 21 +++++++++++++++++++ .../detectors/utils/useCanEditDetector.tsx | 6 ++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/static/app/views/detectors/components/detectorListTable/actions.tsx b/static/app/views/detectors/components/detectorListTable/actions.tsx index c76c3f06c3fe86..52f5dc53f937e5 100644 --- a/static/app/views/detectors/components/detectorListTable/actions.tsx +++ b/static/app/views/detectors/components/detectorListTable/actions.tsx @@ -17,6 +17,7 @@ import {useUpdateDetectorsMutation} from 'sentry/views/detectors/hooks/useEditDe interface DetectorsTableActionsProps { allResultsVisible: boolean; + canDelete: boolean; canEdit: boolean; detectorLimitReached: boolean; pageSelected: boolean; @@ -36,6 +37,7 @@ export function DetectorsTableActions({ showEnable, showDisable, canEdit, + canDelete, detectorLimitReached, }: DetectorsTableActionsProps) { const [allInQuerySelected, setAllInQuerySelected] = useState(false); @@ -200,13 +202,13 @@ export function DetectorsTableActions({ )} diff --git a/static/app/views/detectors/components/detectorListTable/index.tsx b/static/app/views/detectors/components/detectorListTable/index.tsx index a2176c15e2128c..f00eac64a07234 100644 --- a/static/app/views/detectors/components/detectorListTable/index.tsx +++ b/static/app/views/detectors/components/detectorListTable/index.tsx @@ -36,7 +36,10 @@ import { useMonitorViewContext, type MonitorListAdditionalColumn, } from 'sentry/views/detectors/monitorViewContext'; -import {useCanEditDetectors} from 'sentry/views/detectors/utils/useCanEditDetector'; +import { + useCanDeleteDetectors, + useCanEditDetectors, +} from 'sentry/views/detectors/utils/useCanEditDetector'; import {CronServiceIncidents} from 'sentry/views/insights/crons/components/serviceIncidents'; type DetectorListTableProps = { @@ -139,6 +142,7 @@ function DetectorListTable({ const selectedDetectors = detectors.filter(d => selected.has(d.id)); const canEditDetectors = useCanEditDetectors({detectors: selectedDetectors}); + const canDeleteDetectors = useCanDeleteDetectors({detectors: selectedDetectors}); const elementRef = useRef(null); const {width: containerWidth} = useDimensions({elementRef}); @@ -212,6 +216,7 @@ function DetectorListTable({ showDisable={canDisable} showEnable={canEnable} canEdit={canEditDetectors} + canDelete={canDeleteDetectors} // TODO: Check if metric detector limit is reached detectorLimitReached={false} /> diff --git a/static/app/views/detectors/list/allMonitors.spec.tsx b/static/app/views/detectors/list/allMonitors.spec.tsx index 4192369a965a22..131ee43509ceff 100644 --- a/static/app/views/detectors/list/allMonitors.spec.tsx +++ b/static/app/views/detectors/list/allMonitors.spec.tsx @@ -461,6 +461,27 @@ describe('DetectorsList', () => { }); }); + it('can not delete system-created detectors', async () => { + MockApiClient.addMockResponse({ + url: '/organizations/org-slug/detectors/', + body: [ + ErrorDetectorFixture({ + name: 'System Created Detector', + }), + ], + }); + render(, {organization}); + renderGlobalModal(); + await screen.findByText('System Created Detector'); + + const rows = screen.getAllByTestId('detector-list-row'); + const firstRowCheckbox = within(rows[0]!).getByRole('checkbox'); + await userEvent.click(firstRowCheckbox); + + // Click Delete button + expect(screen.getByRole('button', {name: 'Delete'})).toBeDisabled(); + }); + it('shows option to select all query results when page is selected', async () => { const deleteRequest = MockApiClient.addMockResponse({ url: '/organizations/org-slug/detectors/', diff --git a/static/app/views/detectors/utils/useCanEditDetector.tsx b/static/app/views/detectors/utils/useCanEditDetector.tsx index 418c2efb9cd87c..d3ba3422d35100 100644 --- a/static/app/views/detectors/utils/useCanEditDetector.tsx +++ b/static/app/views/detectors/utils/useCanEditDetector.tsx @@ -73,3 +73,9 @@ export function useCanEditDetectors({detectors}: {detectors: Detector[]}) { return true; } + +export function useCanDeleteDetectors({detectors}: {detectors: Detector[]}) { + const canEdit = useCanEditDetectors({detectors}); + + return detectors.every(d => detectorTypeIsUserCreateable(d.type)) && canEdit; +} From 28149bb28459880874f2e2bcc4e024e440081633 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Fri, 21 Nov 2025 14:28:18 -0800 Subject: [PATCH 2/3] clean up test --- static/app/views/detectors/list/allMonitors.spec.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/static/app/views/detectors/list/allMonitors.spec.tsx b/static/app/views/detectors/list/allMonitors.spec.tsx index 131ee43509ceff..a27f68b47f3766 100644 --- a/static/app/views/detectors/list/allMonitors.spec.tsx +++ b/static/app/views/detectors/list/allMonitors.spec.tsx @@ -471,14 +471,13 @@ describe('DetectorsList', () => { ], }); render(, {organization}); - renderGlobalModal(); await screen.findByText('System Created Detector'); const rows = screen.getAllByTestId('detector-list-row'); const firstRowCheckbox = within(rows[0]!).getByRole('checkbox'); await userEvent.click(firstRowCheckbox); - // Click Delete button + // Verify that delete button is disabled expect(screen.getByRole('button', {name: 'Delete'})).toBeDisabled(); }); From 7ca1dfcc6eb9eeb5aca757e309092f2677bd9a66 Mon Sep 17 00:00:00 2001 From: Mia Hsu Date: Fri, 21 Nov 2025 14:40:03 -0800 Subject: [PATCH 3/3] change logic to have better tooltip message --- .../components/detectorListTable/actions.tsx | 12 +++++++++--- .../detectors/components/detectorListTable/index.tsx | 12 ++++++------ .../app/views/detectors/utils/useCanEditDetector.tsx | 6 ------ 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/static/app/views/detectors/components/detectorListTable/actions.tsx b/static/app/views/detectors/components/detectorListTable/actions.tsx index 52f5dc53f937e5..6e5422a33ef00f 100644 --- a/static/app/views/detectors/components/detectorListTable/actions.tsx +++ b/static/app/views/detectors/components/detectorListTable/actions.tsx @@ -17,9 +17,9 @@ import {useUpdateDetectorsMutation} from 'sentry/views/detectors/hooks/useEditDe interface DetectorsTableActionsProps { allResultsVisible: boolean; - canDelete: boolean; canEdit: boolean; detectorLimitReached: boolean; + hasSystemCreatedDetectors: boolean; pageSelected: boolean; queryCount: string; selected: Set; @@ -37,12 +37,14 @@ export function DetectorsTableActions({ showEnable, showDisable, canEdit, - canDelete, + hasSystemCreatedDetectors, detectorLimitReached, }: DetectorsTableActionsProps) { const [allInQuerySelected, setAllInQuerySelected] = useState(false); const anySelected = selected.size > 0; + const canDelete = canEdit && !hasSystemCreatedDetectors; + const {selection} = usePageFilters(); const {query} = useLocationQuery({ fields: { @@ -201,7 +203,11 @@ export function DetectorsTableActions({ )}