From d3262815c65d0d5b970ba189651ac152953d6fc6 Mon Sep 17 00:00:00 2001 From: Colleen O'Rourke Date: Thu, 13 Aug 2026 09:53:17 -0700 Subject: [PATCH 1/3] rm legacy rule creation from project onboarding --- .../modals/projectCreationModal.tsx | 19 --- .../onboarding/scm/useScmProjectDetails.ts | 13 +- .../onboarding/useCreateProjectAndRules.ts | 90 +------------- .../onboarding/useCreateProjectRules.ts | 34 ------ static/app/types/alerts.tsx | 22 ---- .../utils/analytics/issueAnalyticsEvents.tsx | 1 - .../analytics/workflowAnalyticsEvents.tsx | 2 - .../projectInstall/createProject.spec.tsx | 111 ------------------ .../views/projectInstall/createProject.tsx | 30 ++--- .../issueAlertNotificationOptions.tsx | 77 +----------- 10 files changed, 13 insertions(+), 386 deletions(-) delete mode 100644 static/app/components/onboarding/useCreateProjectRules.ts diff --git a/static/app/components/modals/projectCreationModal.tsx b/static/app/components/modals/projectCreationModal.tsx index 36cff7f3dfaf..d89b6c1aeb92 100644 --- a/static/app/components/modals/projectCreationModal.tsx +++ b/static/app/components/modals/projectCreationModal.tsx @@ -119,24 +119,6 @@ export default function ProjectCreationModal({ }, }); - let ruleId: string | undefined; - if (alertRuleConfig.shouldCreateCustomRule) { - const ruleData = await api.requestPromise( - `/projects/${organization.slug}/${projectData.slug}/rules/`, - { - method: 'POST', - data: { - name: alertRuleConfig.name, - conditions: alertRuleConfig.conditions, - actions: alertRuleConfig.actions, - actionMatch: alertRuleConfig.actionMatch, - frequency: alertRuleConfig.frequency, - }, - } - ); - ruleId = ruleData.id; - } - ProjectsStore.onCreateSuccess(projectData, organization.slug); clearIndicators(); trackAnalytics('project_modal.created', { @@ -147,7 +129,6 @@ export default function ProjectCreationModal({ ? 'Custom' : 'No Rule', project_id: projectData.id, - rule_id: ruleId || '', }); addSuccessMessage(`Created project ${projectData.slug}`); diff --git a/static/app/components/onboarding/scm/useScmProjectDetails.ts b/static/app/components/onboarding/scm/useScmProjectDetails.ts index 51044edcab06..b0c02ef40d84 100644 --- a/static/app/components/onboarding/scm/useScmProjectDetails.ts +++ b/static/app/components/onboarding/scm/useScmProjectDetails.ts @@ -149,9 +149,8 @@ export function useScmProjectDetails({ ); // Provides the messaging-integration notification picker (notificationProps, - // rendered in ScmAlertFrequencySection) and the side-effect that creates the - // chosen notification rule at project creation. - const {createNotificationAction, notificationProps} = useScmNotificationAction( + // rendered in ScmAlertFrequencySection). + const {notificationProps} = useScmNotificationAction( restoredNotificationSelectionRef.current ); @@ -377,8 +376,6 @@ export function useScmProjectDetails({ project_id: existingProject.id, platform: selectedPlatform.key, issue_alert: issueAlert, - notification_rule_created: false, - rule_ids: [], variant: 'scm', }); onComplete({project: existingProject, projectDetailsForm: submittedForm}); @@ -391,7 +388,6 @@ export function useScmProjectDetails({ platform: selectedPlatform, team: isOrgMemberWithNoAccess ? undefined : teamSlugResolved, alertRuleConfig: getRequestDataFragment(alertRuleConfig), - createNotificationAction, }) .catch(error => { trackAnalytics('project_creation.project_details_create_failed', { @@ -411,7 +407,7 @@ export function useScmProjectDetails({ if (!creation) { return; } - const {project, ruleIds, notificationRule} = creation; + const {project} = creation; if (selectedRepository?.id) { await linkProjectToRepository({ @@ -426,8 +422,6 @@ export function useScmProjectDetails({ project_id: project.id, platform: selectedPlatform.key, issue_alert: issueAlert, - notification_rule_created: !!notificationRule, - rule_ids: ruleIds, variant: 'scm', }); @@ -440,7 +434,6 @@ export function useScmProjectDetails({ accessTeams, alertRuleConfig, canSubmit, - createNotificationAction, createProjectAndRules, existingProject, hasNotificationAction, diff --git a/static/app/components/onboarding/useCreateProjectAndRules.ts b/static/app/components/onboarding/useCreateProjectAndRules.ts index 370343a6669b..e5e963814f66 100644 --- a/static/app/components/onboarding/useCreateProjectAndRules.ts +++ b/static/app/components/onboarding/useCreateProjectAndRules.ts @@ -1,26 +1,14 @@ -import {useCallback} from 'react'; -import * as Sentry from '@sentry/react'; import {useIsMutating, useMutation, useMutationState} from '@tanstack/react-query'; -import {removeProject} from 'sentry/actionCreators/projects'; import {useCreateProject} from 'sentry/components/onboarding/useCreateProject'; -import {useCreateProjectRules} from 'sentry/components/onboarding/useCreateProjectRules'; -import type {IssueAlertRule} from 'sentry/types/alerts'; import type {OnboardingSelectedSDK} from 'sentry/types/onboarding'; import type {Project} from 'sentry/types/project'; -import {defined} from 'sentry/utils/defined'; import type {RequestError} from 'sentry/utils/requestError/requestError'; -import {useApi} from 'sentry/utils/useApi'; -import {useOrganization} from 'sentry/utils/useOrganization'; -import type {useCreateNotificationAction} from 'sentry/views/projectInstall/issueAlertNotificationOptions'; import type {RequestDataFragment} from 'sentry/views/projectInstall/issueAlertOptions'; const MUTATION_KEY = 'create-project-and-rules'; type Variables = { alertRuleConfig: Partial; - createNotificationAction: ReturnType< - typeof useCreateNotificationAction - >['createNotificationAction']; platform: OnboardingSelectedSDK; projectName: string; team?: string; @@ -28,54 +16,14 @@ type Variables = { type Response = { project: Project; - ruleIds: string[]; - notificationRule?: IssueAlertRule; }; -function useRollbackProject() { - const api = useApi(); - const organization = useOrganization(); - - return useCallback( - async (project: Project) => { - Sentry.logger.error('Rolling back project', { - projectToRollback: project, - }); - - try { - // Rolling back the project also deletes its associated alert rules - // due to the cascading delete constraint. - await removeProject({ - api, - orgSlug: organization.slug, - projectSlug: project.slug, - origin: 'getting_started', - }); - } catch (err) { - Sentry.withScope(scope => { - scope.setExtra('error', err); - Sentry.captureMessage('Failed to rollback project'); - }); - } - }, - [api, organization.slug] - ); -} - export function useCreateProjectAndRules() { const createProject = useCreateProject(); - const createProjectRules = useCreateProjectRules(); - const rollbackProject = useRollbackProject(); return useMutation({ mutationKey: [MUTATION_KEY], - mutationFn: async ({ - projectName, - platform, - alertRuleConfig, - team, - createNotificationAction, - }) => { + mutationFn: async ({projectName, platform, alertRuleConfig, team}) => { const project = await createProject.mutateAsync({ name: projectName, platform, @@ -83,41 +31,7 @@ export function useCreateProjectAndRules() { firstTeamSlug: team, }); - try { - const customRulePromise = alertRuleConfig?.shouldCreateCustomRule - ? createProjectRules.mutateAsync({ - projectSlug: project.slug, - name: project.name, - conditions: alertRuleConfig?.conditions, - actions: alertRuleConfig?.actions, - actionMatch: alertRuleConfig?.actionMatch, - frequency: alertRuleConfig?.frequency, - }) - : undefined; - - const notificationRulePromise = createNotificationAction({ - shouldCreateRule: alertRuleConfig?.shouldCreateRule, - name: project.name, - projectSlug: project.slug, - conditions: alertRuleConfig?.conditions, - actionMatch: alertRuleConfig?.actionMatch, - frequency: alertRuleConfig?.frequency, - }); - - const [customRule, notificationRule] = await Promise.all([ - customRulePromise, - notificationRulePromise, - ]); - - const ruleIds = [customRule, notificationRule] - .filter(defined) - .map(rule => rule.id); - - return {project, notificationRule, ruleIds}; - } catch (error) { - await rollbackProject(project); - throw error; - } + return {project}; }, }); } diff --git a/static/app/components/onboarding/useCreateProjectRules.ts b/static/app/components/onboarding/useCreateProjectRules.ts deleted file mode 100644 index d1cc09cf9314..000000000000 --- a/static/app/components/onboarding/useCreateProjectRules.ts +++ /dev/null @@ -1,34 +0,0 @@ -import {useMutation} from '@tanstack/react-query'; - -import type {IssueAlertRule} from 'sentry/types/alerts'; -import type {RequestError} from 'sentry/utils/requestError/requestError'; -import {useApi} from 'sentry/utils/useApi'; -import {useOrganization} from 'sentry/utils/useOrganization'; -import type {RequestDataFragment} from 'sentry/views/projectInstall/issueAlertOptions'; -interface Variables extends Partial< - Pick< - RequestDataFragment, - 'conditions' | 'actions' | 'actionMatch' | 'frequency' | 'name' - > -> { - projectSlug: string; -} - -export function useCreateProjectRules() { - const api = useApi({persistInFlight: true}); - const organization = useOrganization(); - return useMutation({ - mutationFn: ({projectSlug, name, conditions, actions, actionMatch, frequency}) => { - return api.requestPromise(`/projects/${organization.slug}/${projectSlug}/rules/`, { - method: 'POST', - data: { - name, - conditions, - actions, - actionMatch, - frequency, - }, - }); - }, - }); -} diff --git a/static/app/types/alerts.tsx b/static/app/types/alerts.tsx index 0bc512931139..ab2c8e1ce66f 100644 --- a/static/app/types/alerts.tsx +++ b/static/app/types/alerts.tsx @@ -74,28 +74,6 @@ export interface TicketActionData { dynamic_form_fields?: IssueConfigField[]; } -interface SlackAction { - channel: string | undefined; - id: IssueAlertActionType.SLACK; - workspace: string | undefined; - channel_id?: string | undefined; - notes?: string | undefined; - tags?: string | undefined; -} -interface DiscordAction { - channel_id: string | undefined; - id: IssueAlertActionType.DISCORD; - server: string | undefined; - tags?: string | undefined; -} -interface MSTeamsAction { - channel: string | undefined; - id: IssueAlertActionType.MS_TEAMS; - team: string | undefined; -} - -export type IntegrationAction = SlackAction | DiscordAction | MSTeamsAction; - interface UnsavedIssueAlertRule { /** When an issue matches [actionMatch] of the following */ actionMatch: 'all' | 'any' | 'none'; diff --git a/static/app/utils/analytics/issueAnalyticsEvents.tsx b/static/app/utils/analytics/issueAnalyticsEvents.tsx index 3d4e9317a576..349fdaa67831 100644 --- a/static/app/utils/analytics/issueAnalyticsEvents.tsx +++ b/static/app/utils/analytics/issueAnalyticsEvents.tsx @@ -286,7 +286,6 @@ export type IssueEventParameters = { 'project_modal.created': { issue_alert: 'Default' | 'Custom' | 'No Rule'; project_id: string; - rule_id: string; }; 'quick_trace.connected_services': { projects: number; diff --git a/static/app/utils/analytics/workflowAnalyticsEvents.tsx b/static/app/utils/analytics/workflowAnalyticsEvents.tsx index de79b4ea190a..7b20ae60927e 100644 --- a/static/app/utils/analytics/workflowAnalyticsEvents.tsx +++ b/static/app/utils/analytics/workflowAnalyticsEvents.tsx @@ -141,10 +141,8 @@ export type TeamInsightsEventParameters = { 'issue_stream.updated_empty_state_viewed': {platform: string}; 'project_creation_page.created': { issue_alert: 'Default' | 'Custom' | 'No Rule'; - notification_rule_created: boolean; platform: string; project_id: string; - rule_ids: string[]; // 'legacy' from CreateProject, 'scm' from the SCM wizard. Both variants // populate the same payload; only this discriminator differs. variant?: ProjectCreationVariant; diff --git a/static/app/views/projectInstall/createProject.spec.tsx b/static/app/views/projectInstall/createProject.spec.tsx index 402007c3af3a..e71320507a41 100644 --- a/static/app/views/projectInstall/createProject.spec.tsx +++ b/static/app/views/projectInstall/createProject.spec.tsx @@ -681,117 +681,6 @@ describe('CreateProject', () => { ); }); - it('should rollback project when rule creation fails', async () => { - const {organization} = initializeOrg({ - organization: { - access: ['project:read'], - features: ['team-roles'], - allowMemberProjectCreation: true, - }, - }); - - const discordIntegration = OrganizationIntegrationsFixture({ - id: '338731', - name: "Moo Deng's Server", - provider: { - key: 'discord', - slug: 'discord', - name: 'Discord', - canAdd: true, - canDisable: false, - features: ['alert-rule', 'chat-unfurl'], - aspects: { - alerts: [], - }, - }, - }); - - TeamStore.loadUserTeams([teamWithAccess]); - - MockApiClient.addMockResponse({ - url: `/organizations/${organization.slug}/teams/`, - body: [TeamFixture({slug: teamWithAccess.slug})], - }); - - MockApiClient.addMockResponse({ - url: `/organizations/${organization.slug}/`, - body: organization, - }); - - MockApiClient.addMockResponse({ - url: `/organizations/${organization.slug}/integrations/`, - body: [discordIntegration], - match: [MockApiClient.matchQuery({integrationType: 'messaging'})], - }); - - MockApiClient.addMockResponse({ - url: `/organizations/${organization.slug}/integrations/${discordIntegration.id}/channels/`, - body: { - results: [ - { - id: '1437461639900303454', - name: 'general', - display: '#general', - type: 'text', - }, - ], - }, - }); - - const projectCreationMockRequest = MockApiClient.addMockResponse({ - url: `/teams/${organization.slug}/${teamWithAccess.slug}/projects/`, - method: 'POST', - body: {id: '1', slug: 'testProj', name: 'Test Project'}, - }); - - const ruleCreationMockRequest = MockApiClient.addMockResponse({ - url: `/projects/${organization.slug}/testProj/rules/`, - method: 'POST', - statusCode: 400, - body: { - actions: ['Discord: Discord channel URL is missing or formatted incorrectly'], - }, - }); - - const projectDeletionMockRequest = MockApiClient.addMockResponse({ - url: `/projects/${organization.slug}/testProj/`, - method: 'DELETE', - }); - - MockApiClient.addMockResponse({ - url: `/organizations/${organization.slug}/projects/`, - body: [ - { - id: '1', - slug: 'testProj', - name: 'Test Project', - }, - ], - }); - - render(, {organization}); - - await userEvent.click(screen.getByTestId('platform-apple-ios')); - await userEvent.click( - screen.getByRole('checkbox', { - name: /Notify via integration/, - }) - ); - await selectEvent.select(screen.getByLabelText('channel'), /#general/); - await userEvent.click(screen.getByRole('button', {name: 'Create Project'})); - await waitFor(() => { - expect(projectCreationMockRequest).toHaveBeenCalledTimes(1); - }); - await waitFor(() => { - expect(ruleCreationMockRequest).toHaveBeenCalled(); - }); - await waitFor(() => { - expect(projectDeletionMockRequest).toHaveBeenCalledTimes(1); - }); - - expect(addErrorMessage).toHaveBeenCalledWith('Failed to create project apple-ios'); - }); - describe('Issue Alerts Options', () => { const organization = OrganizationFixture(); beforeEach(() => { diff --git a/static/app/views/projectInstall/createProject.tsx b/static/app/views/projectInstall/createProject.tsx index d51262538dd4..530ccdd0b085 100644 --- a/static/app/views/projectInstall/createProject.tsx +++ b/static/app/views/projectInstall/createProject.tsx @@ -25,7 +25,6 @@ import {PlatformPicker, type Platform} from 'sentry/components/platformPicker'; import {TeamSelector} from 'sentry/components/teamSelector'; import {categoryList} from 'sentry/data/platformPickerCategories'; import {t, tct} from 'sentry/locale'; -import type {IssueAlertRule} from 'sentry/types/alerts'; import type {OnboardingSelectedSDK} from 'sentry/types/onboarding'; import type {Team} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; @@ -70,7 +69,6 @@ type FormData = { type CreatedProject = Pick & { platform: OnboardingSelectedSDK; alertRule?: Partial; - notificationRule?: IssueAlertRule; team?: string; wasNameManuallyModified?: boolean; }; @@ -160,15 +158,7 @@ export function CreateProject() { return referrer === 'getting-started' && projectId === createdProject?.id; }, [referrer, projectId, createdProject?.id]); - const createNotificationActionParam = useMemo(() => { - return autoFill && createdProject?.notificationRule?.actions - ? {actions: createdProject.notificationRule.actions} - : undefined; - }, [autoFill, createdProject?.notificationRule?.actions]); - - const {createNotificationAction, notificationProps} = useCreateNotificationAction( - createNotificationActionParam - ); + const {notificationProps} = useCreateNotificationAction(); const validateChannel = useQuery({ ...validateChannelQueryOptions({ @@ -313,14 +303,12 @@ export function CreateProject() { }); try { - const {project, notificationRule, ruleIds} = - await createProjectAndRules.mutateAsync({ - projectName, - platform: selectedPlatform, - team, - alertRuleConfig, - createNotificationAction, - }); + const {project} = await createProjectAndRules.mutateAsync({ + projectName, + platform: selectedPlatform, + team, + alertRuleConfig, + }); trackAnalytics('project_creation_page.created', { organization, @@ -331,8 +319,6 @@ export function CreateProject() { : 'No Rule', project_id: project.id, platform: selectedPlatform.key, - rule_ids: ruleIds, - notification_rule_created: !!notificationRule, variant: 'legacy', }); @@ -348,7 +334,6 @@ export function CreateProject() { team: project.team?.slug, platform: selectedPlatform, alertRule, - notificationRule, wasNameManuallyModified: hasUserModifiedProjectName.current, }); @@ -385,7 +370,6 @@ export function CreateProject() { setCreatedProject, navigate, createProjectAndRules, - createNotificationAction, alertRuleConfig, accessTeams, ] diff --git a/static/app/views/projectInstall/issueAlertNotificationOptions.tsx b/static/app/views/projectInstall/issueAlertNotificationOptions.tsx index 722b21f0941d..18f8393038ff 100644 --- a/static/app/views/projectInstall/issueAlertNotificationOptions.tsx +++ b/static/app/views/projectInstall/issueAlertNotificationOptions.tsx @@ -15,13 +15,8 @@ import { MessagingIntegrationAnalyticsView, SetupMessagingIntegrationButton, } from 'sentry/components/messagingIntegrations/setupMessagingIntegrationButton'; -import {useCreateProjectRules} from 'sentry/components/onboarding/useCreateProjectRules'; import {t, tct} from 'sentry/locale'; -import { - IssueAlertActionType, - type IntegrationAction, - type IssueAlertRuleAction, -} from 'sentry/types/alerts'; +import {IssueAlertActionType, type IssueAlertRuleAction} from 'sentry/types/alerts'; import type {OrganizationIntegration} from 'sentry/types/integrations'; import {trackAnalytics} from 'sentry/utils/analytics'; import {getApiUrl} from 'sentry/utils/api/getApiUrl'; @@ -99,41 +94,6 @@ export type IssueAlertNotificationProps = { channel?: IntegrationChannel; }; -/** - * Builds the serializable IntegrationAction for the current messaging - * selection. Returns undefined if the provider is unrecognised or unset. - */ -function buildIntegrationAction({ - provider, - integration, - channel, -}: Pick): - | IntegrationAction - | undefined { - switch (provider) { - case 'slack': - return { - id: IssueAlertActionType.SLACK, - workspace: integration?.id, - channel: channel?.value, - }; - case 'discord': - return { - id: IssueAlertActionType.DISCORD, - server: integration?.id, - channel_id: channel?.value, - }; - case 'msteams': - return { - id: IssueAlertActionType.MS_TEAMS, - team: integration?.id, - channel: channel?.value, - }; - default: - return undefined; - } -} - export type NotificationSelection = { channel: string; integrationId: string; @@ -186,7 +146,6 @@ type RestoreResolver = ( */ function useNotificationPicker(resolveRestore: RestoreResolver) { const organization = useOrganization(); - const createProjectRules = useCreateProjectRules(); const messagingIntegrationsQuery = useApiQuery( [ @@ -274,41 +233,7 @@ function useNotificationPicker(resolveRestore: RestoreResolver) { setShouldRenderSetupButton(false); }, [messagingIntegrationsQuery.isSuccess, providersToIntegrations, resolveRestore]); - const createNotificationAction = useCallback( - ({ - shouldCreateRule, - projectSlug, - name, - conditions, - actionMatch, - frequency, - }: Partial & {projectSlug: string}) => { - const isCreatingIntegrationNotification = actions.find( - action => action === MultipleCheckboxOptions.INTEGRATION - ); - if (!shouldCreateRule || !isCreatingIntegrationNotification) { - return; - } - - const integrationAction = buildIntegrationAction({provider, integration, channel}); - if (!integrationAction) { - return; - } - - return createProjectRules.mutateAsync({ - projectSlug, - name, - conditions, - actions: [integrationAction], - actionMatch, - frequency, - }); - }, - [actions, provider, integration, channel, createProjectRules] - ); - return { - createNotificationAction, notificationProps: { actions, provider, From cae099f626beb274adcc08dc494a52d83e477ba8 Mon Sep 17 00:00:00 2001 From: Colleen O'Rourke Date: Thu, 13 Aug 2026 11:11:04 -0700 Subject: [PATCH 2/3] rm custom alert threshold option from onboarding --- .../modals/projectCreationModal.tsx | 9 +- .../onboarding/scm/scmAlertFrequency.tsx | 56 +-------- .../scm/scmAlertFrequencySection.tsx | 1 - .../scm/useScmProjectDetails.spec.tsx | 13 --- .../onboarding/scm/useScmProjectDetails.ts | 19 +--- static/app/types/alerts.tsx | 13 --- .../projectCreationAnalyticsEvents.tsx | 5 - .../projectInstall/createProject.spec.tsx | 52 +-------- .../views/projectInstall/createProject.tsx | 39 +------ .../projectInstall/issueAlertOptions.spec.tsx | 25 ++-- .../projectInstall/issueAlertOptions.tsx | 107 +----------------- 11 files changed, 23 insertions(+), 316 deletions(-) diff --git a/static/app/components/modals/projectCreationModal.tsx b/static/app/components/modals/projectCreationModal.tsx index d89b6c1aeb92..535fae3563e1 100644 --- a/static/app/components/modals/projectCreationModal.tsx +++ b/static/app/components/modals/projectCreationModal.tsx @@ -123,11 +123,7 @@ export default function ProjectCreationModal({ clearIndicators(); trackAnalytics('project_modal.created', { organization, - issue_alert: alertRuleConfig.defaultRules - ? 'Default' - : alertRuleConfig.shouldCreateCustomRule - ? 'Custom' - : 'No Rule', + issue_alert: alertRuleConfig.defaultRules ? 'Default' : 'No Rule', project_id: projectData.id, }); @@ -160,9 +156,6 @@ export default function ProjectCreationModal({ {t('Set your alert frequency')} { setAlertForm(prev => ({ ...prev, diff --git a/static/app/components/onboarding/scm/scmAlertFrequency.tsx b/static/app/components/onboarding/scm/scmAlertFrequency.tsx index e5b3c31370d1..62311f6d07ef 100644 --- a/static/app/components/onboarding/scm/scmAlertFrequency.tsx +++ b/static/app/components/onboarding/scm/scmAlertFrequency.tsx @@ -1,14 +1,9 @@ -import {Input} from '@sentry/scraps/input'; -import {Grid, Stack} from '@sentry/scraps/layout'; -import {Select} from '@sentry/scraps/select'; -import {Text} from '@sentry/scraps/text'; +import {Stack} from '@sentry/scraps/layout'; import {ScmAlertOptionCard} from 'sentry/components/onboarding/scm/scmAlertOptionCard'; import {t} from 'sentry/locale'; import { type AlertRuleOptions, - INTERVAL_CHOICES, - METRIC_CHOICES, RuleAction, } from 'sentry/views/projectInstall/issueAlertOptions'; @@ -21,13 +16,9 @@ interface ScmAlertFrequencyProps extends Partial { export function ScmAlertFrequency({ alertSetting = RuleAction.DEFAULT_ALERT, - interval = '1m', - metric = 0, - threshold = '10', onFieldChange, }: ScmAlertFrequencyProps) { const isDefaultSelected = alertSetting === RuleAction.DEFAULT_ALERT; - const isCustomSelected = alertSetting === RuleAction.CUSTOMIZED_ALERTS; const isLaterSelected = alertSetting === RuleAction.CREATE_ALERT_LATER; return ( @@ -39,51 +30,6 @@ export function ScmAlertFrequency({ onSelect={() => onFieldChange('alertSetting', RuleAction.DEFAULT_ALERT)} /> - onFieldChange('alertSetting', RuleAction.CUSTOMIZED_ALERTS)} - > - {isCustomSelected && ( - - - - {t('When there are more than')} - - - onFieldChange('threshold', e.target.value)} - /> - onFieldChange('interval', option.value)} - menuPortalTarget={document.body} - /> - - - )} - - = { [RuleAction.DEFAULT_ALERT]: [t('High priority issues'), 'info'], - [RuleAction.CUSTOMIZED_ALERTS]: [t('Custom'), 'info'], [RuleAction.CREATE_ALERT_LATER]: [t('Off'), 'muted'], }; diff --git a/static/app/components/onboarding/scm/useScmProjectDetails.spec.tsx b/static/app/components/onboarding/scm/useScmProjectDetails.spec.tsx index ddaa00eac0db..b1763d43a1bc 100644 --- a/static/app/components/onboarding/scm/useScmProjectDetails.spec.tsx +++ b/static/app/components/onboarding/scm/useScmProjectDetails.spec.tsx @@ -8,7 +8,6 @@ import {act, renderHookWithProviders, waitFor} from 'sentry-test/reactTestingLib import {ProjectsStore} from 'sentry/stores/projectsStore'; import {TeamStore} from 'sentry/stores/teamStore'; import type {OnboardingSelectedSDK} from 'sentry/types/onboarding'; -import * as analytics from 'sentry/utils/analytics'; import {MultipleCheckboxOptions} from 'sentry/views/projectInstall/issueAlertNotificationOptions'; import { DEFAULT_ISSUE_ALERT_OPTIONS_VALUES, @@ -105,18 +104,6 @@ describe('useScmProjectDetails', () => { jest.restoreAllMocks(); }); - it('tracks threshold edits for SCM project creation', () => { - const trackAnalyticsSpy = jest.spyOn(analytics, 'trackAnalytics'); - const {result} = renderDetails(); - - act(() => result.current.onAlertChange('threshold', '10')); - - expect(trackAnalyticsSpy).toHaveBeenCalledWith( - 'project_creation.alert_threshold_edited', - expect.objectContaining({field: 'threshold', variant: 'scm'}) - ); - }); - it('requires an integration channel when notifying via integration', () => { TeamStore.loadInitialData([adminTeam]); ProjectsStore.loadInitialData([]); diff --git a/static/app/components/onboarding/scm/useScmProjectDetails.ts b/static/app/components/onboarding/scm/useScmProjectDetails.ts index b0c02ef40d84..e1baf7409902 100644 --- a/static/app/components/onboarding/scm/useScmProjectDetails.ts +++ b/static/app/components/onboarding/scm/useScmProjectDetails.ts @@ -212,18 +212,11 @@ export function useScmProjectDetails({ if (key === 'alertSetting') { const optionMap: Record = { [RuleAction.DEFAULT_ALERT]: 'high_priority', - [RuleAction.CUSTOMIZED_ALERTS]: 'custom', [RuleAction.CREATE_ALERT_LATER]: 'create_later', }; trackAnalytics('project_creation.project_details_alert_selected', { organization, - option: optionMap[value as number] ?? String(value), - variant: 'scm', - }); - } else if (key === 'threshold' || key === 'metric' || key === 'interval') { - trackAnalytics('project_creation.alert_threshold_edited', { - organization, - field: key, + option: optionMap[value] ?? String(value), variant: 'scm', }); } @@ -319,9 +312,6 @@ export function useScmProjectDetails({ projectNameResolved === savedForm.projectName && teamSlugResolved === savedForm.teamSlug && alertRuleConfig.alertSetting === savedAlert?.alertSetting && - alertRuleConfig.interval === savedAlert?.interval && - alertRuleConfig.metric === savedAlert?.metric && - alertRuleConfig.threshold === savedAlert?.threshold && isEqual( hasNotificationAction ? buildNotificationSelection(notificationProps) : undefined, savedForm?.notificationSelection @@ -349,13 +339,10 @@ export function useScmProjectDetails({ notificationSelection, }; // Mirror the legacy project_creation_page.created `issue_alert` breakdown - // (see createProject.tsx): Custom > Default > No Rule, derived from the - // configured alert setting. + // (see createProject.tsx): Default > No Rule, derived from the configured + // alert setting. let issueAlert: 'Custom' | 'Default' | 'No Rule'; switch (alertRuleConfig.alertSetting) { - case RuleAction.CUSTOMIZED_ALERTS: - issueAlert = 'Custom'; - break; case RuleAction.CREATE_ALERT_LATER: issueAlert = 'No Rule'; break; diff --git a/static/app/types/alerts.tsx b/static/app/types/alerts.tsx index ab2c8e1ce66f..8f5ab4ea33ee 100644 --- a/static/app/types/alerts.tsx +++ b/static/app/types/alerts.tsx @@ -29,19 +29,6 @@ export const enum IssueAlertActionType { AZURE_DEVOPS_CREATE_TICKET = 'sentry.integrations.vsts.notify_action.AzureDevopsCreateTicketAction', } -export const enum IssueAlertConditionType { - EVERY_EVENT = 'sentry.rules.conditions.every_event.EveryEventCondition', - FIRST_SEEN_EVENT = 'sentry.rules.conditions.first_seen_event.FirstSeenEventCondition', - REGRESSION_EVENT = 'sentry.rules.conditions.regression_event.RegressionEventCondition', - REAPPEARED_EVENT = 'sentry.rules.conditions.reappeared_event.ReappearedEventCondition', - EVENT_FREQUENCY = 'sentry.rules.conditions.event_frequency.EventFrequencyCondition', - EVENT_UNIQUE_USER_FREQUENCY = 'sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyCondition', - EVENT_UNIQUE_USER_FREQUENCY_WITH_CONDITIONS = 'sentry.rules.conditions.event_frequency.EventUniqueUserFrequencyConditionWithConditions', - EVENT_FREQUENCY_PERCENT = 'sentry.rules.conditions.event_frequency.EventFrequencyPercentCondition', - NEW_HIGH_PRIORITY_ISSUE = 'sentry.rules.conditions.high_priority_issue.NewHighPriorityIssueCondition', - EXISTING_HIGH_PRIORITY_ISSUE = 'sentry.rules.conditions.high_priority_issue.ExistingHighPriorityIssueCondition', -} - /** * These templates that tell the UI how to render the action or condition * and what fields it needs diff --git a/static/app/utils/analytics/projectCreationAnalyticsEvents.tsx b/static/app/utils/analytics/projectCreationAnalyticsEvents.tsx index ab3a40b9094e..16b97f9057a9 100644 --- a/static/app/utils/analytics/projectCreationAnalyticsEvents.tsx +++ b/static/app/utils/analytics/projectCreationAnalyticsEvents.tsx @@ -7,10 +7,6 @@ export type ProjectCreationVariant = 'scm' | 'legacy'; export type ProjectCreationEventParameters = { - 'project_creation.alert_threshold_edited': { - field: 'threshold' | 'metric' | 'interval'; - variant?: ProjectCreationVariant; - }; 'project_creation.back_button_clicked': { variant?: ProjectCreationVariant; }; @@ -175,7 +171,6 @@ export const projectCreationEventMap: Record< 'Project Creation: Data Removal Modal Rendered', 'project_creation.data_removed': 'Project Creation: Data Removed', 'project_creation.back_button_clicked': 'Project Creation: Back Button Clicked', - 'project_creation.alert_threshold_edited': 'Project Creation: Alert Threshold Edited', 'project_creation.notify_integration_toggled': 'Project Creation: Notify Integration Toggled', 'project_creation.notify_provider_changed': 'Project Creation: Notify Provider Changed', diff --git a/static/app/views/projectInstall/createProject.spec.tsx b/static/app/views/projectInstall/createProject.spec.tsx index e71320507a41..674244509cbe 100644 --- a/static/app/views/projectInstall/createProject.spec.tsx +++ b/static/app/views/projectInstall/createProject.spec.tsx @@ -727,18 +727,7 @@ describe('CreateProject', () => { await userEvent.click(screen.getByTestId('platform-apple-ios')); expect(getSubmitButton()).toBeEnabled(); - await userEvent.click(screen.getByText(/When there are more than/)); - expect(getSubmitButton()).toBeEnabled(); - - await userEvent.clear(screen.getByTestId('range-input')); - expect(getSubmitButton()).toBeDisabled(); - - await userEvent.type(screen.getByTestId('range-input'), '2712'); - expect(getSubmitButton()).toBeEnabled(); - - await userEvent.clear(screen.getByTestId('range-input')); - expect(getSubmitButton()).toBeDisabled(); - + // Notifying via integration requires a channel to be picked await userEvent.click( screen.getByRole('checkbox', { name: 'Notify via integration (Slack, Discord, MS Teams, etc.)', @@ -755,7 +744,7 @@ describe('CreateProject', () => { expect(projectCreationMockRequest).toHaveBeenCalled(); }); - it('fires alert_selected and alert_threshold_edited with variant=legacy', async () => { + it('fires alert_selected with variant=legacy', async () => { renderFrameworkModalMockRequests({ organization, teamSlug: teamWithAccess.slug, @@ -764,43 +753,12 @@ describe('CreateProject', () => { render(, {organization}); - // Switch to custom alerts: fires the alert-selected event, and reveals the - // threshold input. - await userEvent.click(screen.getByText(/When there are more than/)); - expect(trackAnalyticsSpy).toHaveBeenCalledWith( - 'project_creation.project_details_alert_selected', - expect.objectContaining({option: 'custom', variant: 'legacy'}) + await userEvent.click( + screen.getByRole('radio', {name: /create my own alerts later/i}) ); - - // Edit the threshold: fires the threshold-edited event. - await userEvent.type(screen.getByTestId('range-input'), '5'); expect(trackAnalyticsSpy).toHaveBeenCalledWith( - 'project_creation.alert_threshold_edited', - expect.objectContaining({field: 'threshold', variant: 'legacy'}) - ); - }); - - it('does not fire alert_threshold_edited unless custom alerts are selected', async () => { - renderFrameworkModalMockRequests({ - organization, - teamSlug: teamWithAccess.slug, - }); - const trackAnalyticsSpy = jest.spyOn(analytics, 'trackAnalytics'); - - render(, {organization}); - - // Default is high-priority. Metric/interval Selects stay interactive while - // the custom radio is unselected (their wrappers call preventDefault), so - // an edit must not count as a custom-threshold change. - await selectEvent.select(screen.getByText('occurrences of'), /users affected/); - - expect(trackAnalyticsSpy).not.toHaveBeenCalledWith( - 'project_creation.alert_threshold_edited', - expect.anything() - ); - expect(trackAnalyticsSpy).not.toHaveBeenCalledWith( 'project_creation.project_details_alert_selected', - expect.objectContaining({option: 'custom'}) + expect.objectContaining({option: 'create_later', variant: 'legacy'}) ); }); diff --git a/static/app/views/projectInstall/createProject.tsx b/static/app/views/projectInstall/createProject.tsx index 530ccdd0b085..435773e3017c 100644 --- a/static/app/views/projectInstall/createProject.tsx +++ b/static/app/views/projectInstall/createProject.tsx @@ -76,10 +76,8 @@ type CreatedProject = Pick & { function getMissingValues({ team, projectName, - conditions, notificationProps, shouldCreateRule, - shouldCreateCustomRule, isOrgMemberWithNoAccess, platform, }: { @@ -91,17 +89,10 @@ function getMissingValues({ projectName: string; team: string | undefined; platform?: OnboardingSelectedSDK; -} & Partial< - Pick ->) { +} & Partial>) { return { isMissingTeam: !isOrgMemberWithNoAccess && !team, isMissingProjectName: projectName === '', - isMissingAlertThreshold: - shouldCreateCustomRule && - (!conditions || - conditions.length === 0 || - !conditions.every(condition => !!condition.value)), isMissingMessagingIntegrationChannel: shouldCreateRule && notificationProps.actions?.includes(MultipleCheckboxOptions.INTEGRATION) && @@ -112,7 +103,6 @@ function getMissingValues({ function getSubmitTooltipText({ isMissingProjectName, - isMissingAlertThreshold, isMissingMessagingIntegrationChannel, isMissingPlatform, formErrorCount, @@ -125,9 +115,6 @@ function getSubmitTooltipText({ if (isMissingProjectName) { return t('Please provide a project slug'); } - if (isMissingAlertThreshold) { - return t('Please provide an alert threshold'); - } if (isMissingMessagingIntegrationChannel) { return t('Please provide an integration channel for alert notifications'); } @@ -227,9 +214,7 @@ export function CreateProject() { notificationProps, projectName: formData.projectName, team: formData.team, - shouldCreateCustomRule: alertRuleConfig.shouldCreateCustomRule, shouldCreateRule: alertRuleConfig.shouldCreateRule, - conditions: alertRuleConfig.conditions, platform: formData.platform, }); @@ -241,7 +226,6 @@ export function CreateProject() { missingValues.isMissingPlatform, missingValues.isMissingTeam, missingValues.isMissingProjectName, - missingValues.isMissingAlertThreshold, missingValues.isMissingMessagingIntegrationChannel, isNotifyingViaIntegration && validateChannelError, ].filter(Boolean).length; @@ -312,11 +296,7 @@ export function CreateProject() { trackAnalytics('project_creation_page.created', { organization, - issue_alert: alertRuleConfig.shouldCreateCustomRule - ? 'Custom' - : alertRuleConfig.shouldCreateRule - ? 'Default' - : 'No Rule', + issue_alert: alertRuleConfig.shouldCreateRule ? 'Default' : 'No Rule', project_id: project.id, platform: selectedPlatform.key, variant: 'legacy', @@ -509,9 +489,6 @@ export function CreateProject() { {t('Set your alert frequency')} { updateFormData('alertRule', { @@ -521,21 +498,11 @@ export function CreateProject() { if (field === 'alertSetting') { const optionMap: Record = { [RuleAction.DEFAULT_ALERT]: 'high_priority', - [RuleAction.CUSTOMIZED_ALERTS]: 'custom', [RuleAction.CREATE_ALERT_LATER]: 'create_later', }; trackAnalytics('project_creation.project_details_alert_selected', { organization, - option: optionMap[value as number] ?? String(value), - variant: 'legacy', - }); - } else if ( - (field === 'threshold' || field === 'metric' || field === 'interval') && - formData.alertRule?.alertSetting === RuleAction.CUSTOMIZED_ALERTS - ) { - trackAnalytics('project_creation.alert_threshold_edited', { - organization, - field, + option: optionMap[value] ?? String(value), variant: 'legacy', }); } diff --git a/static/app/views/projectInstall/issueAlertOptions.spec.tsx b/static/app/views/projectInstall/issueAlertOptions.spec.tsx index bb70e229437f..25b1b3d474ee 100644 --- a/static/app/views/projectInstall/issueAlertOptions.spec.tsx +++ b/static/app/views/projectInstall/issueAlertOptions.spec.tsx @@ -42,18 +42,16 @@ describe('IssueAlertOptions', () => { jest.clearAllMocks(); }); - it('should pre-fill threshold value after a valid server response', () => { + it('should report the selected alert setting', async () => { render(getComponent()); - expect(screen.getByTestId('range-input')).toHaveValue(10); + await userEvent.click(screen.getByLabelText(/I'll create my own alerts later/i)); + expect(mockOnChange).toHaveBeenCalledWith( + 'alertSetting', + RuleAction.CREATE_ALERT_LATER + ); }); - it('should provide fallthroughType with issue action', async () => { - render(getComponent()); - await userEvent.click(screen.getByLabelText(/When there are more than/i)); - expect(mockOnChange).toHaveBeenCalledWith('alertSetting', 1); - }); - - it('should render alert configuration if `Default` or `Custom` alerts are selected', async () => { + it('should render alert configuration if `Default` alerts are selected', () => { render(getComponent()); // Default will be RuleAction.DEFAULT_ALERT @@ -63,15 +61,6 @@ describe('IssueAlertOptions', () => { name: 'Notify via integration (Slack, Discord, MS Teams, etc.)', }) ).toBeInTheDocument(); - - // Select RuleAction.CUSTOMIZED_ALERTS - await userEvent.click(screen.getByLabelText(/When there are more than/i)); - expect(screen.getByRole('checkbox', {name: 'Notify via email'})).toBeInTheDocument(); - expect( - screen.getByRole('checkbox', { - name: 'Notify via integration (Slack, Discord, MS Teams, etc.)', - }) - ).toBeInTheDocument(); }); it('should not render notification configuration if `Create Alerts Later` is selected', () => { diff --git a/static/app/views/projectInstall/issueAlertOptions.tsx b/static/app/views/projectInstall/issueAlertOptions.tsx index 834f30449d7e..0d2b1650f96e 100644 --- a/static/app/views/projectInstall/issueAlertOptions.tsx +++ b/static/app/views/projectInstall/issueAlertOptions.tsx @@ -1,26 +1,18 @@ import styled from '@emotion/styled'; -import {Input} from '@sentry/scraps/input'; import {Flex} from '@sentry/scraps/layout'; -import {Select} from '@sentry/scraps/select'; import {RadioGroup} from 'sentry/components/forms/controls/radioGroup'; -import {t, tct} from 'sentry/locale'; +import {t} from 'sentry/locale'; import type {IssueAlertRule} from 'sentry/types/alerts'; -import {IssueAlertActionType, IssueAlertConditionType} from 'sentry/types/alerts'; +import {IssueAlertActionType} from 'sentry/types/alerts'; import { IssueAlertNotificationOptions, type IssueAlertNotificationProps, } from 'sentry/views/projectInstall/issueAlertNotificationOptions'; -enum MetricValues { - ERRORS = 0, - USERS = 1, -} - export enum RuleAction { DEFAULT_ALERT = 0, - CUSTOMIZED_ALERTS = 1, CREATE_ALERT_LATER = 2, } @@ -36,76 +28,29 @@ function parseRuleAction(val: number | string) { throw new RangeError('Supplied alert creation action is not handled'); } -function metricValueToConditionType(metricValue: MetricValues): IssueAlertConditionType { - switch (metricValue) { - case MetricValues.ERRORS: - return IssueAlertConditionType.EVENT_FREQUENCY; - case MetricValues.USERS: - return IssueAlertConditionType.EVENT_UNIQUE_USER_FREQUENCY; - default: - throw new RangeError(`Supplied metric value ${metricValue} is not handled`); - } -} - -export const METRIC_CHOICES = [ - {value: MetricValues.ERRORS, label: t('occurrences of')}, - {value: MetricValues.USERS, label: t('users affected by')}, -]; - -export const INTERVAL_CHOICES = [ - {value: '1m', label: t('one minute')}, - {value: '5m', label: t('5 minutes')}, - {value: '15m', label: t('15 minutes')}, - {value: '1h', label: t('one hour')}, - {value: '1d', label: t('one day')}, - {value: '1w', label: t('one week')}, - {value: '30d', label: t('30 days')}, -]; - export const DEFAULT_ISSUE_ALERT_OPTIONS_VALUES = { alertSetting: RuleAction.DEFAULT_ALERT, - interval: '1m', - metric: MetricValues.ERRORS, - threshold: '10', }; export type RequestDataFragment = Pick< IssueAlertRule, - 'actionMatch' | 'actions' | 'conditions' | 'frequency' | 'name' + 'actionMatch' | 'actions' | 'frequency' | 'name' > & { defaultRules: boolean; - shouldCreateCustomRule: boolean; shouldCreateRule: boolean; }; export interface AlertRuleOptions { alertSetting: RuleAction; - interval: string; - metric: MetricValues; - threshold: string; } export function getRequestDataFragment({ alertSetting = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.alertSetting, - interval = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.interval, - metric = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.metric, - threshold = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.threshold, }: Partial = {}): RequestDataFragment { return { defaultRules: alertSetting === RuleAction.DEFAULT_ALERT, shouldCreateRule: alertSetting !== RuleAction.CREATE_ALERT_LATER, - shouldCreateCustomRule: alertSetting === RuleAction.CUSTOMIZED_ALERTS, name: 'Send a notification for new issues', - conditions: - interval.length > 0 && threshold.length > 0 - ? [ - { - interval, - id: metricValueToConditionType(metric), - value: threshold, - }, - ] - : [], actions: [ { id: IssueAlertActionType.NOTIFY_EMAIL, @@ -128,57 +73,11 @@ export interface IssueAlertOptionsProps extends Partial { export function IssueAlertOptions({ alertSetting = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.alertSetting, - interval = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.interval, - metric = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.metric, - threshold = DEFAULT_ISSUE_ALERT_OPTIONS_VALUES.threshold, notificationProps, onFieldChange, }: IssueAlertOptionsProps) { const issueAlertOptionsChoices: Array<[RuleAction, React.ReactNode]> = [ [RuleAction.DEFAULT_ALERT, t('Alert me on high priority issues')], - [ - RuleAction.CUSTOMIZED_ALERTS, - tct('When there are more than [threshold][metric] a unique error in [interval]', { - threshold: ( - // 80px is just enough to see 6 digits at a time -
- { - onFieldChange('threshold', e.target.value); - }} - data-test-id="range-input" - /> -
- ), - metric: ( -
e.preventDefault()}> - { - onFieldChange('interval', option.value); - }} - /> -
- ), - }), - ], [RuleAction.CREATE_ALERT_LATER, t("I'll create my own alerts later")], ]; From a11f7ac811666374eee7a1cfdced33968b15dcff Mon Sep 17 00:00:00 2001 From: Colleen O'Rourke Date: Thu, 13 Aug 2026 12:50:30 -0700 Subject: [PATCH 3/3] rm rule creation from the shared scm project creation hook --- .../scm/useScmProjectCreation.spec.tsx | 2 +- .../onboarding/scm/useScmProjectCreation.ts | 20 ------------------- 2 files changed, 1 insertion(+), 21 deletions(-) diff --git a/static/app/components/onboarding/scm/useScmProjectCreation.spec.tsx b/static/app/components/onboarding/scm/useScmProjectCreation.spec.tsx index f6db6133f10b..473912099f6c 100644 --- a/static/app/components/onboarding/scm/useScmProjectCreation.spec.tsx +++ b/static/app/components/onboarding/scm/useScmProjectCreation.spec.tsx @@ -95,7 +95,7 @@ describe('useScmProjectCreation', () => { ); expect(onProjectCreated).toHaveBeenCalledWith('python'); expect(onSuccess).toHaveBeenCalledWith( - expect.objectContaining({project: createdProject, reused: false, ruleIds: []}) + expect.objectContaining({project: createdProject, reused: false}) ); // The slug must be persisted before completion so the duplicate-prevention // handoff to SDK setup survives a failure later in the sequence. diff --git a/static/app/components/onboarding/scm/useScmProjectCreation.ts b/static/app/components/onboarding/scm/useScmProjectCreation.ts index 0c034706982d..eaeb026a346b 100644 --- a/static/app/components/onboarding/scm/useScmProjectCreation.ts +++ b/static/app/components/onboarding/scm/useScmProjectCreation.ts @@ -5,7 +5,6 @@ import {addErrorMessage} from 'sentry/actionCreators/indicator'; import {linkProjectToRepository} from 'sentry/components/onboarding/scm/linkProjectToRepository'; import {useCreateProjectAndRules} from 'sentry/components/onboarding/useCreateProjectAndRules'; import {t} from 'sentry/locale'; -import type {IssueAlertRule} from 'sentry/types/alerts'; import type {Repository} from 'sentry/types/integrations'; import type {OnboardingSelectedSDK} from 'sentry/types/onboarding'; import type {Team} from 'sentry/types/organization'; @@ -13,13 +12,8 @@ import type {Project} from 'sentry/types/project'; import {useOrganization} from 'sentry/utils/useOrganization'; import {useProjects} from 'sentry/utils/useProjects'; import {useTeams} from 'sentry/utils/useTeams'; -import type {useCreateNotificationAction} from 'sentry/views/projectInstall/issueAlertNotificationOptions'; import type {RequestDataFragment} from 'sentry/views/projectInstall/issueAlertOptions'; -type CreateNotificationAction = ReturnType< - typeof useCreateNotificationAction ->['createNotificationAction']; - export interface ScmProjectCreationResult { project: Project; /** @@ -27,8 +21,6 @@ export interface ScmProjectCreationResult { * unchanged platform) instead of creating a new one. */ reused: boolean; - ruleIds: string[]; - notificationRule?: IssueAlertRule; } interface UseScmProjectCreationOptions { @@ -62,15 +54,8 @@ interface CreateOrReuseProjectOptions { * default (email) rules only. */ alertRuleConfig?: Partial; - /** - * Creates the messaging-integration notification rule, if one is configured. - * Defaults to a no-op (email-only creation). - */ - createNotificationAction?: CreateNotificationAction; } -const noopNotificationAction: CreateNotificationAction = () => {}; - /** * Shared project + alert-rule creation for the SCM onboarding flow. Both * experiment branches call this from their respective creation boundaries: @@ -105,7 +90,6 @@ export function useScmProjectCreation({ async ({ platform, alertRuleConfig, - createNotificationAction, onSuccess, }: CreateOrReuseProjectOptions): Promise => { if (isCreatingRef.current) { @@ -123,7 +107,6 @@ export function useScmProjectCreation({ const result: ScmProjectCreationResult = { project: existingProject, reused: true, - ruleIds: [], }; onSuccess(result); return result; @@ -142,7 +125,6 @@ export function useScmProjectCreation({ platform, team: firstAdminTeam?.slug, alertRuleConfig: alertRuleConfig ?? {defaultRules: true}, - createNotificationAction: createNotificationAction ?? noopNotificationAction, }) .catch(error => { addErrorMessage(t('Failed to create project')); @@ -166,8 +148,6 @@ export function useScmProjectCreation({ const result: ScmProjectCreationResult = { project: creation.project, reused: false, - ruleIds: creation.ruleIds, - notificationRule: creation.notificationRule, }; onSuccess(result); return result;