|
1 | | -import {useMemo, useState} from 'react'; |
| 1 | +import {useEffect, useMemo} from 'react'; |
2 | 2 | import styled from '@emotion/styled'; |
3 | 3 | import {debounce, parseAsString, useQueryState} from 'nuqs'; |
4 | 4 |
|
5 | 5 | import {InputGroup} from '@sentry/scraps/input'; |
6 | 6 | import {Stack} from '@sentry/scraps/layout'; |
7 | 7 |
|
8 | 8 | import { |
9 | | - useGetBulkAutofixAutomationSettings, |
| 9 | + bulkAutofixAutomationSettingsInfiniteOptions, |
10 | 10 | useUpdateBulkAutofixAutomationSettings, |
11 | | - type AutofixAutomationSettings, |
12 | 11 | } from 'sentry/components/events/autofix/preferences/hooks/useBulkAutofixAutomationSettings'; |
| 12 | +import {organizationIntegrationsCodingAgents} from 'sentry/components/events/autofix/useAutofix'; |
13 | 13 | import LoadingError from 'sentry/components/loadingError'; |
14 | 14 | import LoadingIndicator from 'sentry/components/loadingIndicator'; |
15 | 15 | import {SimpleTable} from 'sentry/components/tables/simpleTable'; |
16 | 16 | import {IconSearch} from 'sentry/icons/iconSearch'; |
17 | 17 | import {t, tct} from 'sentry/locale'; |
| 18 | +import ProjectsStore from 'sentry/stores/projectsStore'; |
18 | 19 | import type {Project} from 'sentry/types/project'; |
19 | 20 | import type {Sort} from 'sentry/utils/discover/fields'; |
20 | 21 | import {ListItemCheckboxProvider} from 'sentry/utils/list/useListItemCheckboxState'; |
| 22 | +import {useInfiniteQuery, useQuery, useQueryClient} from 'sentry/utils/queryClient'; |
21 | 23 | import type {ApiQueryKey} from 'sentry/utils/queryClient'; |
22 | 24 | import parseAsSort from 'sentry/utils/url/parseAsSort'; |
| 25 | +import useOrganization from 'sentry/utils/useOrganization'; |
23 | 26 | import useProjects from 'sentry/utils/useProjects'; |
24 | 27 |
|
25 | 28 | import ProjectTableHeader from 'getsentry/views/seerAutomation/components/projectTable/seerProjectTableHeader'; |
26 | 29 | import SeerProjectTableRow from 'getsentry/views/seerAutomation/components/projectTable/seerProjectTableRow'; |
27 | 30 |
|
28 | | -function getDefaultAutofixSettings(projectId: string): AutofixAutomationSettings { |
29 | | - return { |
30 | | - autofixAutomationTuning: 'off', |
31 | | - automatedRunStoppingPoint: 'code_changes', |
32 | | - automationHandoff: undefined, |
33 | | - projectId, |
34 | | - reposCount: 0, |
35 | | - }; |
36 | | -} |
37 | | - |
38 | 31 | export default function SeerProjectTable() { |
| 32 | + const queryClient = useQueryClient(); |
| 33 | + const organization = useOrganization(); |
39 | 34 | const {projects, fetching, fetchError} = useProjects(); |
40 | 35 |
|
41 | | - const {pages: autofixAutomationSettings, isFetching: isFetchingSettings} = |
42 | | - useGetBulkAutofixAutomationSettings(); |
| 36 | + const autofixSettingsQueryOptions = bulkAutofixAutomationSettingsInfiniteOptions({ |
| 37 | + organization, |
| 38 | + }); |
| 39 | + const { |
| 40 | + data: autofixAutomationSettings, |
| 41 | + hasNextPage, |
| 42 | + isError, |
| 43 | + fetchNextPage, |
| 44 | + isFetchingNextPage, |
| 45 | + } = useInfiniteQuery({ |
| 46 | + ...autofixSettingsQueryOptions, |
| 47 | + select: ({pages}) => pages.flatMap(page => page.json), |
| 48 | + }); |
| 49 | + |
| 50 | + // Auto-fetch each page, one at a time |
| 51 | + useEffect(() => { |
| 52 | + if (!isError && !isFetchingNextPage && hasNextPage) { |
| 53 | + fetchNextPage(); |
| 54 | + } |
| 55 | + }, [hasNextPage, fetchNextPage, isError, isFetchingNextPage]); |
43 | 56 |
|
44 | | - const [mutationData, setMutations] = useState< |
45 | | - Record<string, Partial<AutofixAutomationSettings>> |
46 | | - >({}); |
| 57 | + const {data: integrations, isPending: isPendingIntegrations} = useQuery({ |
| 58 | + ...organizationIntegrationsCodingAgents(organization), |
| 59 | + select: data => data.json.integrations ?? [], |
| 60 | + }); |
47 | 61 |
|
48 | 62 | const {mutate: updateBulkAutofixAutomationSettings} = |
49 | 63 | useUpdateBulkAutofixAutomationSettings({ |
50 | 64 | onSuccess: (_data, variables) => { |
51 | | - const {projectIds, ...rest} = variables; |
52 | | - setMutations(prev => { |
53 | | - const updated = {...prev}; |
54 | | - projectIds.forEach(projectId => { |
55 | | - updated[projectId] = { |
56 | | - ...prev[projectId], |
57 | | - ...rest, |
58 | | - }; |
59 | | - }); |
60 | | - return updated; |
| 65 | + const {projectIds, ...updates} = variables; |
| 66 | + const projectIdSet = new Set(projectIds); |
| 67 | + |
| 68 | + queryClient.setQueryData(autofixSettingsQueryOptions.queryKey, oldData => { |
| 69 | + if (!oldData) { |
| 70 | + return oldData; |
| 71 | + } |
| 72 | + return { |
| 73 | + ...oldData, |
| 74 | + pages: oldData.pages.map(page => ({ |
| 75 | + ...page, |
| 76 | + json: page.json.map(setting => |
| 77 | + projectIdSet.has(String(setting.projectId)) |
| 78 | + ? { |
| 79 | + ...setting, |
| 80 | + ...(updates.autofixAutomationTuning !== undefined && { |
| 81 | + autofixAutomationTuning: updates.autofixAutomationTuning, |
| 82 | + }), |
| 83 | + ...(updates.automatedRunStoppingPoint !== undefined && { |
| 84 | + automatedRunStoppingPoint: updates.automatedRunStoppingPoint, |
| 85 | + }), |
| 86 | + } |
| 87 | + : setting |
| 88 | + ), |
| 89 | + })), |
| 90 | + }; |
61 | 91 | }); |
| 92 | + |
| 93 | + for (const projectId of projectIds) { |
| 94 | + if (updates.autofixAutomationTuning !== undefined) { |
| 95 | + ProjectsStore.onUpdateSuccess({ |
| 96 | + id: projectId, |
| 97 | + autofixAutomationTuning: updates.autofixAutomationTuning ?? undefined, |
| 98 | + }); |
| 99 | + } |
| 100 | + } |
62 | 101 | }, |
63 | 102 | }); |
64 | 103 |
|
65 | 104 | const autofixSettingsByProjectId = useMemo( |
66 | 105 | () => |
67 | 106 | new Map( |
68 | | - autofixAutomationSettings.flatMap(page => |
69 | | - page.map(setting => [String(setting.projectId), setting]) |
70 | | - ) |
| 107 | + (autofixAutomationSettings ?? []).map(setting => [ |
| 108 | + String(setting.projectId), |
| 109 | + setting, |
| 110 | + ]) |
71 | 111 | ), |
72 | 112 | [autofixAutomationSettings] |
73 | 113 | ); |
@@ -179,14 +219,10 @@ export default function SeerProjectTable() { |
179 | 219 | filteredProjects.map(project => ( |
180 | 220 | <SeerProjectTableRow |
181 | 221 | key={project.id} |
| 222 | + autofixSettings={autofixSettingsByProjectId.get(project.id)} |
| 223 | + integrations={integrations ?? []} |
| 224 | + isPendingIntegrations={isPendingIntegrations} |
182 | 225 | project={project} |
183 | | - isFetchingSettings={isFetchingSettings} |
184 | | - autofixSettings={{ |
185 | | - ...getDefaultAutofixSettings(project.id), |
186 | | - ...autofixSettingsByProjectId.get(project.id), |
187 | | - ...mutationData[project.id], |
188 | | - }} |
189 | | - updateBulkAutofixAutomationSettings={updateBulkAutofixAutomationSettings} |
190 | 226 | /> |
191 | 227 | )) |
192 | 228 | )} |
@@ -251,5 +287,6 @@ const FiltersContainer = styled('div')` |
251 | 287 | `; |
252 | 288 |
|
253 | 289 | const SimpleTableWithColumns = styled(SimpleTable)` |
254 | | - grid-template-columns: max-content 1fr repeat(4, max-content); |
| 290 | + grid-template-columns: 3fr minmax(300px, 1fr) repeat(2, max-content); |
| 291 | + overflow: visible; |
255 | 292 | `; |
0 commit comments