diff --git a/static/app/views/detectors/components/forms/uptime/formatUptimeUrl.spec.ts b/static/app/views/detectors/components/forms/uptime/formatUptimeUrl.spec.ts
new file mode 100644
index 00000000000000..216839011e4805
--- /dev/null
+++ b/static/app/views/detectors/components/forms/uptime/formatUptimeUrl.spec.ts
@@ -0,0 +1,17 @@
+import {formatUptimeUrl} from 'sentry/views/detectors/components/forms/uptime/formatUptimeUrl';
+
+describe('formatUptimeUrl', () => {
+ it('returns the host when the URL has no path', () => {
+ expect(formatUptimeUrl('https://example.com')).toBe('example.com');
+ });
+
+ it('includes the path and strips a trailing slash', () => {
+ expect(formatUptimeUrl('https://example.com/health/check/')).toBe(
+ 'example.com/health/check'
+ );
+ });
+
+ it('returns null for invalid URLs', () => {
+ expect(formatUptimeUrl('not-a-url')).toBeNull();
+ });
+});
diff --git a/static/app/views/detectors/components/forms/uptime/formatUptimeUrl.ts b/static/app/views/detectors/components/forms/uptime/formatUptimeUrl.ts
new file mode 100644
index 00000000000000..a46287d0d2d339
--- /dev/null
+++ b/static/app/views/detectors/components/forms/uptime/formatUptimeUrl.ts
@@ -0,0 +1,15 @@
+/**
+ * Takes a full URL used by the uptime detector and formats it nicely for display purposes
+ *
+ * https://example.com/health/check/ -> example.com/health/check
+ */
+export function formatUptimeUrl(url: string): string | null {
+ const parsedUrl = URL.parse(url);
+ if (!parsedUrl?.hostname) {
+ return null;
+ }
+
+ const path = parsedUrl.pathname === '/' ? '' : parsedUrl.pathname;
+
+ return `${parsedUrl.hostname}${path}`.replace(/\/$/, '');
+}
diff --git a/static/app/views/detectors/components/forms/uptime/index.tsx b/static/app/views/detectors/components/forms/uptime/index.tsx
index 8cc3a603631564..f572af43fddf11 100644
--- a/static/app/views/detectors/components/forms/uptime/index.tsx
+++ b/static/app/views/detectors/components/forms/uptime/index.tsx
@@ -27,9 +27,11 @@ import {
uptimeFormDataToEndpointPayload,
uptimeSavedDetectorToFormData,
} from 'sentry/views/detectors/components/forms/uptime/fields';
+import {formatUptimeUrl} from 'sentry/views/detectors/components/forms/uptime/formatUptimeUrl';
import {PreviewSection} from 'sentry/views/detectors/components/forms/uptime/previewSection';
import {UptimeRegionWarning} from 'sentry/views/detectors/components/forms/uptime/regionWarning';
import {UptimeDetectorResolveSection} from 'sentry/views/detectors/components/forms/uptime/resolve';
+import {UptimeIssuePreview} from 'sentry/views/detectors/components/forms/uptime/uptimeIssuePreview';
import {UptimeDetectorVerificationSection} from 'sentry/views/detectors/components/forms/uptime/verification';
const ENVIRONMENT_CONFIG: EnvironmentConfig = {
@@ -49,14 +51,11 @@ function UptimeDetectorForm() {
return null;
}
- const parsedUrl = URL.parse(url);
- if (!parsedUrl) {
+ const urlName = formatUptimeUrl(url);
+ if (!urlName) {
return null;
}
- const path = parsedUrl.pathname === '/' ? '' : parsedUrl.pathname;
- const urlName = `${parsedUrl.hostname}${path}`.replace(/\/$/, '');
-
return t('Uptime check for %s', urlName);
});
@@ -70,6 +69,7 @@ function UptimeDetectorForm() {
+
);
diff --git a/static/app/views/detectors/components/forms/uptime/uptimeIssuePreview.tsx b/static/app/views/detectors/components/forms/uptime/uptimeIssuePreview.tsx
new file mode 100644
index 00000000000000..414aaabeccee43
--- /dev/null
+++ b/static/app/views/detectors/components/forms/uptime/uptimeIssuePreview.tsx
@@ -0,0 +1,43 @@
+import {t} from 'sentry/locale';
+import {DetectorIssuePreview} from 'sentry/views/detectors/components/forms/common/detectorIssuePreview';
+import {IssuePreviewSection} from 'sentry/views/detectors/components/forms/common/issuePreviewSection';
+import {ownerToActor} from 'sentry/views/detectors/components/forms/common/ownerToActor';
+import {useDetectorFormContext} from 'sentry/views/detectors/components/forms/context';
+import {useUptimeDetectorFormField} from 'sentry/views/detectors/components/forms/uptime/fields';
+import {formatUptimeUrl} from 'sentry/views/detectors/components/forms/uptime/formatUptimeUrl';
+
+const FALLBACK_ISSUE_TITLE = t('Downtime detected for …');
+const SUBTITLE = t('Your monitored domain is down');
+
+function useUptimeIssueTitle() {
+ const url = useUptimeDetectorFormField('url');
+
+ if (!url) {
+ return FALLBACK_ISSUE_TITLE;
+ }
+
+ const displayUrl = formatUptimeUrl(url);
+ if (!displayUrl) {
+ return FALLBACK_ISSUE_TITLE;
+ }
+
+ return t('Downtime detected for %s', displayUrl);
+}
+
+export function UptimeIssuePreview({step}: {step?: number}) {
+ const owner = useUptimeDetectorFormField('owner');
+ const issueTitle = useUptimeIssueTitle();
+ const assignee = ownerToActor(owner);
+ const {project} = useDetectorFormContext();
+
+ return (
+
+
+
+ );
+}
diff --git a/static/app/views/detectors/new-setting.spec.tsx b/static/app/views/detectors/new-setting.spec.tsx
index 434f16d58d952c..4432582039ba67 100644
--- a/static/app/views/detectors/new-setting.spec.tsx
+++ b/static/app/views/detectors/new-setting.spec.tsx
@@ -930,6 +930,11 @@ describe('DetectorEdit', () => {
await userEvent.click(bodyInput);
await userEvent.paste('{"test": "data"}');
+ // Issue preview reflects the URL
+ expect(
+ screen.getByText('Downtime detected for uptime.example.com')
+ ).toBeInTheDocument();
+
await selectEvent.openMenu(screen.getByLabelText('Select Environment'));
expect(
screen.queryByRole('menuitemradio', {name: 'All Environments'})