From 4437b775513ba0dd0615a4a0cbb02f43c415be30 Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Fri, 23 May 2025 09:54:29 +0200 Subject: [PATCH 1/3] ref(project-creation): Introduce useCreateProjectRules hook --- .../onboarding/useCreateProjectRules.tsx | 35 +++++++++++++++++++ .../views/projectInstall/createProject.tsx | 25 ++++++------- .../issueAlertNotificationOptions.tsx | 22 ++++++------ 3 files changed, 56 insertions(+), 26 deletions(-) create mode 100644 static/app/components/onboarding/useCreateProjectRules.tsx diff --git a/static/app/components/onboarding/useCreateProjectRules.tsx b/static/app/components/onboarding/useCreateProjectRules.tsx new file mode 100644 index 00000000000000..bb13ab70715980 --- /dev/null +++ b/static/app/components/onboarding/useCreateProjectRules.tsx @@ -0,0 +1,35 @@ +import {useMutation} from 'sentry/utils/queryClient'; +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(); + const organization = useOrganization(); + // TODO(priscila): Introduce better response types + return useMutation<{id: string}, RequestError, Variables>({ + 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/views/projectInstall/createProject.tsx b/static/app/views/projectInstall/createProject.tsx index dcb8871b5c2db8..aae8b8b9b51b34 100644 --- a/static/app/views/projectInstall/createProject.tsx +++ b/static/app/views/projectInstall/createProject.tsx @@ -19,6 +19,7 @@ import List from 'sentry/components/list'; import ListItem from 'sentry/components/list/listItem'; import {SupportedLanguages} from 'sentry/components/onboarding/frameworkSuggestionModal'; import {useCreateProject} from 'sentry/components/onboarding/useCreateProject'; +import {useCreateProjectRules} from 'sentry/components/onboarding/useCreateProjectRules'; import type {Platform} from 'sentry/components/platformPicker'; import PlatformPicker from 'sentry/components/platformPicker'; import TeamSelector from 'sentry/components/teamSelector'; @@ -140,6 +141,7 @@ export function CreateProject() { const {createNotificationAction, notificationProps} = useCreateNotificationAction(); const canUserCreateProject = useCanCreateProject(); const createProject = useCreateProject(); + const createProjectRules = useCreateProjectRules(); const {teams} = useTeams(); const accessTeams = teams.filter((team: Team) => team.access.includes('team:admin')); const referrer = decodeScalar(location.query.referrer); @@ -157,19 +159,14 @@ export function CreateProject() { const ruleIds = []; if (alertRuleConfig?.shouldCreateCustomRule) { - const ruleData = await api.requestPromise( - `/projects/${organization.slug}/${project.slug}/rules/`, - { - method: 'POST', - data: { - name: project.name, - conditions: alertRuleConfig?.conditions, - actions: alertRuleConfig?.actions, - actionMatch: alertRuleConfig?.actionMatch, - frequency: alertRuleConfig?.frequency, - }, - } - ); + const ruleData = await createProjectRules.mutateAsync({ + projectSlug: project.slug, + name: project.name, + conditions: alertRuleConfig?.conditions, + actions: alertRuleConfig?.actions, + actionMatch: alertRuleConfig?.actionMatch, + frequency: alertRuleConfig?.frequency, + }); ruleIds.push(ruleData.id); } @@ -189,7 +186,7 @@ export function CreateProject() { return ruleIds; }, - [organization, api, createNotificationAction] + [createNotificationAction, createProjectRules] ); const autoFill = useMemo(() => { diff --git a/static/app/views/projectInstall/issueAlertNotificationOptions.tsx b/static/app/views/projectInstall/issueAlertNotificationOptions.tsx index 3f2518a572f142..724da75ac2f7dd 100644 --- a/static/app/views/projectInstall/issueAlertNotificationOptions.tsx +++ b/static/app/views/projectInstall/issueAlertNotificationOptions.tsx @@ -2,13 +2,13 @@ import {Fragment, useCallback, useEffect, useMemo, useState} from 'react'; import styled from '@emotion/styled'; import MultipleCheckbox from 'sentry/components/forms/controls/multipleCheckbox'; +import {useCreateProjectRules} from 'sentry/components/onboarding/useCreateProjectRules'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import {type IntegrationAction, IssueAlertActionType} from 'sentry/types/alerts'; import type {OrganizationIntegration} from 'sentry/types/integrations'; import {useApiQuery} from 'sentry/utils/queryClient'; import useRouteAnalyticsParams from 'sentry/utils/routeAnalytics/useRouteAnalyticsParams'; -import useApi from 'sentry/utils/useApi'; import useOrganization from 'sentry/utils/useOrganization'; import SetupMessagingIntegrationButton, { MessagingIntegrationAnalyticsView, @@ -77,8 +77,8 @@ export type IssueAlertNotificationProps = { }; export function useCreateNotificationAction() { - const api = useApi(); const organization = useOrganization(); + const createProjectRules = useCreateProjectRules(); const messagingIntegrationsQuery = useApiQuery( [`/organizations/${organization.slug}/integrations/?integrationType=messaging`], @@ -174,18 +174,16 @@ export function useCreateNotificationAction() { return undefined; } - return api.requestPromise(`/projects/${organization.slug}/${projectSlug}/rules/`, { - method: 'POST', - data: { - name, - conditions, - actions: [integrationAction], - actionMatch, - frequency, - }, + return createProjectRules.mutateAsync({ + projectSlug, + name, + conditions, + actions: [integrationAction], + actionMatch, + frequency, }); }, - [actions, api, provider, integration, channel, organization.slug] + [actions, provider, integration, channel, createProjectRules] ); return { From 7f0ae304193acbc5addb63111e83c5824db3017f Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Tue, 27 May 2025 09:21:01 +0200 Subject: [PATCH 2/3] it does not need to be tsx --- .../{useCreateProjectRules.tsx => useCreateProjectRules.ts} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename static/app/components/onboarding/{useCreateProjectRules.tsx => useCreateProjectRules.ts} (100%) diff --git a/static/app/components/onboarding/useCreateProjectRules.tsx b/static/app/components/onboarding/useCreateProjectRules.ts similarity index 100% rename from static/app/components/onboarding/useCreateProjectRules.tsx rename to static/app/components/onboarding/useCreateProjectRules.ts From 049dae1bddb23db1cea9aa2d89378b0094c546f7 Mon Sep 17 00:00:00 2001 From: Priscila Oliveira Date: Mon, 2 Jun 2025 10:39:13 +0200 Subject: [PATCH 3/3] feedback --- static/app/components/onboarding/useCreateProjectRules.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/app/components/onboarding/useCreateProjectRules.ts b/static/app/components/onboarding/useCreateProjectRules.ts index bb13ab70715980..d666ae4f0a253f 100644 --- a/static/app/components/onboarding/useCreateProjectRules.ts +++ b/static/app/components/onboarding/useCreateProjectRules.ts @@ -15,7 +15,7 @@ interface Variables } export function useCreateProjectRules() { - const api = useApi(); + const api = useApi({persistInFlight: true}); const organization = useOrganization(); // TODO(priscila): Introduce better response types return useMutation<{id: string}, RequestError, Variables>({