From 2d9b611c2fa42d0bc6508f24e84ad926cb6c37de Mon Sep 17 00:00:00 2001 From: Malachi Willey Date: Fri, 14 Feb 2025 09:16:12 -0800 Subject: [PATCH 1/2] feat(nav): Add pathname builder for alerts --- static/app/views/alerts/pathnames.tsx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 static/app/views/alerts/pathnames.tsx diff --git a/static/app/views/alerts/pathnames.tsx b/static/app/views/alerts/pathnames.tsx new file mode 100644 index 00000000000000..e5aa7afd3d50dd --- /dev/null +++ b/static/app/views/alerts/pathnames.tsx @@ -0,0 +1,19 @@ +import type {Organization} from 'sentry/types/organization'; +import normalizeUrl from 'sentry/utils/url/normalizeUrl'; + +const LEGACY_ALERTS_BASE_PATHNAME = 'alerts'; +const ALERTS_BASE_PATHNAME = 'issues/alerts'; + +export function makeAlertsPathname({ + path, + organization, +}: { + organization: Organization; + path: '/' | `/${string}/`; +}) { + return normalizeUrl( + organization.features.includes('navigation-sidebar-v2') + ? `/organizations/${organization.slug}/${ALERTS_BASE_PATHNAME}${path}` + : `/organizations/${organization.slug}/${LEGACY_ALERTS_BASE_PATHNAME}${path}` + ); +} From b80e6a384bc8b1947722a65090bd3bf9cca5c911 Mon Sep 17 00:00:00 2001 From: Malachi Willey Date: Fri, 14 Feb 2025 09:25:07 -0800 Subject: [PATCH 2/2] feat(nav): Update alert links from rest of app --- static/app/components/createAlertButton.tsx | 13 ++++++++++-- .../interfaces/crons/cronTimelineSection.tsx | 6 +++++- .../interfaces/uptime/uptimeDataSection.tsx | 9 +++++---- .../onboardingWizard/taskConfig.tsx | 11 ++++++++-- static/app/components/sidebar/index.tsx | 6 +++++- .../streamline/sidebar/detectorSection.tsx | 16 ++++++++++++--- .../teamInsights/teamAlertsTriggered.tsx | 11 ++++++++-- .../projectDetail/projectLatestAlerts.tsx | 20 ++++++++++++------- .../integrationAlertRules.tsx | 6 +++++- .../views/settings/projectAlerts/settings.tsx | 6 +++++- 10 files changed, 80 insertions(+), 24 deletions(-) diff --git a/static/app/components/createAlertButton.tsx b/static/app/components/createAlertButton.tsx index d413120e694c86..e811880ea22278 100644 --- a/static/app/components/createAlertButton.tsx +++ b/static/app/components/createAlertButton.tsx @@ -18,6 +18,7 @@ import type EventView from 'sentry/utils/discover/eventView'; import useApi from 'sentry/utils/useApi'; import useProjects from 'sentry/utils/useProjects'; import useRouter from 'sentry/utils/useRouter'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import type {AlertType, AlertWizardAlertNames} from 'sentry/views/alerts/wizard/options'; import { AlertWizardRuleTemplates, @@ -75,7 +76,10 @@ function CreateAlertFromViewButton({ : DEFAULT_WIZARD_TEMPLATE; const to = { - pathname: `/organizations/${organization.slug}/alerts/new/metric/`, + pathname: makeAlertsPathname({ + path: '/new/metric/', + organization, + }), query: { ...queryParams, createFromDiscover: true, @@ -143,7 +147,12 @@ export default function CreateAlertButton({ if (alertOption) { params.append('alert_option', alertOption); } - return `/organizations/${organization.slug}/alerts/wizard/?${params.toString()}`; + return ( + makeAlertsPathname({ + path: '/wizard/', + organization, + }) + `?${params.toString()}` + ); }; function handleClickWithoutProject(event: React.MouseEvent) { diff --git a/static/app/components/events/interfaces/crons/cronTimelineSection.tsx b/static/app/components/events/interfaces/crons/cronTimelineSection.tsx index c9ea45e6b49257..76416400e23ab1 100644 --- a/static/app/components/events/interfaces/crons/cronTimelineSection.tsx +++ b/static/app/components/events/interfaces/crons/cronTimelineSection.tsx @@ -23,6 +23,7 @@ import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {useDimensions} from 'sentry/utils/useDimensions'; import {useLocation} from 'sentry/utils/useLocation'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import {SectionKey} from 'sentry/views/issueDetails/streamline/context'; import {InterimSection} from 'sentry/views/issueDetails/streamline/interimSection'; import {ResolutionSelector} from 'sentry/views/monitors/components/overviewTimeline/resolutionSelector'; @@ -77,7 +78,10 @@ export function CronTimelineSection({event, organization, project}: Props) { size="xs" icon={} to={{ - pathname: `/organizations/${organization.slug}/alerts/rules/crons/${project.slug}/${monitorSlug}/details/`, + pathname: makeAlertsPathname({ + path: `/rules/crons/${project.slug}/${monitorSlug}/details/`, + organization, + }), query: {environment}, }} > diff --git a/static/app/components/events/interfaces/uptime/uptimeDataSection.tsx b/static/app/components/events/interfaces/uptime/uptimeDataSection.tsx index 67bdded595cec2..a3a275cb305114 100644 --- a/static/app/components/events/interfaces/uptime/uptimeDataSection.tsx +++ b/static/app/components/events/interfaces/uptime/uptimeDataSection.tsx @@ -25,11 +25,11 @@ import type {Event} from 'sentry/types/event'; import {type Group, GroupActivityType, GroupStatus} from 'sentry/types/group'; import type {Project} from 'sentry/types/project'; import {defined} from 'sentry/utils'; -import normalizeUrl from 'sentry/utils/url/normalizeUrl'; import {useDebouncedValue} from 'sentry/utils/useDebouncedValue'; import {useDimensions} from 'sentry/utils/useDimensions'; import {useLocation} from 'sentry/utils/useLocation'; import useOrganization from 'sentry/utils/useOrganization'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import { checkStatusPrecedent, statusToText, @@ -133,9 +133,10 @@ export function UptimeDataSection({group, event, project}: Props) { } size="xs" - to={normalizeUrl( - `/organizations/${organization.slug}/alerts/rules/uptime/${project.slug}/${alertRuleId}/details/` - )} + to={makeAlertsPathname({ + path: `/rules/uptime/${project.slug}/${alertRuleId}/details/`, + organization, + })} > {t('Uptime Alert Rule')} diff --git a/static/app/components/onboardingWizard/taskConfig.tsx b/static/app/components/onboardingWizard/taskConfig.tsx index d616b765529dee..5336c654581c1c 100644 --- a/static/app/components/onboardingWizard/taskConfig.tsx +++ b/static/app/components/onboardingWizard/taskConfig.tsx @@ -22,6 +22,7 @@ import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {isDemoModeEnabled} from 'sentry/utils/demoMode'; import {getDemoWalkthroughTasks} from 'sentry/utils/demoMode/guides'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import {getPerformanceBaseUrl} from 'sentry/views/performance/utils'; import {makeReplaysPathname} from 'sentry/views/replays/pathnames'; @@ -47,12 +48,18 @@ type Options = { function getIssueAlertUrl({projects, organization}: Options) { if (!projects || !projects.length) { - return `/organizations/${organization.slug}/alerts/rules/`; + return makeAlertsPathname({ + path: '/rules/', + organization, + }); } // pick the first project with events if we have that, otherwise just pick the first project const firstProjectWithEvents = projects.find(project => !!project.firstEvent); const project = firstProjectWithEvents ?? projects[0]!; - return `/organizations/${organization.slug}/alerts/${project.slug}/wizard/`; + return makeAlertsPathname({ + path: `/${project.slug}/wizard/`, + organization, + }); } function getOnboardingInstructionsUrl({projects, organization}: Options) { diff --git a/static/app/components/sidebar/index.tsx b/static/app/components/sidebar/index.tsx index 7b9d40d23be03a..abd41b0041ec29 100644 --- a/static/app/components/sidebar/index.tsx +++ b/static/app/components/sidebar/index.tsx @@ -55,6 +55,7 @@ import useMedia from 'sentry/utils/useMedia'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; import useProjects from 'sentry/utils/useProjects'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import {MODULE_BASE_URLS} from 'sentry/views/insights/common/utils/useModuleURL'; import { AI_LANDING_SUB_PATH, @@ -328,7 +329,10 @@ function Sidebar() { {...sidebarItemProps} icon={} label={t('Alerts')} - to={`/organizations/${organization.slug}/alerts/rules/`} + to={makeAlertsPathname({ + path: '/rules/', + organization, + })} id="alerts" /> ); diff --git a/static/app/views/issueDetails/streamline/sidebar/detectorSection.tsx b/static/app/views/issueDetails/streamline/sidebar/detectorSection.tsx index 5976441a014c16..f6e57e60197242 100644 --- a/static/app/views/issueDetails/streamline/sidebar/detectorSection.tsx +++ b/static/app/views/issueDetails/streamline/sidebar/detectorSection.tsx @@ -8,6 +8,7 @@ import type {Group} from 'sentry/types/group'; import type {Organization} from 'sentry/types/organization'; import type {Project} from 'sentry/types/project'; import {getConfigForIssueType} from 'sentry/utils/issueTypeConfig'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import {useIssueDetails} from 'sentry/views/issueDetails/streamline/context'; import {SidebarSectionTitle} from 'sentry/views/issueDetails/streamline/sidebar/sidebar'; @@ -38,7 +39,10 @@ export function getDetectorDetails({ return { detectorType: 'metric_alert', detectorId: metricAlertRuleId, - detectorPath: `/organizations/${organization.slug}/alerts/rules/details/${metricAlertRuleId}/`, + detectorPath: makeAlertsPathname({ + path: `/rules/details/${metricAlertRuleId}/`, + organization, + }), // TODO(issues): We can probably enrich this description with details from the alert itself. description: t( 'This issue was created by a metric alert detector. View the detector details to learn more.' @@ -53,7 +57,10 @@ export function getDetectorDetails({ detectorType: 'cron_monitor', detectorId: cronId, detectorSlug: cronSlug, - detectorPath: `/organizations/${organization.slug}/alerts/rules/crons/${project.slug}/${cronSlug}/details/`, + detectorPath: makeAlertsPathname({ + path: `/rules/crons/${project.slug}/${cronSlug}/details/`, + organization, + }), description: t( 'This issue was created by a cron monitor. View the monitor details to learn more.' ), @@ -65,7 +72,10 @@ export function getDetectorDetails({ return { detectorType: 'uptime_monitor', detectorId: uptimeAlertRuleId, - detectorPath: `/organizations/${organization.slug}/alerts/rules/uptime/${project.slug}/${uptimeAlertRuleId}/details/`, + detectorPath: makeAlertsPathname({ + path: `/rules/uptime/${project.slug}/${uptimeAlertRuleId}/details/`, + organization, + }), // TODO(issues): Update this to mention detectors when that language is user-facing description: t( 'This issue was created by an uptime monitoring alert rule after detecting 3 consecutive failed checks.' diff --git a/static/app/views/organizationStats/teamInsights/teamAlertsTriggered.tsx b/static/app/views/organizationStats/teamInsights/teamAlertsTriggered.tsx index 7f228d1a5d86bb..43ec651eb31f43 100644 --- a/static/app/views/organizationStats/teamInsights/teamAlertsTriggered.tsx +++ b/static/app/views/organizationStats/teamInsights/teamAlertsTriggered.tsx @@ -19,6 +19,7 @@ import type {Project} from 'sentry/types/project'; import {formatPercentage} from 'sentry/utils/number/formatPercentage'; import {useApiQuery} from 'sentry/utils/queryClient'; import type {ColorOrAlias} from 'sentry/utils/theme'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import type {MetricRule} from 'sentry/views/alerts/rules/metric/types'; import {ProjectBadge, ProjectBadgeContainer} from './styles'; @@ -154,7 +155,10 @@ function TeamAlertsTriggered({ {t('Create Alert')} @@ -182,7 +186,10 @@ function TeamAlertsTriggered({ {rule.name} diff --git a/static/app/views/projectDetail/projectLatestAlerts.tsx b/static/app/views/projectDetail/projectLatestAlerts.tsx index 3788fe369660bd..dcd15a2c1d7c10 100644 --- a/static/app/views/projectDetail/projectLatestAlerts.tsx +++ b/static/app/views/projectDetail/projectLatestAlerts.tsx @@ -15,6 +15,8 @@ import {t} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Organization} from 'sentry/types/organization'; import {useApiQuery} from 'sentry/utils/queryClient'; +import useOrganization from 'sentry/utils/useOrganization'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import type {Incident} from 'sentry/views/alerts/types'; import {IncidentStatus} from 'sentry/views/alerts/types'; @@ -25,10 +27,10 @@ const PLACEHOLDER_AND_EMPTY_HEIGHT = '172px'; interface AlertRowProps { alert: Incident; - orgSlug: string; } -function AlertRow({alert, orgSlug}: AlertRowProps) { +function AlertRow({alert}: AlertRowProps) { + const organization = useOrganization(); const {status, identifier, title, dateClosed, dateStarted} = alert; const isResolved = status === IncidentStatus.CLOSED; const isWarning = status === IncidentStatus.WARNING; @@ -40,7 +42,10 @@ function AlertRow({alert, orgSlug}: AlertRowProps) { return ( @@ -154,9 +159,7 @@ function ProjectLatestAlerts({ return alertsUnresolvedAndResolved .slice(0, 3) - .map(alert => ( - - )); + .map(alert => ); } return ( @@ -166,7 +169,10 @@ function ProjectLatestAlerts({ {/* as this is a link to latest alerts, we want to only preserve project and environment */} {t('Add Alert Rule')} diff --git a/static/app/views/settings/projectAlerts/settings.tsx b/static/app/views/settings/projectAlerts/settings.tsx index e6d0816e12584f..be02595a3e0db8 100644 --- a/static/app/views/settings/projectAlerts/settings.tsx +++ b/static/app/views/settings/projectAlerts/settings.tsx @@ -19,6 +19,7 @@ import type {ApiQueryKey} from 'sentry/utils/queryClient'; import {setApiQueryData, useApiQuery, useQueryClient} from 'sentry/utils/queryClient'; import routeTitleGen from 'sentry/utils/routeTitle'; import useOrganization from 'sentry/utils/useOrganization'; +import {makeAlertsPathname} from 'sentry/views/alerts/pathnames'; import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader'; import {ProjectPermissionAlert} from 'sentry/views/settings/project/projectPermissionAlert'; @@ -107,7 +108,10 @@ function ProjectAlertSettings({canEditRule, params}: ProjectAlertSettingsProps) action={