Skip to content

Commit 46595cd

Browse files
ryan953Claude Sonnet 4
authored andcommitted
feat(seer): Setup Agent bulk-edit on the Seer Autofix list page, and bring back Create PR bulk edits (#112249)
<img width="279" height="198" alt="SCR-20260403-oruo" src="https://github.com/user-attachments/assets/c8b49017-40fe-49ec-aa47-d364ed9b4d8c" /> --------- Co-authored-by: Claude Sonnet 4 <noreply@example.com>
1 parent f073319 commit 46595cd

File tree

6 files changed

+57
-34
lines changed

6 files changed

+57
-34
lines changed

static/app/views/settings/seer/overview/autofixOverviewSection.tsx

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -161,9 +161,7 @@ function AgentNameForm({
161161
const preferredAgent = useFetchPreferredAgent({organization});
162162
const codingAgentSelectOptions = useFetchAgentOptions({organization});
163163
const codingAgentMutationOptions = getPreferredAgentMutationOptions({organization});
164-
const bulkMutateSelectedAgent = useBulkMutateSelectedAgent({
165-
projects: projects.filter(p => !projectsIdsWithPreferredAgent.has(p.id)),
166-
});
164+
const bulkMutateSelectedAgent = useBulkMutateSelectedAgent();
167165

168166
const preferredAgentLabel = codingAgentSelectOptions.data?.find(
169167
o => o.value === preferredAgent.data
@@ -225,7 +223,10 @@ function AgentNameForm({
225223
onClick={async () => {
226224
if (preferredAgent.data) {
227225
setIsBulkMutatingAgent(true);
228-
await bulkMutateSelectedAgent(preferredAgent.data);
226+
await bulkMutateSelectedAgent(
227+
projects.filter(p => !projectsIdsWithPreferredAgent.has(p.id)),
228+
preferredAgent.data
229+
);
229230
setIsBulkMutatingAgent(false);
230231
} else {
231232
addErrorMessage(t('No coding agent integration found'));

static/app/views/settings/seer/overview/utils/seerPreferredAgent.spec.ts

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -295,11 +295,10 @@ describe('seerPreferredAgent', () => {
295295

296296
const {result} = renderHookWithProviders(useBulkMutateSelectedAgent, {
297297
organization,
298-
initialProps: {projects: [project]},
299298
});
300299

301300
await act(async () => {
302-
await result.current('seer');
301+
await result.current([project], 'seer');
303302
});
304303

305304
expect(projectPutRequest).toHaveBeenCalledWith(
@@ -328,11 +327,10 @@ describe('seerPreferredAgent', () => {
328327

329328
const {result} = renderHookWithProviders(useBulkMutateSelectedAgent, {
330329
organization,
331-
initialProps: {projects: [project]},
332330
});
333331

334332
await act(async () => {
335-
await result.current(integration);
333+
await result.current([project], integration);
336334
});
337335

338336
expect(projectPutRequest).toHaveBeenCalledWith(
@@ -368,10 +366,9 @@ describe('seerPreferredAgent', () => {
368366

369367
const {result} = renderHookWithProviders(useBulkMutateSelectedAgent, {
370368
organization,
371-
initialProps: {projects: [project]},
372369
});
373370
await act(async () => {
374-
await result.current(integration);
371+
await result.current([project], integration);
375372
});
376373

377374
expect(seerPreferencesPostRequest).toHaveBeenCalledWith(
@@ -392,11 +389,10 @@ describe('seerPreferredAgent', () => {
392389

393390
const {result} = renderHookWithProviders(useBulkMutateSelectedAgent, {
394391
organization,
395-
initialProps: {projects: [project]},
396392
});
397393

398394
await act(async () => {
399-
await result.current('seer');
395+
await result.current([project], 'seer');
400396
});
401397

402398
expect(updateSuccessSpy).toHaveBeenCalledWith({
@@ -427,11 +423,10 @@ describe('seerPreferredAgent', () => {
427423

428424
const {result} = renderHookWithProviders(useBulkMutateSelectedAgent, {
429425
organization,
430-
initialProps: {projects: [project]},
431426
});
432427

433428
await act(async () => {
434-
await result.current('seer');
429+
await result.current([project], 'seer');
435430
});
436431

437432
expect(addErrorMessageSpy).toHaveBeenCalledWith(
@@ -461,11 +456,10 @@ describe('seerPreferredAgent', () => {
461456

462457
const {result} = renderHookWithProviders(useBulkMutateSelectedAgent, {
463458
organization,
464-
initialProps: {projects: [project]},
465459
});
466460

467461
await act(async () => {
468-
await result.current('seer');
462+
await result.current([project], 'seer');
469463
});
470464

471465
expect(addErrorMessageSpy).toHaveBeenCalledWith(

static/app/views/settings/seer/overview/utils/seerPreferredAgent.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,15 +101,15 @@ export function getPreferredAgentMutationOptions({
101101
});
102102
}
103103

104-
export function useBulkMutateSelectedAgent({projects}: {projects: Project[]}) {
104+
export function useBulkMutateSelectedAgent() {
105105
const organization = useOrganization();
106106
const queryClient = useQueryClient();
107107
const autofixSettingsQueryOptions = bulkAutofixAutomationSettingsInfiniteOptions({
108108
organization,
109109
});
110110

111111
return useCallback(
112-
async (integration: PreferredAgent) => {
112+
async (projects: Project[], integration: PreferredAgent) => {
113113
const results = await processInChunks({
114114
items: projects,
115115
chunkSize: 15,
@@ -184,6 +184,6 @@ export function useBulkMutateSelectedAgent({projects}: {projects: Project[]}) {
184184
}
185185
}
186186
},
187-
[projects, organization, queryClient, autofixSettingsQueryOptions.queryKey]
187+
[organization, queryClient, autofixSettingsQueryOptions.queryKey]
188188
);
189189
}

static/gsApp/views/seerAutomation/components/projectTable/seerProjectTable.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,6 @@ const FiltersContainer = styled('div')`
280280
`;
281281

282282
const SimpleTableWithColumns = styled(SimpleTable)`
283-
grid-template-columns: 3fr minmax(300px, 1fr) repeat(2, max-content);
283+
grid-template-columns: max-content 3fr minmax(300px, 1fr) repeat(2, max-content);
284284
overflow: visible;
285285
`;

static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableHeader.tsx

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ import {parseQueryKey} from 'sentry/utils/api/apiQueryKey';
1616
import type {Sort} from 'sentry/utils/discover/fields';
1717
import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
1818
import {useOrganization} from 'sentry/utils/useOrganization';
19+
import {
20+
useBulkMutateSelectedAgent,
21+
useFetchAgentOptions,
22+
} from 'sentry/views/settings/seer/overview/utils/seerPreferredAgent';
1923

2024
import {useCanWriteSettings} from 'getsentry/views/seerAutomation/components/useCanWriteSettings';
2125

@@ -91,15 +95,18 @@ export function ProjectTableHeader({
9195
[projects, selectedIds]
9296
);
9397

98+
const agentOptions = useFetchAgentOptions({organization});
99+
const bulkMutateSelectedAgent = useBulkMutateSelectedAgent();
100+
94101
return (
95102
<Fragment>
96103
<TableHeader>
97-
{/* <SimpleTable.HeaderCell>
104+
<SimpleTable.HeaderCell>
98105
<SelectAllCheckbox
99106
listItemCheckboxState={listItemCheckboxState}
100107
projects={projects}
101108
/>
102-
</SimpleTable.HeaderCell> */}
109+
</SimpleTable.HeaderCell>
103110
{COLUMNS.map(({title, key, sortKey}) => (
104111
<SimpleTable.HeaderCell
105112
key={key}
@@ -133,6 +140,23 @@ export function ProjectTableHeader({
133140
/>
134141
</TableCellFirst>
135142
<TableCellsRemainingContent align="center" gap="md">
143+
<DropdownMenu
144+
isDisabled={!canWrite}
145+
size="xs"
146+
items={
147+
agentOptions.data?.map(({value, label}) => ({
148+
key: typeof value === 'object' ? value.provider : value,
149+
label,
150+
onAction: () => {
151+
const selectedProjects = projects.filter(p =>
152+
projectIds.includes(p.id)
153+
);
154+
bulkMutateSelectedAgent(selectedProjects, value);
155+
},
156+
})) ?? []
157+
}
158+
triggerLabel={t('Agent')}
159+
/>
136160
<DropdownMenu
137161
isDisabled={!canWrite || organization.enableSeerCoding === false}
138162
size="xs"
@@ -156,7 +180,7 @@ export function ProjectTableHeader({
156180
),
157181
},
158182
]}
159-
triggerLabel={t('Auto Create PRs')}
183+
triggerLabel={t('Create PRs')}
160184
/>
161185
</TableCellsRemainingContent>
162186
</TableHeader>

static/gsApp/views/seerAutomation/components/projectTable/seerProjectTableRow.tsx

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
import styled from '@emotion/styled';
2+
3+
import {Checkbox} from '@sentry/scraps/checkbox';
14
import {InfoTip} from '@sentry/scraps/info';
25
import {Flex, Stack} from '@sentry/scraps/layout';
36
import {Link} from '@sentry/scraps/link';
@@ -15,6 +18,7 @@ import {SimpleTable} from 'sentry/components/tables/simpleTable';
1518
import {IconWarning} from 'sentry/icons/iconWarning';
1619
import {t, tct} from 'sentry/locale';
1720
import type {Project} from 'sentry/types/project';
21+
import {useListItemCheckboxContext} from 'sentry/utils/list/useListItemCheckboxState';
1822
import {useOrganization} from 'sentry/utils/useOrganization';
1923
import {
2024
useAgentOptions,
@@ -40,7 +44,7 @@ export function SeerProjectTableRow({
4044
}: Props) {
4145
const organization = useOrganization();
4246
const canWrite = useCanWriteSettings();
43-
// const {isSelected, toggleSelected} = useListItemCheckboxContext();
47+
const {isSelected, toggleSelected} = useListItemCheckboxContext();
4448

4549
const options = useAgentOptions({integrations: integrations ?? []});
4650
const autofixAgent = useSelectedAgentFromBulkSettings({
@@ -66,7 +70,7 @@ export function SeerProjectTableRow({
6670

6771
return (
6872
<SimpleTable.Row key={project.id}>
69-
{/* <SimpleTable.RowCell>
73+
<SimpleTable.RowCell>
7074
<CheckboxClickTarget htmlFor={`replay-table-select-${project.id}`}>
7175
<Checkbox
7276
id={`replay-table-select-${project.id}`}
@@ -75,7 +79,7 @@ export function SeerProjectTableRow({
7579
onChange={() => toggleSelected(project.id)}
7680
/>
7781
</CheckboxClickTarget>
78-
</SimpleTable.RowCell> */}
82+
</SimpleTable.RowCell>
7983
<SimpleTable.RowCell>
8084
<Link to={`/settings/${organization.slug}/projects/${project.slug}/seer/`}>
8185
<ProjectBadge disableLink project={project} avatarSize={16} />
@@ -194,11 +198,11 @@ export function SeerProjectTableRow({
194198
);
195199
}
196200

197-
// const CheckboxClickTarget = styled('label')`
198-
// cursor: pointer;
199-
// display: block;
200-
// margin: -${p => p.theme.space.md};
201-
// padding: ${p => p.theme.space.md};
202-
// max-width: unset;
203-
// line-height: 0;
204-
// `;
201+
const CheckboxClickTarget = styled('label')`
202+
cursor: pointer;
203+
display: block;
204+
margin: -${p => p.theme.space.md};
205+
padding: ${p => p.theme.space.md};
206+
max-width: unset;
207+
line-height: 0;
208+
`;

0 commit comments

Comments
 (0)