@@ -13,7 +13,10 @@ import {ButtonBar} from 'sentry/components/core/button/buttonBar';
13
13
import { Checkbox } from 'sentry/components/core/checkbox' ;
14
14
import { Flex } from 'sentry/components/core/layout' ;
15
15
import { DropdownMenu } from 'sentry/components/dropdownMenu' ;
16
- import { useProjectSeerPreferences } from 'sentry/components/events/autofix/preferences/hooks/useProjectSeerPreferences' ;
16
+ import {
17
+ makeProjectSeerPreferencesQueryKey ,
18
+ useProjectSeerPreferences ,
19
+ } from 'sentry/components/events/autofix/preferences/hooks/useProjectSeerPreferences' ;
17
20
import LoadingError from 'sentry/components/loadingError' ;
18
21
import LoadingIndicator from 'sentry/components/loadingIndicator' ;
19
22
import Panel from 'sentry/components/panels/panel' ;
@@ -290,6 +293,97 @@ export function SeerAutomationProjectList() {
290
293
}
291
294
}
292
295
296
+ async function setAllToRecommended ( ) {
297
+ addLoadingMessage ( 'Setting all projects to recommended settings...' , {
298
+ duration : 30000 ,
299
+ } ) ;
300
+ try {
301
+ // Get preferences for all filtered projects to check repo counts
302
+ const projectPreferences = await Promise . all (
303
+ filteredProjects . map ( async project => {
304
+ try {
305
+ const response = await queryClient . fetchQuery ( {
306
+ queryKey : [
307
+ makeProjectSeerPreferencesQueryKey ( organization . slug , project . slug ) ,
308
+ ] ,
309
+ queryFn : ( ) =>
310
+ api . requestPromise (
311
+ makeProjectSeerPreferencesQueryKey ( organization . slug , project . slug )
312
+ ) ,
313
+ staleTime : 60000 ,
314
+ } ) ;
315
+ return {
316
+ project,
317
+ repoCount : response [ 0 ] . preference ?. repositories ?. length || 0 ,
318
+ } ;
319
+ } catch ( err ) {
320
+ // If we can't get preferences, assume no repos
321
+ return {
322
+ project,
323
+ repoCount : 0 ,
324
+ } ;
325
+ }
326
+ } )
327
+ ) ;
328
+
329
+ // Update all projects
330
+ await Promise . all (
331
+ projectPreferences . map ( ( { project, repoCount} ) => {
332
+ const updateData : any = { } ;
333
+
334
+ if ( ! project . seerScannerAutomation ) {
335
+ updateData . seerScannerAutomation = true ; // Set scanner to on for all projects
336
+ }
337
+
338
+ // Sets fixes to highly actionable if repos are connected and no automation already set
339
+ if (
340
+ ( ! project . autofixAutomationTuning ||
341
+ project . autofixAutomationTuning === 'off' ) &&
342
+ repoCount > 0
343
+ ) {
344
+ updateData . autofixAutomationTuning = 'low' ;
345
+ }
346
+
347
+ // no updates, so don't make a request
348
+ if ( Object . keys ( updateData ) . length === 0 ) {
349
+ return Promise . resolve ( ) ;
350
+ }
351
+
352
+ // make the request
353
+ return api . requestPromise ( `/projects/${ organization . slug } /${ project . slug } /` , {
354
+ method : 'PUT' ,
355
+ data : updateData ,
356
+ } ) ;
357
+ } )
358
+ ) ;
359
+
360
+ const projectsWithNoRepos = projectPreferences . filter (
361
+ ( { repoCount} ) => repoCount === 0
362
+ ) . length ;
363
+ const updatedProjectsCount = projectPreferences . length ;
364
+
365
+ if ( projectsWithNoRepos > 0 ) {
366
+ addSuccessMessage (
367
+ `Settings applied to ${ updatedProjectsCount } project(s). ${ projectsWithNoRepos } project(s) have no repos connected and were skipped.`
368
+ ) ;
369
+ } else {
370
+ addSuccessMessage ( `Settings applied to ${ updatedProjectsCount } projects.` ) ;
371
+ }
372
+ } catch ( err ) {
373
+ addErrorMessage ( 'Failed to update some projects' ) ;
374
+ } finally {
375
+ // Invalidate queries for all filtered projects
376
+ filteredProjects . forEach ( project => {
377
+ queryClient . invalidateQueries ( {
378
+ queryKey : makeDetailedProjectQueryKey ( {
379
+ orgSlug : organization . slug ,
380
+ projectSlug : project . slug ,
381
+ } ) ,
382
+ } ) ;
383
+ } ) ;
384
+ }
385
+ }
386
+
293
387
const actionMenuItems = SEER_THRESHOLD_MAP . map ( key => ( {
294
388
key,
295
389
label : getSeerDropdownLabel ( key ) ,
@@ -312,11 +406,24 @@ export function SeerAutomationProjectList() {
312
406
return (
313
407
< Fragment >
314
408
< SearchWrapper >
315
- < SearchBar
316
- query = { search }
317
- onChange = { handleSearchChange }
318
- placeholder = { t ( 'Search projects' ) }
319
- />
409
+ < SearchBarWrapper >
410
+ < SearchBar
411
+ query = { search }
412
+ onChange = { handleSearchChange }
413
+ placeholder = { t ( 'Search projects' ) }
414
+ />
415
+ </ SearchBarWrapper >
416
+ < Button
417
+ size = "sm"
418
+ priority = "primary"
419
+ onClick = { setAllToRecommended }
420
+ disabled = { filteredProjects . length === 0 }
421
+ title = { t (
422
+ 'For all projects, turns Issue Scans on, and if repos are connected, sets Issue Fixes to run automatically.'
423
+ ) }
424
+ >
425
+ { t ( 'Turn On for Recommended Projects' ) }
426
+ </ Button >
320
427
</ SearchWrapper >
321
428
< Panel >
322
429
< PanelHeader hasButtons >
@@ -390,6 +497,13 @@ export function SeerAutomationProjectList() {
390
497
391
498
const SearchWrapper = styled ( 'div' ) `
392
499
margin-bottom: ${ space ( 2 ) } ;
500
+ display: flex;
501
+ gap: ${ space ( 2 ) } ;
502
+ align-items: center;
503
+ ` ;
504
+
505
+ const SearchBarWrapper = styled ( 'div' ) `
506
+ flex: 1;
393
507
` ;
394
508
395
509
const SeerValue = styled ( 'div' ) `
0 commit comments