From 79163fc3b76cf21c4d49005849cd2f78579d2218 Mon Sep 17 00:00:00 2001
From: Nate Moore
Date: Fri, 18 Apr 2025 16:06:34 -0500
Subject: [PATCH 01/37] giant demo mess
---
.../form/control/priorityControl.tsx | 2 +
.../workflowEngine/gridCell/actionCell.tsx | 2 -
.../gridCell/connectionCell.tsx | 2 +-
.../workflowEngine/gridCell/index.stories.tsx | 4 +-
.../workflowEngine/layout/actions.tsx | 7 +-
.../workflowEngine/ui/container.tsx | 2 +-
.../components/workflowEngine/ui/footer.tsx | 1 +
.../components/workflowEngine/ui/section.tsx | 14 +-
static/app/plugins/components/pluginIcon.tsx | 2 +-
static/app/types/workflowEngine/detectors.tsx | 5 +-
.../automations/components/automationForm.tsx | 39 ++
.../components/automationListRow.tsx | 6 +-
.../components/automationListTable.tsx | 5 +-
static/app/views/automations/detail.tsx | 4 +-
static/app/views/automations/hooks/index.ts | 70 ++
static/app/views/automations/hooks/utils.ts | 22 +
static/app/views/automations/list.tsx | 2 +-
static/app/views/automations/new.tsx | 177 +++++
.../detectors/components/detectorListRow.tsx | 8 +-
.../components/detectorListTable.tsx | 7 +-
.../detectors/components/detectorTypeForm.tsx | 2 -
.../detectors/components/forms/metric.tsx | 604 ++++++++++++++++++
static/app/views/detectors/edit.tsx | 10 +-
static/app/views/detectors/hooks/index.ts | 223 +++++++
static/app/views/detectors/list.tsx | 8 +-
static/app/views/detectors/new-settings.tsx | 7 +-
tests/js/fixtures/automations.ts | 14 +
tests/js/fixtures/dataConditions.ts | 21 +
tests/js/fixtures/detectors.ts | 34 +
29 files changed, 1268 insertions(+), 36 deletions(-)
create mode 100644 static/app/views/automations/hooks/index.ts
create mode 100644 static/app/views/automations/hooks/utils.ts
create mode 100644 static/app/views/detectors/components/forms/metric.tsx
create mode 100644 static/app/views/detectors/hooks/index.ts
create mode 100644 tests/js/fixtures/automations.ts
create mode 100644 tests/js/fixtures/dataConditions.ts
create mode 100644 tests/js/fixtures/detectors.ts
diff --git a/static/app/components/workflowEngine/form/control/priorityControl.tsx b/static/app/components/workflowEngine/form/control/priorityControl.tsx
index a50f600c2a5aad..4004f0af6e4106 100644
--- a/static/app/components/workflowEngine/form/control/priorityControl.tsx
+++ b/static/app/components/workflowEngine/form/control/priorityControl.tsx
@@ -76,6 +76,7 @@ export default function PriorityControl({
flexibleControlStateSize
size="sm"
suffix="s"
+ placeholder="0"
// empty string required to keep this as a controlled input
value={thresholds[PriorityLevel.MEDIUM] ?? ''}
onChange={threshold => setMediumThreshold(Number(threshold))}
@@ -96,6 +97,7 @@ export default function PriorityControl({
flexibleControlStateSize
size="sm"
suffix="s"
+ placeholder="0"
// empty string required to keep this as a controlled input
value={thresholds[PriorityLevel.HIGH] ?? ''}
onChange={threshold => setHighThreshold(Number(threshold))}
diff --git a/static/app/components/workflowEngine/gridCell/actionCell.tsx b/static/app/components/workflowEngine/gridCell/actionCell.tsx
index 5ef0e281397436..bfcae7178bc4dd 100644
--- a/static/app/components/workflowEngine/gridCell/actionCell.tsx
+++ b/static/app/components/workflowEngine/gridCell/actionCell.tsx
@@ -31,12 +31,10 @@ export function ActionCell({actions, disabled}: ActionCellProps) {
);
}
-
const actionsList = actions
.map(action => ActionMetadata[action]?.name)
.filter(x => x)
.join(', ');
-
return (
diff --git a/static/app/components/workflowEngine/gridCell/connectionCell.tsx b/static/app/components/workflowEngine/gridCell/connectionCell.tsx
index c7f06722d7c3e7..4edf617482674e 100644
--- a/static/app/components/workflowEngine/gridCell/connectionCell.tsx
+++ b/static/app/components/workflowEngine/gridCell/connectionCell.tsx
@@ -32,7 +32,7 @@ const links: Record<
};
export function ConnectionCell({
- ids: items,
+ ids: items = [],
type,
disabled = false,
className,
diff --git a/static/app/components/workflowEngine/gridCell/index.stories.tsx b/static/app/components/workflowEngine/gridCell/index.stories.tsx
index a55f46da8bb7b1..4b84ceae394e7f 100644
--- a/static/app/components/workflowEngine/gridCell/index.stories.tsx
+++ b/static/app/components/workflowEngine/gridCell/index.stories.tsx
@@ -50,7 +50,7 @@ export default storyBook('Grid Cell Components', story => {
},
openIssues: 3,
creator: '1',
- type: 'trace',
+ type: 'uptime',
},
{
title: {
@@ -95,7 +95,7 @@ export default storyBook('Grid Cell Components', story => {
},
actions: [ActionType.SLACK, ActionType.DISCORD, ActionType.EMAIL],
creator: 'sentry',
- type: 'errors',
+ type: 'uptime',
timeAgo: null,
linkedItems: {
ids: [],
diff --git a/static/app/components/workflowEngine/layout/actions.tsx b/static/app/components/workflowEngine/layout/actions.tsx
index 822de1f4e48d7e..16b422b3fd32d0 100644
--- a/static/app/components/workflowEngine/layout/actions.tsx
+++ b/static/app/components/workflowEngine/layout/actions.tsx
@@ -1,7 +1,8 @@
import {createContext, useContext} from 'react';
-import {ButtonBar} from 'sentry/components/core/button/buttonBar';
+import {Flex} from 'sentry/components/container/flex';
import {HeaderActions} from 'sentry/components/layouts/thirds';
+import {space} from 'sentry/styles/space';
const ActionContext = createContext(undefined);
@@ -26,9 +27,7 @@ export function ActionsFromContext() {
}
return (
-
- {actions}
-
+ {actions}
);
}
diff --git a/static/app/components/workflowEngine/ui/container.tsx b/static/app/components/workflowEngine/ui/container.tsx
index e148b7baab8705..ba3f97789b5329 100644
--- a/static/app/components/workflowEngine/ui/container.tsx
+++ b/static/app/components/workflowEngine/ui/container.tsx
@@ -7,7 +7,7 @@ export const Container = styled('div')`
flex-direction: column;
gap: ${space(2)};
justify-content: flex-start;
- background-color: ${p => p.theme.backgroundSecondary};
+ background-color: ${p => p.theme.background};
border: 1px solid ${p => p.theme.translucentBorder};
border-radius: ${p => p.theme.borderRadius};
padding: ${space(1.5)};
diff --git a/static/app/components/workflowEngine/ui/footer.tsx b/static/app/components/workflowEngine/ui/footer.tsx
index 7e55d27edfcf80..f2bc96e3a31c26 100644
--- a/static/app/components/workflowEngine/ui/footer.tsx
+++ b/static/app/components/workflowEngine/ui/footer.tsx
@@ -5,6 +5,7 @@ import {space} from 'sentry/styles/space';
export const StickyFooter = styled('div')`
position: sticky;
margin-top: auto;
+ margin-bottom: -56px;
bottom: 0;
right: 0;
width: 100%;
diff --git a/static/app/components/workflowEngine/ui/section.tsx b/static/app/components/workflowEngine/ui/section.tsx
index f8f3c851b2ea2a..92e67a5df4e9fe 100644
--- a/static/app/components/workflowEngine/ui/section.tsx
+++ b/static/app/components/workflowEngine/ui/section.tsx
@@ -6,18 +6,28 @@ import {space} from 'sentry/styles/space';
type SectionProps = {
children: React.ReactNode;
title: string;
+ description?: string;
};
-export default function Section({children, title}: SectionProps) {
+export default function Section({children, title, description}: SectionProps) {
return (
{title}
+ {description && {description}}
{children}
);
}
-const SectionHeading = styled('h4')`
+export const SectionHeading = styled('h4')`
+ font-size: ${p => p.theme.fontSizeLarge};
+ font-weight: ${p => p.theme.fontWeightBold};
+ margin: 0;
+`;
+
+export const SectionDescription = styled('p')`
font-size: ${p => p.theme.fontSizeMedium};
+ font-weight: ${p => p.theme.fontWeightNormal};
+ color: ${p => p.theme.subText};
margin: 0;
`;
diff --git a/static/app/plugins/components/pluginIcon.tsx b/static/app/plugins/components/pluginIcon.tsx
index b2344e9167c85d..fdd8bd77d6d40d 100644
--- a/static/app/plugins/components/pluginIcon.tsx
+++ b/static/app/plugins/components/pluginIcon.tsx
@@ -69,7 +69,7 @@ const PLUGIN_ICONS = {
} satisfies Record;
export interface PluginIconProps extends React.RefAttributes {
- pluginId: string | keyof typeof PLUGIN_ICONS;
+ pluginId: keyof typeof PLUGIN_ICONS | (string & {});
/**
* @default 20
*/
diff --git a/static/app/types/workflowEngine/detectors.tsx b/static/app/types/workflowEngine/detectors.tsx
index 15de579d255d6e..491618af95e3a5 100644
--- a/static/app/types/workflowEngine/detectors.tsx
+++ b/static/app/types/workflowEngine/detectors.tsx
@@ -4,11 +4,12 @@ import type {
} from 'sentry/types/workflowEngine/dataConditions';
export type DetectorType =
- | 'metric'
+ | 'crons'
| 'errors'
+ | 'metric'
| 'performance'
- | 'trace'
| 'replay'
+ | 'trace'
| 'uptime';
interface NewDetector {
diff --git a/static/app/views/automations/components/automationForm.tsx b/static/app/views/automations/components/automationForm.tsx
index 313b6a18d0777e..f42840f4f23265 100644
--- a/static/app/views/automations/components/automationForm.tsx
+++ b/static/app/views/automations/components/automationForm.tsx
@@ -49,6 +49,45 @@ export default function AutomationForm() {
const title = useDocumentTitle();
const {state, actions} = useAutomationBuilderReducer();
const [model] = useState(() => new FormModel());
+ const [monitor1Connected, setMonitor1Connected] = useState(true);
+ const [monitor2Connected, setMonitor2Connected] = useState(true);
+
+ const data: MonitorsData[] = [
+ {
+ name: {
+ name: 'Error Grouping',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'errors',
+ createdBy: 'sentry',
+ connect: {connected: monitor1Connected, toggleConnected: setMonitor1Connected},
+ },
+ {
+ name: {
+ name: 'Error Grouping',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'metric',
+ createdBy: {
+ email: 'miahsu@sentry.io',
+ name: 'Mia Hsu',
+ username: 'f4ea91ef8dc34fe8a54b3732030fbf7b',
+ id: '3286015',
+ ip_address: '1.1.1.1',
+ },
+ connect: {connected: monitor2Connected, toggleConnected: setMonitor2Connected},
+ },
+ ];
useEffect(() => {
model.setValue('name', title);
diff --git a/static/app/views/automations/components/automationListRow.tsx b/static/app/views/automations/components/automationListRow.tsx
index dbbb2cb370c94b..85bfac1811ce1d 100644
--- a/static/app/views/automations/components/automationListRow.tsx
+++ b/static/app/views/automations/components/automationListRow.tsx
@@ -4,6 +4,7 @@ import styled from '@emotion/styled';
import {Flex} from 'sentry/components/container/flex';
import {Checkbox} from 'sentry/components/core/checkbox';
import InteractionStateLayer from 'sentry/components/interactionStateLayer';
+import {ProjectList} from 'sentry/components/projectList';
import {ActionCell} from 'sentry/components/workflowEngine/gridCell/actionCell';
import AutomationTitleCell from 'sentry/components/workflowEngine/gridCell/automationTitleCell';
import {ConnectionCell} from 'sentry/components/workflowEngine/gridCell/connectionCell';
@@ -18,7 +19,7 @@ type AutomationListRowProps = {
automation: Automation;
handleSelect: (id: string, checked: boolean) => void;
selected: boolean;
-};
+}
export function AutomationListRow({
automation,
@@ -51,6 +52,9 @@ export function AutomationListRow({
+
+
+
diff --git a/static/app/views/automations/components/automationListTable.tsx b/static/app/views/automations/components/automationListTable.tsx
index e2f83265b06528..24445a88ceb78a 100644
--- a/static/app/views/automations/components/automationListTable.tsx
+++ b/static/app/views/automations/components/automationListTable.tsx
@@ -43,6 +43,10 @@ function AutomationListTable() {
{t('Actions')}
+
+
+ {t('Projects')}
+
{t('Monitors')}
@@ -50,7 +54,6 @@ function AutomationListTable() {
{isLoading ? : null}
-
{automations.map(automation => (
+
{t('Last Triggered')}
@@ -78,7 +78,7 @@ function Details() {
-
+
);
}
diff --git a/static/app/views/automations/hooks/index.ts b/static/app/views/automations/hooks/index.ts
new file mode 100644
index 00000000000000..05b92425b38aab
--- /dev/null
+++ b/static/app/views/automations/hooks/index.ts
@@ -0,0 +1,70 @@
+import type {Automation, NewAutomation} from 'sentry/types/workflowEngine/automations';
+import {useApiQuery, useMutation, useQueryClient} from 'sentry/utils/queryClient';
+import useApi from 'sentry/utils/useApi';
+import useOrganization from 'sentry/utils/useOrganization';
+
+export interface UseAutomationsQueryOptions {
+ query?: string;
+ sort?: string;
+}
+export function useAutomationsQuery(_options: UseAutomationsQueryOptions = {}) {
+ const { slug } = useOrganization();
+
+ return useApiQuery([`/organizations/${slug}/workflows/`], {
+ staleTime: 0,
+ retry: false,
+ })
+}
+
+export const makeAutomationQueryKey = (
+ orgSlug: string,
+ automationId = ''
+): [url: string] => [`/organizations/${orgSlug}/workflows/${automationId}/`];
+
+export function useCreateAutomation(automation: NewAutomation) {
+ const org = useOrganization();
+
+ return useApiQuery(
+ [...makeAutomationQueryKey(org.slug), {method: 'POST', data: automation}],
+ {
+ staleTime: 0,
+ retry: false,
+ }
+ );
+}
+
+export function useAutomationQuery(automationId: string) {
+ const org = useOrganization();
+
+ return useApiQuery([...makeAutomationQueryKey(org.slug, automationId)], {
+ staleTime: 0,
+ retry: false,
+ });
+}
+
+export function useAutomationMutation(automation: Partial & {id: string}) {
+ const api = useApi({persistInFlight: true});
+ const queryClient = useQueryClient();
+ const org = useOrganization();
+ const queryKey = makeAutomationQueryKey(org.slug, automation.id);
+ return useMutation({
+ mutationFn: data =>
+ api.requestPromise(queryKey[0], {
+ method: 'PUT',
+ data,
+ }),
+ onSuccess: _ => {
+ queryClient.invalidateQueries({queryKey});
+ // setApiQueryData(
+ // queryClient,
+ // makeDetailedProjectQueryKey({
+ // orgSlug: organization.slug,
+ // projectSlug: project.slug,
+ // }),
+ // existingData => (updatedProject ? updatedProject : existingData)
+ // );
+ // return onSuccess?.(updatedProject);
+ },
+ onError: _ => {},
+ });
+}
diff --git a/static/app/views/automations/hooks/utils.ts b/static/app/views/automations/hooks/utils.ts
new file mode 100644
index 00000000000000..a442e1bb14292f
--- /dev/null
+++ b/static/app/views/automations/hooks/utils.ts
@@ -0,0 +1,22 @@
+import type {ActionType} from 'sentry/types/workflowEngine/actions';
+import type {Automation} from 'sentry/types/workflowEngine/automations';
+import {useDetectorQueriesByIds} from 'sentry/views/detectors/hooks';
+
+export function useAutomationActions(automation: Automation): ActionType[] {
+ return [
+ ...new Set(
+ automation.actionFilters
+ .flatMap(dataConditionGroup =>
+ dataConditionGroup.actions?.map(action => action.type)
+ )
+ .filter(x => x)
+ ),
+ ] as ActionType[];
+}
+
+export function useAutomationProjectIds(automation: Automation): string[] {
+ const queries = useDetectorQueriesByIds(automation.detectorIds);
+ return [
+ ...new Set(queries.map(query => query.data?.projectId).filter(x => x)),
+ ] as string[];
+}
diff --git a/static/app/views/automations/list.tsx b/static/app/views/automations/list.tsx
index ef1af8bc5d1e1c..9bd7dae88e8586 100644
--- a/static/app/views/automations/list.tsx
+++ b/static/app/views/automations/list.tsx
@@ -36,7 +36,7 @@ export default function AutomationsList() {
function TableHeader() {
return (
-
+
diff --git a/static/app/views/automations/new.tsx b/static/app/views/automations/new.tsx
index ac63b9a1c02090..433f6d807e0c29 100644
--- a/static/app/views/automations/new.tsx
+++ b/static/app/views/automations/new.tsx
@@ -21,6 +21,183 @@ import {makeAutomationBasePathname} from 'sentry/views/automations/pathnames';
export default function AutomationNew() {
const organization = useOrganization();
useWorkflowEngineFeatureGate({redirect: true});
+ const [monitor1Connected, setMonitor1Connected] = useState(true);
+ const [monitor2Connected, setMonitor2Connected] = useState(true);
+ const [monitor3Connected, setMonitor3Connected] = useState(false);
+ const [monitor4Connected, setMonitor4Connected] = useState(false);
+ const [monitor5Connected, setMonitor5Connected] = useState(false);
+ const [monitor6Connected, setMonitor6Connected] = useState(false);
+ const [monitor7Connected, setMonitor7Connected] = useState(false);
+ const [monitor8Connected, setMonitor8Connected] = useState(false);
+ const [monitor9Connected, setMonitor9Connected] = useState(false);
+ const [monitor10Connected, setMonitor10Connected] = useState(false);
+
+ const data: MonitorsData[] = [
+ {
+ name: {
+ name: 'Error Grouping',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'errors',
+ createdBy: 'sentry',
+ connect: {connected: monitor1Connected, toggleConnected: setMonitor1Connected},
+ },
+ {
+ name: {
+ name: 'Endpoint Regression',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'metric',
+ createdBy: {
+ email: 'miahsu@sentry.io',
+ name: 'Mia Hsu',
+ username: 'f4ea91ef8dc34fe8a54b3732030fbf7b',
+ id: '3286015',
+ ip_address: '1.1.1.1',
+ },
+ connect: {connected: monitor2Connected, toggleConnected: setMonitor2Connected},
+ },
+ {
+ name: {
+ name: 'Consecutive DB Queries',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'trace',
+ createdBy: 'sentry',
+ connect: {connected: monitor3Connected, toggleConnected: setMonitor3Connected},
+ },
+ {
+ name: {
+ name: 'Consecutive HTTP',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ type: 'trace',
+ createdBy: 'sentry',
+ connect: {connected: monitor4Connected, toggleConnected: setMonitor4Connected},
+ },
+ {
+ name: {
+ name: 'N+1 API Call',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'trace',
+ createdBy: {
+ email: 'cathy.teng@sentry.io',
+ name: 'Cathy Teng',
+ id: '2120569',
+ ip_address: '1.1.1.1',
+ },
+ connect: {connected: monitor5Connected, toggleConnected: setMonitor5Connected},
+ },
+ {
+ name: {
+ name: 'Slow DB Query',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ type: 'trace',
+ createdBy: {
+ email: 'michelle.fu@sentry.io',
+ name: 'Michelle Fu',
+ id: '2787837',
+ ip_address: '1.1.1.1',
+ },
+ connect: {connected: monitor6Connected, toggleConnected: setMonitor6Connected},
+ },
+ {
+ name: {
+ name: 'Uncompressed Asset',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'errors',
+ createdBy: {
+ email: 'christina.long@sentry.io',
+ name: 'Christina Long',
+ id: '3284085',
+ ip_address: '1.1.1.1',
+ },
+ connect: {connected: monitor7Connected, toggleConnected: setMonitor7Connected},
+ },
+ {
+ name: {
+ name: 'Rage Click',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'replay',
+ createdBy: {
+ email: 'raj.joshi@sentry.io',
+ name: 'Raj Joshi',
+ id: '3068985',
+ ip_address: '1.1.1.1',
+ },
+ connect: {connected: monitor8Connected, toggleConnected: setMonitor8Connected},
+ },
+ {
+ name: {
+ name: 'Error Grouping',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'errors',
+ createdBy: 'sentry',
+ connect: {connected: monitor9Connected, toggleConnected: setMonitor9Connected},
+ },
+ {
+ name: {
+ name: 'Error Grouping',
+ project: {
+ slug: 'javascript',
+ platform: 'javascript',
+ },
+ link: '/issues/1',
+ },
+ lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
+ type: 'errors',
+ createdBy: 'sentry',
+ connect: {connected: monitor10Connected, toggleConnected: setMonitor10Connected},
+ },
+ ];
return (
diff --git a/static/app/views/detectors/components/detectorListRow.tsx b/static/app/views/detectors/components/detectorListRow.tsx
index 0c6c0d8a2f9a24..8965b0ccade67e 100644
--- a/static/app/views/detectors/components/detectorListRow.tsx
+++ b/static/app/views/detectors/components/detectorListRow.tsx
@@ -23,7 +23,7 @@ interface DetectorListRowProps {
}
export function DetectorListRow({
- detector: {workflowIds, id, name, disabled, projectId},
+ detector: {workflowIds, createdBy, id, projectId, name, disabled, type},
handleSelect,
selected,
}: DetectorListRowProps) {
@@ -51,7 +51,7 @@ export function DetectorListRow({
-
+
-
-
+
+
diff --git a/static/app/views/detectors/components/detectorListTable.tsx b/static/app/views/detectors/components/detectorListTable.tsx
index 36a06e470ea4cf..1827e486273cad 100644
--- a/static/app/views/detectors/components/detectorListTable.tsx
+++ b/static/app/views/detectors/components/detectorListTable.tsx
@@ -40,13 +40,13 @@ function DetectorListTable({detectors}: DetectorListTableProps) {
{t('Type')}
-
+
{t('Last Issue')}
-
+
- {t('Owner')}
+ {t('Creator')}
@@ -86,6 +86,7 @@ const StyledPanelHeader = styled(PanelHeader)`
min-height: 40px;
align-items: center;
display: grid;
+ text-transform: none;
.type,
.owner,
diff --git a/static/app/views/detectors/components/detectorTypeForm.tsx b/static/app/views/detectors/components/detectorTypeForm.tsx
index fc9ab07b6428d0..6ace886117d8c4 100644
--- a/static/app/views/detectors/components/detectorTypeForm.tsx
+++ b/static/app/views/detectors/components/detectorTypeForm.tsx
@@ -9,7 +9,6 @@ import SentryProjectSelectorField from 'sentry/components/forms/fields/sentryPro
import Form from 'sentry/components/forms/form';
import FormModel from 'sentry/components/forms/model';
import {useDocumentTitle} from 'sentry/components/sentryDocumentTitle';
-import {DebugForm} from 'sentry/components/workflowEngine/form/debug';
import {useFormField} from 'sentry/components/workflowEngine/form/hooks';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
@@ -74,7 +73,6 @@ export function DetectorTypeForm() {
-
diff --git a/static/app/views/detectors/components/forms/metric.tsx b/static/app/views/detectors/components/forms/metric.tsx
new file mode 100644
index 00000000000000..ef3fa55ba84fcd
--- /dev/null
+++ b/static/app/views/detectors/components/forms/metric.tsx
@@ -0,0 +1,604 @@
+import {useMemo} from 'react';
+import styled from '@emotion/styled';
+
+import {Flex} from 'sentry/components/container/flex';
+import {Button} from 'sentry/components/core/button';
+import NumberField from 'sentry/components/forms/fields/numberField';
+import SegmentedRadioField from 'sentry/components/forms/fields/segmentedRadioField';
+import SelectField from 'sentry/components/forms/fields/selectField';
+import SentryMemberTeamSelectorField from 'sentry/components/forms/fields/sentryMemberTeamSelectorField';
+import Form from 'sentry/components/forms/form';
+import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder';
+import type {FilterKeySection} from 'sentry/components/searchQueryBuilder/types';
+import PriorityControl from 'sentry/components/workflowEngine/form/control/priorityControl';
+import {useFormField} from 'sentry/components/workflowEngine/form/hooks';
+import {Container} from 'sentry/components/workflowEngine/ui/container';
+import Section from 'sentry/components/workflowEngine/ui/section';
+import {IconAdd} from 'sentry/icons';
+import {t} from 'sentry/locale';
+import {space} from 'sentry/styles/space';
+import type {TagCollection} from 'sentry/types/group';
+import {
+ ALLOWED_EXPLORE_VISUALIZE_AGGREGATES,
+ FieldKey,
+ FieldKind,
+ MobileVital,
+ WebVital,
+} from 'sentry/utils/fields';
+
+type MetricDetectorKind = 'threshold' | 'change' | 'dynamic';
+
+export function MetricDetectorForm() {
+ return (
+
+ );
+}
+
+function ResolveSection() {
+ const kind = useFormField('kind')!;
+
+ return (
+
+
+ {kind !== 'dynamic' && (
+
+ )}
+
+
+ );
+}
+
+function AutomateSection() {
+ // const {openDrawer, isDrawerOpen} = useDrawer();
+ // const showDetails = () => {}
+ return (
+
+
+ }
+ >
+ Connect Automations
+
+
+
+ );
+}
+
+function AssignSection() {
+ return (
+
+
+
+ );
+}
+
+function PrioritizeSection() {
+ const kind = useFormField('kind')!;
+ return (
+
+
+ {kind !== 'dynamic' && }
+
+
+ );
+}
+
+function DetectSection() {
+ const kind = useFormField('kind')!;
+ const aggregateOptions: Array<[string, string]> = useMemo(() => {
+ return ALLOWED_EXPLORE_VISUALIZE_AGGREGATES.map(aggregate => {
+ return [aggregate, aggregate];
+ });
+ }, []);
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+ {kind !== 'dynamic' && (
+
+ {(!kind || kind === 'threshold') && (
+
+ An issue will be created when query value exceeds:
+
+
+ )}
+ {kind === 'change' && (
+
+ An issue will be created when query value is:
+
+
+ percent
+
+ than the previous
+
+
+
+ )}
+
+ )}
+
+
+ );
+}
+
+function OwnerField() {
+ return (
+
+ );
+}
+
+const FormStack = styled(Flex)`
+ max-width: ${p => p.theme.breakpoints.xlarge};
+ flex-direction: column;
+ gap: ${space(4)};
+ padding: ${space(4)};
+`;
+
+const FirstRow = styled('div')`
+ display: grid;
+ grid-template-columns: 1fr 2fr;
+ gap: ${space(1)};
+ border-bottom: 1px solid ${p => p.theme.border};
+`;
+
+const DetectColumn = styled(Flex)`
+ flex-grow: 1;
+`;
+
+const StyledSelectField = styled(SelectField)`
+ width: 180px;
+ padding: 0;
+ margin: 0;
+
+ > div {
+ padding-left: 0;
+ }
+`;
+
+const StyledMemberTeamSelectorField = styled(SentryMemberTeamSelectorField)`
+ padding-left: 0;
+`;
+
+const VisualizeField = styled(SelectField)`
+ flex: 2;
+ padding-left: 0;
+ padding-right: 0;
+ margin-left: 0;
+ border-bottom: none;
+`;
+
+const ChartContainer = styled('div')`
+ background: ${p => p.theme.background};
+ width: 100%;
+ border-bottom: 1px solid ${p => p.theme.border};
+ padding: 24px 32px 16px 32px;
+`;
+
+const AggregateField = styled(SelectField)`
+ width: 120px;
+ margin-top: auto;
+ padding-top: 0;
+ padding-left: 0;
+ padding-right: 0;
+
+ > div {
+ padding-left: 0;
+ }
+`;
+
+const DirectionField = styled(SelectField)`
+ width: 16ch;
+ padding: 0;
+ margin: 0;
+
+ > div {
+ padding-left: 0;
+ }
+`;
+
+const MonitorKindField = styled(SegmentedRadioField)`
+ padding-left: 0;
+ padding-block: ${space(1)};
+ border-bottom: 1px solid ${p => p.theme.border};
+ max-width: 840px;
+
+ > div {
+ padding: 0;
+ }
+`;
+const ThresholdField = styled(NumberField)`
+ padding: 0;
+ margin: 0;
+ border: none;
+
+ > div {
+ padding: 0;
+ width: 10ch;
+ }
+`;
+
+const ChangePercentField = styled(NumberField)`
+ padding: 0;
+ margin: 0;
+ border: none;
+
+ > div {
+ padding: 0;
+ max-width: 10ch;
+ }
+`;
+
+const MutedText = styled('p')`
+ color: ${p => p.theme.text};
+ padding-top: ${space(1)};
+ margin-bottom: ${space(1)};
+ border-top: 1px solid ${p => p.theme.border};
+`;
+
+function FilterField() {
+ return (
+
+ Filter
+
+
+ );
+}
+
+const getTagValues = (): Promise => {
+ return new Promise(resolve => {
+ setTimeout(() => {
+ resolve(['foo', 'bar', 'baz']);
+ }, 500);
+ });
+};
+
+const FILTER_KEYS: TagCollection = {
+ [FieldKey.ASSIGNED]: {
+ key: FieldKey.ASSIGNED,
+ name: 'Assigned To',
+ kind: FieldKind.FIELD,
+ predefined: true,
+ values: [
+ {
+ title: 'Suggested',
+ type: 'header',
+ icon: null,
+ children: [{value: 'me'}, {value: 'unassigned'}],
+ },
+ {
+ title: 'All',
+ type: 'header',
+ icon: null,
+ children: [{value: 'person1@sentry.io'}, {value: 'person2@sentry.io'}],
+ },
+ ],
+ },
+ [FieldKey.BROWSER_NAME]: {
+ key: FieldKey.BROWSER_NAME,
+ name: 'Browser Name',
+ kind: FieldKind.FIELD,
+ predefined: true,
+ values: ['Chrome', 'Firefox', 'Safari', 'Edge', 'Internet Explorer', 'Opera 1,2'],
+ },
+ [FieldKey.IS]: {
+ key: FieldKey.IS,
+ name: 'is',
+ predefined: true,
+ values: ['resolved', 'unresolved', 'ignored'],
+ },
+ [FieldKey.LAST_SEEN]: {
+ key: FieldKey.LAST_SEEN,
+ name: 'lastSeen',
+ kind: FieldKind.FIELD,
+ },
+ [FieldKey.TIMES_SEEN]: {
+ key: FieldKey.TIMES_SEEN,
+ name: 'timesSeen',
+ kind: FieldKind.FIELD,
+ },
+ [WebVital.LCP]: {
+ key: WebVital.LCP,
+ name: 'lcp',
+ kind: FieldKind.FIELD,
+ },
+ [MobileVital.FRAMES_SLOW_RATE]: {
+ key: MobileVital.FRAMES_SLOW_RATE,
+ name: 'framesSlowRate',
+ kind: FieldKind.FIELD,
+ },
+ custom_tag_name: {
+ key: 'custom_tag_name',
+ name: 'Custom_Tag_Name',
+ },
+};
+
+const FILTER_KEY_SECTIONS: FilterKeySection[] = [
+ {
+ value: 'cat_1',
+ label: 'Category 1',
+ children: [FieldKey.ASSIGNED, FieldKey.IS],
+ },
+ {
+ value: 'cat_2',
+ label: 'Category 2',
+ children: [WebVital.LCP, MobileVital.FRAMES_SLOW_RATE],
+ },
+ {
+ value: 'cat_3',
+ label: 'Category 3',
+ children: [FieldKey.TIMES_SEEN],
+ },
+];
diff --git a/static/app/views/detectors/edit.tsx b/static/app/views/detectors/edit.tsx
index 30dc7914f16385..92e94e20ead0e4 100644
--- a/static/app/views/detectors/edit.tsx
+++ b/static/app/views/detectors/edit.tsx
@@ -1,5 +1,5 @@
/* eslint-disable no-alert */
-import {Fragment} from 'react';
+import {Fragment, useState} from 'react';
import {Button} from 'sentry/components/core/button';
import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
@@ -9,20 +9,22 @@ import EditLayout from 'sentry/components/workflowEngine/layout/edit';
import {useWorkflowEngineFeatureGate} from 'sentry/components/workflowEngine/useWorkflowEngineFeatureGate';
import {t} from 'sentry/locale';
import useOrganization from 'sentry/utils/useOrganization';
+import {MetricDetectorForm} from 'sentry/views/detectors/components/forms/metric';
import {makeMonitorBasePathname} from 'sentry/views/detectors/pathnames';
export default function DetectorEdit() {
const organization = useOrganization();
useWorkflowEngineFeatureGate({redirect: true});
+ const [title, setTitle] = useState(t('Edit Monitor'));
return (
-
+
}>
-
- Edit Monitor
+
+
diff --git a/static/app/views/detectors/hooks/index.ts b/static/app/views/detectors/hooks/index.ts
new file mode 100644
index 00000000000000..2e740c4da68311
--- /dev/null
+++ b/static/app/views/detectors/hooks/index.ts
@@ -0,0 +1,223 @@
+import moment from 'moment-timezone';
+
+import {ActionType} from 'sentry/types/workflowEngine/actions';
+import {DataConditionGroupLogicType} from 'sentry/types/workflowEngine/dataConditions';
+import type {Detector} from 'sentry/types/workflowEngine/detectors';
+import {
+ useApiQueries,
+ useApiQuery,
+ useMutation,
+ useQuery,
+ useQueryClient,
+} from 'sentry/utils/queryClient';
+import useApi from 'sentry/utils/useApi';
+import useOrganization from 'sentry/utils/useOrganization';
+
+const mockDetectors: Detector[] = [
+ {
+ createdBy: '3363271',
+ dateCreated: moment().subtract(7, 'days').toDate(),
+ dateUpdated: moment().subtract(31, 'minutes').toDate(),
+ id: 'def123',
+ lastTriggered: moment().subtract(8, 'days').toDate(),
+ workflowIds: ['123456789'],
+ config: {},
+ dataCondition: {
+ conditions: [],
+ id: 'def456',
+ logicType: DataConditionGroupLogicType.ALL,
+ actions: [{data: {}, id: '1', type: ActionType.EMAIL}],
+ },
+ dataSource: {
+ id: '',
+ snubaQuery: {
+ aggregate: '',
+ dataset: '',
+ id: '',
+ query: '',
+ timeWindow: 60,
+ environment: '',
+ },
+ status: 1,
+ subscription: '',
+ },
+ disabled: false,
+ name: 'CLS Anomaly',
+ projectId: '11276',
+ type: 'metric',
+ },
+ {
+ createdBy: 'sentry',
+ dateCreated: moment().subtract(7, 'days').toDate(),
+ dateUpdated: moment().subtract(31, 'minutes').toDate(),
+ id: 'def123',
+ lastTriggered: moment().subtract(1, 'days').toDate(),
+
+ workflowIds: ['123456789'],
+ config: {},
+ dataCondition: {
+ conditions: [],
+ id: 'def456',
+ logicType: DataConditionGroupLogicType.ALL,
+ actions: [{data: {}, id: '1', type: ActionType.EMAIL}],
+ },
+ dataSource: {
+ id: '',
+ snubaQuery: {
+ aggregate: '',
+ dataset: '',
+ id: '',
+ query: '',
+ timeWindow: 60,
+ environment: '',
+ },
+ status: 1,
+ subscription: '',
+ },
+ disabled: false,
+ name: 'Error Grouping',
+ projectId: '1',
+ type: 'errors',
+ },
+ {
+ createdBy: 'sentry',
+ dateCreated: moment().subtract(7, 'days').toDate(),
+ dateUpdated: moment().subtract(31, 'minutes').toDate(),
+ id: 'abc123',
+ lastTriggered: moment().subtract(1, 'days').toDate(),
+
+ workflowIds: ['123456789', '987654321'],
+ config: {},
+ dataCondition: {
+ conditions: [],
+ id: 'def456',
+ logicType: DataConditionGroupLogicType.ALL,
+ actions: [{data: {}, id: '1', type: ActionType.EMAIL}],
+ },
+ dataSource: {
+ id: '',
+ snubaQuery: {
+ aggregate: '',
+ dataset: '',
+ id: '',
+ query: '',
+ timeWindow: 60,
+ environment: '',
+ },
+ status: 1,
+ subscription: '',
+ },
+ disabled: false,
+ name: 'Rage Click',
+ projectId: '11276',
+ type: 'replay',
+ },
+];
+
+export interface UseDetectorsQueryOptions {
+ query?: string;
+ sort?: string;
+}
+export function useDetectorsQuery(
+ projectId: string,
+ _options: UseDetectorsQueryOptions = {}
+) {
+ // const org = useOrganization();
+ // return useApiQuery([`/projects/${org.slug}/${projectId}/detectors/`], {
+ // staleTime: 0,
+ // retry: false
+ // })
+ return useQuery({
+ queryKey: [`/projects/${projectId}/detectors/`],
+ queryFn: () => mockDetectors,
+ staleTime: 0,
+ retry: false,
+ });
+}
+
+export const makeDetectorQueryKey = (orgSlug: string, detectorId = ''): [url: string] => [
+ `/organizations/${orgSlug}/detectors/${detectorId}`,
+];
+
+export function useCreateDetector(detector: Detector) {
+ const org = useOrganization();
+ return useQuery({
+ queryKey: [...makeDetectorQueryKey(org.slug), detector],
+ queryFn: () => {
+ mockDetectors.push(detector);
+ return detector;
+ },
+ staleTime: 0,
+ retry: false,
+ });
+ return useApiQuery(
+ [...makeDetectorQueryKey(org.slug), {method: 'POST', data: detector}],
+ {
+ staleTime: 0,
+ retry: false,
+ }
+ );
+}
+
+export function useDetectorQuery(detectorId: string) {
+ const org = useOrganization();
+
+ return useQuery({
+ queryKey: makeDetectorQueryKey(org.slug, detectorId),
+ queryFn: () => {
+ return mockDetectors.find(d => d.id === detectorId)!;
+ },
+ staleTime: 0,
+ retry: false,
+ });
+
+ return useApiQuery(makeDetectorQueryKey(org.slug, detectorId), {
+ staleTime: 0,
+ retry: false,
+ });
+}
+
+export function useDetectorQueriesByIds(detectorId: string[]) {
+ const org = useOrganization();
+
+ return useApiQueries(
+ detectorId.map(id => makeDetectorQueryKey(org.slug, id)),
+ {
+ staleTime: 0,
+ retry: false,
+ }
+ );
+}
+
+export function useDetectorMutation(detector: Partial & {id: string}) {
+ const api = useApi({persistInFlight: true});
+ const queryClient = useQueryClient();
+ const org = useOrganization();
+ const queryKey = makeDetectorQueryKey(org.slug, detector.id);
+ return useMutation({
+ mutationFn: data =>
+ api.requestPromise(queryKey[0], {
+ method: 'PUT',
+ data,
+ }),
+ onSuccess: _ => {
+ queryClient.invalidateQueries({queryKey});
+ // setApiQueryData(
+ // queryClient,
+ // makeDetailedProjectQueryKey({
+ // orgSlug: organization.slug,
+ // projectSlug: project.slug,
+ // }),
+ // existingData => (updatedProject ? updatedProject : existingData)
+ // );
+ // return onSuccess?.(updatedProject);
+
+ // eslint-disable-next-line no-console
+ console.log('updated detector');
+ },
+ onError: error => {
+ // eslint-disable-next-line no-console
+ console.error('error updating detector', error);
+ },
+ });
+}
diff --git a/static/app/views/detectors/list.tsx b/static/app/views/detectors/list.tsx
index f3978c701a87ad..7b3049cbd47bd1 100644
--- a/static/app/views/detectors/list.tsx
+++ b/static/app/views/detectors/list.tsx
@@ -1,5 +1,6 @@
import {Fragment} from 'react';
+// import moment from 'moment-timezone';
import {Flex} from 'sentry/components/container/flex';
import {LinkButton} from 'sentry/components/core/button/linkButton';
import PageFiltersContainer from 'sentry/components/organizations/pageFilters/container';
@@ -14,10 +15,15 @@ import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import useOrganization from 'sentry/utils/useOrganization';
import DetectorListTable from 'sentry/views/detectors/components/detectorListTable';
+// import { ActionType } from 'sentry/types/workflowEngine/actions';
+// import { DataConditionGroupLogicType } from 'sentry/types/workflowEngine/dataConditions';
+// import type { Detector } from 'sentry/types/workflowEngine/detectors';
+import {useDetectorsQuery} from 'sentry/views/detectors/hooks';
import {makeMonitorBasePathname} from 'sentry/views/detectors/pathnames';
export default function DetectorsList() {
useWorkflowEngineFeatureGate({redirect: true});
+ const {data: detectors} = useDetectorsQuery('11276');
return (
@@ -25,7 +31,7 @@ export default function DetectorsList() {
}>
-
+
diff --git a/static/app/views/detectors/new-settings.tsx b/static/app/views/detectors/new-settings.tsx
index 91a270784d7153..bf2d460f4463dd 100644
--- a/static/app/views/detectors/new-settings.tsx
+++ b/static/app/views/detectors/new-settings.tsx
@@ -1,5 +1,4 @@
import {Flex} from 'sentry/components/container/flex';
-import {Button} from 'sentry/components/core/button';
import {LinkButton} from 'sentry/components/core/button/linkButton';
import {
StickyFooter,
@@ -9,6 +8,7 @@ import {useWorkflowEngineFeatureGate} from 'sentry/components/workflowEngine/use
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import useOrganization from 'sentry/utils/useOrganization';
+import {MetricDetectorForm} from 'sentry/views/detectors/components/forms/metric';
import NewDetectorLayout from 'sentry/views/detectors/layouts/new';
import {makeMonitorBasePathname} from 'sentry/views/detectors/pathnames';
@@ -18,6 +18,7 @@ export default function DetectorNewSettings() {
return (
+
{t('Step 2 of 2')}
@@ -27,7 +28,9 @@ export default function DetectorNewSettings() {
>
{t('Back')}
-
+
+ {t('Create Monitor')}
+
diff --git a/tests/js/fixtures/automations.ts b/tests/js/fixtures/automations.ts
new file mode 100644
index 00000000000000..89f9449171a255
--- /dev/null
+++ b/tests/js/fixtures/automations.ts
@@ -0,0 +1,14 @@
+import { DataConditionGroupFixture } from "sentry-fixture/dataConditions";
+import { Automation } from "sentry/types/workflowEngine/automations";
+
+export function AutomationFixture(params: Partial): Automation {
+ return {
+ id: '1',
+ name: 'Automation',
+ lastTriggered: new Date('2025-01-01T00:00:00.000Z'),
+ actionFilters: [],
+ detectorIds: [],
+ triggers: DataConditionGroupFixture({}),
+ ...params
+ }
+}
diff --git a/tests/js/fixtures/dataConditions.ts b/tests/js/fixtures/dataConditions.ts
new file mode 100644
index 00000000000000..a122a32a3707ae
--- /dev/null
+++ b/tests/js/fixtures/dataConditions.ts
@@ -0,0 +1,21 @@
+import { DataCondition, DataConditionGroup, DataConditionGroupLogicType, DataConditionType } from 'sentry/types/workflowEngine/dataConditions';
+
+export function DataConditionFixture(params: Partial): DataCondition {
+ return {
+ type: DataConditionGroupLogicType.ALL,
+ comparison_type: DataConditionType.EQUAL,
+ comparison: '8',
+ id: '1',
+ ...params
+ }
+}
+
+export function DataConditionGroupFixture(params: Partial): DataConditionGroup {
+ return {
+ conditions: [DataConditionFixture({})],
+ id: '1',
+ logicType: DataConditionGroupLogicType.ANY,
+ actions: [],
+ ...params
+ }
+}
diff --git a/tests/js/fixtures/detectors.ts b/tests/js/fixtures/detectors.ts
new file mode 100644
index 00000000000000..80072df77c842b
--- /dev/null
+++ b/tests/js/fixtures/detectors.ts
@@ -0,0 +1,34 @@
+import { Detector } from "sentry/types/workflowEngine/detectors";
+import { UserFixture } from "sentry-fixture/user";
+import { DataConditionGroupFixture } from "sentry-fixture/dataConditions";
+
+export function DetectorFixture(params: Partial): Detector {
+ return {
+ id: '1',
+ name: 'detector',
+ projectId: '1',
+ createdBy: UserFixture().id,
+ dateCreated: new Date('2025-01-01T00:00:00.000Z'),
+ dateUpdated: new Date('2025-01-01T00:00:00.000Z'),
+ lastTriggered: new Date('2025-01-01T00:00:00.000Z'),
+ workflowIds: [],
+ config: {},
+ type: 'metric',
+ dataCondition: DataConditionGroupFixture({}),
+ disabled: false,
+ dataSource: {
+ id: '1',
+ status: 1,
+ snubaQuery: {
+ aggregate: '',
+ dataset: '',
+ id: '',
+ query: '',
+ timeWindow: 60,
+ ...(params.dataSource?.snubaQuery ?? {}),
+ },
+ ...(params.dataSource ?? {}),
+ },
+ ...params,
+ };
+}
From 72af9dd07863e27343488a58c026217d3a105c97 Mon Sep 17 00:00:00 2001
From: Nate Moore
Date: Mon, 21 Apr 2025 11:20:40 -0500
Subject: [PATCH 02/37] chore: data
---
static/app/views/automations/new.tsx | 177 ---------------------------
1 file changed, 177 deletions(-)
diff --git a/static/app/views/automations/new.tsx b/static/app/views/automations/new.tsx
index 433f6d807e0c29..ac63b9a1c02090 100644
--- a/static/app/views/automations/new.tsx
+++ b/static/app/views/automations/new.tsx
@@ -21,183 +21,6 @@ import {makeAutomationBasePathname} from 'sentry/views/automations/pathnames';
export default function AutomationNew() {
const organization = useOrganization();
useWorkflowEngineFeatureGate({redirect: true});
- const [monitor1Connected, setMonitor1Connected] = useState(true);
- const [monitor2Connected, setMonitor2Connected] = useState(true);
- const [monitor3Connected, setMonitor3Connected] = useState(false);
- const [monitor4Connected, setMonitor4Connected] = useState(false);
- const [monitor5Connected, setMonitor5Connected] = useState(false);
- const [monitor6Connected, setMonitor6Connected] = useState(false);
- const [monitor7Connected, setMonitor7Connected] = useState(false);
- const [monitor8Connected, setMonitor8Connected] = useState(false);
- const [monitor9Connected, setMonitor9Connected] = useState(false);
- const [monitor10Connected, setMonitor10Connected] = useState(false);
-
- const data: MonitorsData[] = [
- {
- name: {
- name: 'Error Grouping',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'errors',
- createdBy: 'sentry',
- connect: {connected: monitor1Connected, toggleConnected: setMonitor1Connected},
- },
- {
- name: {
- name: 'Endpoint Regression',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'metric',
- createdBy: {
- email: 'miahsu@sentry.io',
- name: 'Mia Hsu',
- username: 'f4ea91ef8dc34fe8a54b3732030fbf7b',
- id: '3286015',
- ip_address: '1.1.1.1',
- },
- connect: {connected: monitor2Connected, toggleConnected: setMonitor2Connected},
- },
- {
- name: {
- name: 'Consecutive DB Queries',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'trace',
- createdBy: 'sentry',
- connect: {connected: monitor3Connected, toggleConnected: setMonitor3Connected},
- },
- {
- name: {
- name: 'Consecutive HTTP',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- type: 'trace',
- createdBy: 'sentry',
- connect: {connected: monitor4Connected, toggleConnected: setMonitor4Connected},
- },
- {
- name: {
- name: 'N+1 API Call',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'trace',
- createdBy: {
- email: 'cathy.teng@sentry.io',
- name: 'Cathy Teng',
- id: '2120569',
- ip_address: '1.1.1.1',
- },
- connect: {connected: monitor5Connected, toggleConnected: setMonitor5Connected},
- },
- {
- name: {
- name: 'Slow DB Query',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- type: 'trace',
- createdBy: {
- email: 'michelle.fu@sentry.io',
- name: 'Michelle Fu',
- id: '2787837',
- ip_address: '1.1.1.1',
- },
- connect: {connected: monitor6Connected, toggleConnected: setMonitor6Connected},
- },
- {
- name: {
- name: 'Uncompressed Asset',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'errors',
- createdBy: {
- email: 'christina.long@sentry.io',
- name: 'Christina Long',
- id: '3284085',
- ip_address: '1.1.1.1',
- },
- connect: {connected: monitor7Connected, toggleConnected: setMonitor7Connected},
- },
- {
- name: {
- name: 'Rage Click',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'replay',
- createdBy: {
- email: 'raj.joshi@sentry.io',
- name: 'Raj Joshi',
- id: '3068985',
- ip_address: '1.1.1.1',
- },
- connect: {connected: monitor8Connected, toggleConnected: setMonitor8Connected},
- },
- {
- name: {
- name: 'Error Grouping',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'errors',
- createdBy: 'sentry',
- connect: {connected: monitor9Connected, toggleConnected: setMonitor9Connected},
- },
- {
- name: {
- name: 'Error Grouping',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'errors',
- createdBy: 'sentry',
- connect: {connected: monitor10Connected, toggleConnected: setMonitor10Connected},
- },
- ];
return (
From 4e10a794d3abf8d5d5a052796c3e7673fc39d471 Mon Sep 17 00:00:00 2001
From: Nate Moore
Date: Mon, 21 Apr 2025 11:23:35 -0500
Subject: [PATCH 03/37] chore: data
---
.../automations/components/automationForm.tsx | 39 -------------------
1 file changed, 39 deletions(-)
diff --git a/static/app/views/automations/components/automationForm.tsx b/static/app/views/automations/components/automationForm.tsx
index f42840f4f23265..313b6a18d0777e 100644
--- a/static/app/views/automations/components/automationForm.tsx
+++ b/static/app/views/automations/components/automationForm.tsx
@@ -49,45 +49,6 @@ export default function AutomationForm() {
const title = useDocumentTitle();
const {state, actions} = useAutomationBuilderReducer();
const [model] = useState(() => new FormModel());
- const [monitor1Connected, setMonitor1Connected] = useState(true);
- const [monitor2Connected, setMonitor2Connected] = useState(true);
-
- const data: MonitorsData[] = [
- {
- name: {
- name: 'Error Grouping',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'errors',
- createdBy: 'sentry',
- connect: {connected: monitor1Connected, toggleConnected: setMonitor1Connected},
- },
- {
- name: {
- name: 'Error Grouping',
- project: {
- slug: 'javascript',
- platform: 'javascript',
- },
- link: '/issues/1',
- },
- lastIssue: {shortId: 'JAVASCRIPT-SHGH', platform: 'javascript'},
- type: 'metric',
- createdBy: {
- email: 'miahsu@sentry.io',
- name: 'Mia Hsu',
- username: 'f4ea91ef8dc34fe8a54b3732030fbf7b',
- id: '3286015',
- ip_address: '1.1.1.1',
- },
- connect: {connected: monitor2Connected, toggleConnected: setMonitor2Connected},
- },
- ];
useEffect(() => {
model.setValue('name', title);
From 906f004b83045fbde981379e9f5992ee6cea2897 Mon Sep 17 00:00:00 2001
From: Nate Moore
Date: Mon, 28 Apr 2025 10:59:14 -0500
Subject: [PATCH 04/37] ref(aci): create mock chart component
---
.../detectors/components/forms/metric.tsx | 283 +++++++++---------
1 file changed, 142 insertions(+), 141 deletions(-)
diff --git a/static/app/views/detectors/components/forms/metric.tsx b/static/app/views/detectors/components/forms/metric.tsx
index ef3fa55ba84fcd..7afd006e454233 100644
--- a/static/app/views/detectors/components/forms/metric.tsx
+++ b/static/app/views/detectors/components/forms/metric.tsx
@@ -32,147 +32,7 @@ export function MetricDetectorForm() {
return (