Skip to content

Commit edcced9

Browse files
malwilleyandrewshie-sentry
authored andcommitted
feat(aci): Hook up filtering and pagination to automations list page (#93479)
1 parent 88f4010 commit edcced9

File tree

9 files changed

+338
-51
lines changed

9 files changed

+338
-51
lines changed
Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,11 @@
1-
import styled from '@emotion/styled';
2-
3-
import Link from 'sentry/components/links/link';
4-
import {space} from 'sentry/styles/space';
1+
import {TitleCell} from 'sentry/components/workflowEngine/gridCell/titleCell';
52

63
interface Props {
74
href: string;
85
name: string;
6+
createdBy?: string | null;
97
}
108

11-
export default function AutomationTitleCell({name, href}: Props) {
12-
return <StyledLink to={href}>{name}</StyledLink>;
9+
export default function AutomationTitleCell({name, href, createdBy}: Props) {
10+
return <TitleCell name={name} link={href} createdBy={createdBy} />;
1311
}
14-
15-
const StyledLink = styled(Link)`
16-
padding: ${space(2)};
17-
margin: -${space(2)};
18-
color: ${p => p.theme.textColor};
19-
20-
&:hover,
21-
&:active {
22-
text-decoration: underline;
23-
color: ${p => p.theme.textColor};
24-
}
25-
`;

static/app/views/automations/components/automationListTable.tsx renamed to static/app/views/automations/components/automationListTable/index.tsx

Lines changed: 70 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,84 @@
11
import styled from '@emotion/styled';
22

33
import {Flex} from 'sentry/components/container/flex';
4-
import LoadingIndicator from 'sentry/components/loadingIndicator';
4+
import LoadingError from 'sentry/components/loadingError';
55
import Panel from 'sentry/components/panels/panel';
6+
import PanelBody from 'sentry/components/panels/panelBody';
67
import PanelHeader from 'sentry/components/panels/panelHeader';
78
import {t} from 'sentry/locale';
89
import {space} from 'sentry/styles/space';
9-
import {AutomationListRow} from 'sentry/views/automations/components/automationListRow';
10-
import {useAutomationsQuery} from 'sentry/views/automations/hooks';
10+
import type {Automation} from 'sentry/types/workflowEngine/automations';
11+
import {
12+
AutomationListRow,
13+
AutomationListRowSkeleton,
14+
} from 'sentry/views/automations/components/automationListTable/row';
15+
import {AUTOMATION_LIST_PAGE_LIMIT} from 'sentry/views/automations/constants';
16+
17+
type AutomationListTableProps = {
18+
automations: Automation[];
19+
isError: boolean;
20+
isPending: boolean;
21+
};
22+
23+
function LoadingSkeletons() {
24+
return Array.from({length: AUTOMATION_LIST_PAGE_LIMIT}).map((_, index) => (
25+
<AutomationListRowSkeleton key={index} />
26+
));
27+
}
28+
29+
function TableHeader() {
30+
return (
31+
<StyledPanelHeader>
32+
<Flex className="name">
33+
<Heading>{t('Name')}</Heading>
34+
</Flex>
35+
<Flex className="last-triggered">
36+
<Heading>{t('Last Triggered')}</Heading>
37+
</Flex>
38+
<Flex className="action">
39+
<HeaderDivider />
40+
<Heading>{t('Actions')}</Heading>
41+
</Flex>
42+
<Flex className="projects">
43+
<HeaderDivider />
44+
<Heading>{t('Projects')}</Heading>
45+
</Flex>
46+
<Flex className="connected-monitors">
47+
<HeaderDivider />
48+
<Heading>{t('Monitors')}</Heading>
49+
</Flex>
50+
</StyledPanelHeader>
51+
);
52+
}
53+
54+
function AutomationListTable({
55+
automations,
56+
isPending,
57+
isError,
58+
}: AutomationListTableProps) {
59+
if (isError) {
60+
return (
61+
<PanelGrid>
62+
<TableHeader />
63+
<PanelBody>
64+
<LoadingError />
65+
</PanelBody>
66+
</PanelGrid>
67+
);
68+
}
1169

12-
function AutomationListTable() {
13-
const {data: automations = [], isPending} = useAutomationsQuery();
70+
if (isPending) {
71+
return (
72+
<PanelGrid>
73+
<TableHeader />
74+
<LoadingSkeletons />
75+
</PanelGrid>
76+
);
77+
}
1478

1579
return (
1680
<PanelGrid>
17-
<StyledPanelHeader>
18-
<Flex className="name">
19-
<Heading>{t('Name')}</Heading>
20-
</Flex>
21-
<Flex className="last-triggered">
22-
<Heading>{t('Last Triggered')}</Heading>
23-
</Flex>
24-
<Flex className="action">
25-
<HeaderDivider />
26-
<Heading>{t('Actions')}</Heading>
27-
</Flex>
28-
<Flex className="projects">
29-
<HeaderDivider />
30-
<Heading>{t('Projects')}</Heading>
31-
</Flex>
32-
<Flex className="connected-monitors">
33-
<HeaderDivider />
34-
<Heading>{t('Monitors')}</Heading>
35-
</Flex>
36-
</StyledPanelHeader>
37-
{isPending ? <LoadingIndicator /> : null}
81+
<TableHeader />
3882
{automations.map(automation => (
3983
<AutomationListRow key={automation.id} automation={automation} />
4084
))}

static/app/views/automations/components/automationListRow.tsx renamed to static/app/views/automations/components/automationListTable/row.tsx

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import styled from '@emotion/styled';
33

44
import {Checkbox} from 'sentry/components/core/checkbox';
55
import InteractionStateLayer from 'sentry/components/interactionStateLayer';
6+
import Placeholder from 'sentry/components/placeholder';
67
import {ProjectList} from 'sentry/components/projectList';
78
import {ActionCell} from 'sentry/components/workflowEngine/gridCell/actionCell';
89
import AutomationTitleCell from 'sentry/components/workflowEngine/gridCell/automationTitleCell';
@@ -25,18 +26,19 @@ type AutomationListRowProps = {
2526
export function AutomationListRow({automation}: AutomationListRowProps) {
2627
const organization = useOrganization();
2728
const actions = getAutomationActions(automation);
28-
const {id, name, disabled, lastTriggered, detectorIds = []} = automation;
29+
const {id, name, disabled, lastTriggered, detectorIds = [], createdBy} = automation;
2930
const projectIds = useAutomationProjectIds(automation);
3031
const projectSlugs = projectIds.map(
3132
projectId => ProjectsStore.getById(projectId)?.slug
3233
) as string[];
3334
return (
34-
<RowWrapper disabled={disabled}>
35+
<RowWrapper disabled={disabled} data-test-id="automation-list-row">
3536
<InteractionStateLayer />
3637
<CellWrapper>
3738
<AutomationTitleCell
3839
name={name}
3940
href={makeAutomationDetailsPathname(organization.slug, id)}
41+
createdBy={createdBy}
4042
/>
4143
</CellWrapper>
4244
<CellWrapper className="last-triggered">
@@ -55,6 +57,28 @@ export function AutomationListRow({automation}: AutomationListRowProps) {
5557
);
5658
}
5759

60+
export function AutomationListRowSkeleton() {
61+
return (
62+
<RowWrapper>
63+
<CellWrapper>
64+
<Placeholder height="20px" />
65+
</CellWrapper>
66+
<CellWrapper className="last-triggered">
67+
<Placeholder height="20px" />
68+
</CellWrapper>
69+
<CellWrapper className="action">
70+
<Placeholder height="20px" />
71+
</CellWrapper>
72+
<CellWrapper className="projects">
73+
<Placeholder height="20px" />
74+
</CellWrapper>
75+
<CellWrapper className="connected-monitors">
76+
<Placeholder height="20px" />
77+
</CellWrapper>
78+
</RowWrapper>
79+
);
80+
}
81+
5882
const StyledCheckbox = styled(Checkbox)<{checked?: boolean}>`
5983
visibility: ${p => (p.checked ? 'visible' : 'hidden')};
6084
align-self: flex-start;
@@ -80,6 +104,8 @@ const RowWrapper = styled('div')<{disabled?: boolean}>`
80104
align-items: center;
81105
padding: ${space(2)};
82106
107+
min-height: 60px;
108+
83109
${p =>
84110
p.disabled &&
85111
css`
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import {SearchQueryBuilder} from 'sentry/components/searchQueryBuilder';
2+
import {t} from 'sentry/locale';
3+
import type {TagCollection} from 'sentry/types/group';
4+
import type {FieldDefinition} from 'sentry/utils/fields';
5+
import {FieldKind} from 'sentry/utils/fields';
6+
import {useLocation} from 'sentry/utils/useLocation';
7+
import {useNavigate} from 'sentry/utils/useNavigate';
8+
import {AUTOMATION_FILTER_KEYS} from 'sentry/views/automations/constants';
9+
10+
function getAutomationFilterKeyDefinition(filterKey: string): FieldDefinition | null {
11+
if (
12+
AUTOMATION_FILTER_KEYS.hasOwnProperty(filterKey) &&
13+
AUTOMATION_FILTER_KEYS[filterKey]
14+
) {
15+
const {description, valueType, keywords, values} = AUTOMATION_FILTER_KEYS[filterKey];
16+
17+
return {
18+
kind: FieldKind.FIELD,
19+
desc: description,
20+
valueType,
21+
keywords,
22+
values,
23+
};
24+
}
25+
26+
return null;
27+
}
28+
29+
const FILTER_KEYS: TagCollection = Object.fromEntries(
30+
Object.keys(AUTOMATION_FILTER_KEYS).map(key => {
31+
const {values} = AUTOMATION_FILTER_KEYS[key] ?? {};
32+
33+
return [
34+
key,
35+
{
36+
key,
37+
name: key,
38+
predefined: values !== undefined,
39+
values,
40+
},
41+
];
42+
})
43+
);
44+
45+
export function AutomationSearch() {
46+
const location = useLocation();
47+
const navigate = useNavigate();
48+
const query = typeof location.query.query === 'string' ? location.query.query : '';
49+
50+
return (
51+
<SearchQueryBuilder
52+
initialQuery={query}
53+
placeholder={t('Search for automations')}
54+
onSearch={searchQuery => {
55+
navigate({
56+
pathname: location.pathname,
57+
query: {
58+
...location.query,
59+
query: searchQuery,
60+
},
61+
});
62+
}}
63+
filterKeys={FILTER_KEYS}
64+
getTagValues={() => Promise.resolve([])}
65+
searchSource="automations-list"
66+
fieldDefinitionGetter={getAutomationFilterKeyDefinition}
67+
disallowUnsupportedFilters
68+
disallowWildcard
69+
disallowLogicalOperators
70+
searchOnChange
71+
/>
72+
);
73+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import {FieldValueType} from 'sentry/utils/fields';
2+
import {ActionType} from 'sentry/views/alerts/rules/metric/types';
3+
4+
export const AUTOMATION_LIST_PAGE_LIMIT = 20;
5+
6+
const ACTION_TYPE_VALUES = Object.values(ActionType).sort();
7+
8+
export const AUTOMATION_FILTER_KEYS: Record<
9+
string,
10+
{
11+
description: string;
12+
valueType: FieldValueType;
13+
keywords?: string[];
14+
values?: string[];
15+
}
16+
> = {
17+
name: {
18+
description: 'Name of the automation (exact match).',
19+
valueType: FieldValueType.STRING,
20+
keywords: ['name'],
21+
},
22+
action: {
23+
description: 'Action triggered by the automation.',
24+
valueType: FieldValueType.STRING,
25+
values: ACTION_TYPE_VALUES,
26+
},
27+
};

static/app/views/automations/hooks/index.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,18 @@ const makeAutomationsQueryKey = ({
1515
ids,
1616
limit,
1717
cursor,
18+
projects,
1819
}: {
1920
orgSlug: string;
2021
cursor?: string;
2122
ids?: string[];
2223
limit?: number;
24+
projects?: number[];
2325
query?: string;
2426
sort?: string;
2527
}): ApiQueryKey => [
2628
`/organizations/${orgSlug}/workflows/`,
27-
{query: {query, sort, id: ids, per_page: limit, cursor}},
29+
{query: {query, sort, id: ids, per_page: limit, cursor, project: projects}},
2830
];
2931

3032
const makeAutomationQueryKey = (orgSlug: string, automationId: string): ApiQueryKey => [
@@ -35,6 +37,7 @@ interface UseAutomationsQueryOptions {
3537
cursor?: string;
3638
ids?: string[];
3739
limit?: number;
40+
projects?: number[];
3841
query?: string;
3942
sort?: string;
4043
}

0 commit comments

Comments
 (0)