11import { Fragment , useCallback , useMemo , useState } from 'react' ;
22import styled from '@emotion/styled' ;
33
4+ import { Tag } from '@sentry/scraps/badge' ;
45import { Container , Flex } from '@sentry/scraps/layout' ;
56import { Heading , Text } from '@sentry/scraps/text' ;
67
@@ -243,6 +244,13 @@ function ClusterCard({
243244 ) }
244245 </ Fragment >
245246 ) }
247+ { cluster . tags && cluster . tags . length > 0 && (
248+ < Flex wrap = "wrap" gap = "xs" >
249+ { cluster . tags . map ( tag => (
250+ < Tag key = { tag } > { tag } </ Tag >
251+ ) ) }
252+ </ Flex >
253+ ) }
246254 </ Flex >
247255 < IssueCountBadge >
248256 < IssueCountNumber > { issueCount } </ IssueCountNumber >
@@ -346,6 +354,7 @@ function DynamicGrouping() {
346354 null
347355 ) ;
348356 const [ jsonError , setJsonError ] = useState < string | null > ( null ) ;
357+ const [ disableFilters , setDisableFilters ] = useState ( false ) ;
349358
350359 // Fetch cluster data from API
351360 const { data : topIssuesResponse , isPending} = useApiQuery < TopIssuesResponse > (
@@ -377,6 +386,7 @@ function DynamicGrouping() {
377386 setCustomClusterData ( null ) ;
378387 setJsonInputValue ( '' ) ;
379388 setJsonError ( null ) ;
389+ setDisableFilters ( false ) ;
380390 } , [ ] ) ;
381391
382392 const clusterData = customClusterData ?? topIssuesResponse ?. data ?? [ ] ;
@@ -419,32 +429,36 @@ function DynamicGrouping() {
419429 setRemovedClusterIds ( prev => new Set ( [ ...prev , clusterId ] ) ) ;
420430 } ;
421431
422- const filteredAndSortedClusters = clusterData
423- . filter ( cluster => {
424- if ( removedClusterIds . has ( cluster . cluster_id ) ) return false ;
425-
426- const fixabilityScore = ( cluster . fixability_score ?? 0 ) * 100 ;
427- if ( fixabilityScore < minFixabilityScore ) return false ;
428-
429- if ( filterByAssignedToMe ) {
430- if ( ! cluster . assignedTo ?. length ) return false ;
431- return cluster . assignedTo . some (
432- entity =>
433- ( entity . type === 'user' && entity . id === user . id ) ||
434- ( entity . type === 'team' && userTeams . some ( team => team . id === entity . id ) )
435- ) ;
436- }
437-
438- if ( isTeamFilterActive ) {
439- if ( ! cluster . assignedTo ?. length ) return false ;
440- return cluster . assignedTo . some (
441- entity => entity . type === 'team' && selectedTeamIds . has ( entity . id )
442- ) ;
443- }
444-
445- return true ;
446- } )
447- . sort ( ( a , b ) => ( b . fixability_score ?? 0 ) - ( a . fixability_score ?? 0 ) ) ;
432+ // When using custom JSON data with filters disabled, skip all filtering and sorting
433+ const shouldSkipFilters = isUsingCustomData && disableFilters ;
434+ const filteredAndSortedClusters = shouldSkipFilters
435+ ? clusterData . filter ( cluster => ! removedClusterIds . has ( cluster . cluster_id ) )
436+ : clusterData
437+ . filter ( cluster => {
438+ if ( removedClusterIds . has ( cluster . cluster_id ) ) return false ;
439+
440+ const fixabilityScore = ( cluster . fixability_score ?? 0 ) * 100 ;
441+ if ( fixabilityScore < minFixabilityScore ) return false ;
442+
443+ if ( filterByAssignedToMe ) {
444+ if ( ! cluster . assignedTo ?. length ) return false ;
445+ return cluster . assignedTo . some (
446+ entity =>
447+ ( entity . type === 'user' && entity . id === user . id ) ||
448+ ( entity . type === 'team' && userTeams . some ( team => team . id === entity . id ) )
449+ ) ;
450+ }
451+
452+ if ( isTeamFilterActive ) {
453+ if ( ! cluster . assignedTo ?. length ) return false ;
454+ return cluster . assignedTo . some (
455+ entity => entity . type === 'team' && selectedTeamIds . has ( entity . id )
456+ ) ;
457+ }
458+
459+ return true ;
460+ } )
461+ . sort ( ( a , b ) => ( b . fixability_score ?? 0 ) - ( a . fixability_score ?? 0 ) ) ;
448462
449463 const totalIssues = filteredAndSortedClusters . flatMap ( c => c . group_ids ) . length ;
450464
@@ -506,6 +520,17 @@ function DynamicGrouping() {
506520 { jsonError }
507521 </ Text >
508522 ) }
523+ < Flex gap = "sm" align = "center" style = { { marginTop : space ( 1.5 ) } } >
524+ < Checkbox
525+ checked = { disableFilters }
526+ onChange = { e => setDisableFilters ( e . target . checked ) }
527+ aria-label = { t ( 'Disable filters and sorting' ) }
528+ size = "sm"
529+ />
530+ < Text size = "sm" variant = "muted" >
531+ { t ( 'Disable filters and sorting' ) }
532+ </ Text >
533+ </ Flex >
509534 < Flex gap = "sm" style = { { marginTop : space ( 1 ) } } >
510535 < Button size = "sm" priority = "primary" onClick = { handleParseJson } >
511536 { t ( 'Parse and Load' ) }
@@ -532,77 +557,80 @@ function DynamicGrouping() {
532557 totalIssues ,
533558 filteredAndSortedClusters . length
534559 ) }
560+ { shouldSkipFilters && ` ${ t ( '(filters disabled)' ) } ` }
535561 </ Text >
536562
537- < Container
538- padding = "sm"
539- border = "primary"
540- radius = "md"
541- background = "primary"
542- marginTop = "md"
543- >
544- < Disclosure >
545- < Disclosure . Title >
546- < Text size = "sm" bold >
547- { t ( 'More Filters' ) }
548- </ Text >
549- </ Disclosure . Title >
550- < Disclosure . Content >
551- < Flex direction = "column" gap = "md" paddingTop = "md" >
552- < Flex gap = "sm" align = "center" >
553- < Checkbox
554- checked = { filterByAssignedToMe }
555- onChange = { e => handleAssignedToMeChange ( e . target . checked ) }
556- aria-label = { t ( 'Show only issues assigned to me' ) }
557- size = "sm"
558- disabled = { isTeamFilterActive }
559- />
560- < FilterLabel disabled = { isTeamFilterActive } >
561- { t ( 'Only show issues assigned to me' ) }
562- </ FilterLabel >
563- </ Flex >
564-
565- { teamsInData . length > 0 && (
566- < Flex direction = "column" gap = "sm" >
567- < FilterLabel disabled = { filterByAssignedToMe } >
568- { t ( 'Filter by teams' ) }
563+ { ! shouldSkipFilters && (
564+ < Container
565+ padding = "sm"
566+ border = "primary"
567+ radius = "md"
568+ background = "primary"
569+ marginTop = "md"
570+ >
571+ < Disclosure >
572+ < Disclosure . Title >
573+ < Text size = "sm" bold >
574+ { t ( 'More Filters' ) }
575+ </ Text >
576+ </ Disclosure . Title >
577+ < Disclosure . Content >
578+ < Flex direction = "column" gap = "md" paddingTop = "md" >
579+ < Flex gap = "sm" align = "center" >
580+ < Checkbox
581+ checked = { filterByAssignedToMe }
582+ onChange = { e => handleAssignedToMeChange ( e . target . checked ) }
583+ aria-label = { t ( 'Show only issues assigned to me' ) }
584+ size = "sm"
585+ disabled = { isTeamFilterActive }
586+ />
587+ < FilterLabel disabled = { isTeamFilterActive } >
588+ { t ( 'Only show issues assigned to me' ) }
569589 </ FilterLabel >
570- < Flex direction = "column" gap = "xs" style = { { paddingLeft : 8 } } >
571- { teamsInData . map ( team => (
572- < Flex key = { team . id } gap = "sm" align = "center" >
573- < Checkbox
574- checked = { selectedTeamIds . has ( team . id ) }
575- onChange = { ( ) => handleTeamToggle ( team . id ) }
576- aria-label = { t ( 'Filter by team %s' , team . name ) }
577- size = "sm"
578- disabled = { filterByAssignedToMe }
579- />
580- < FilterLabel disabled = { filterByAssignedToMe } >
581- #{ team . name }
582- </ FilterLabel >
583- </ Flex >
584- ) ) }
590+ </ Flex >
591+
592+ { teamsInData . length > 0 && (
593+ < Flex direction = "column" gap = "sm" >
594+ < FilterLabel disabled = { filterByAssignedToMe } >
595+ { t ( 'Filter by teams' ) }
596+ </ FilterLabel >
597+ < Flex direction = "column" gap = "xs" style = { { paddingLeft : 8 } } >
598+ { teamsInData . map ( team => (
599+ < Flex key = { team . id } gap = "sm" align = "center" >
600+ < Checkbox
601+ checked = { selectedTeamIds . has ( team . id ) }
602+ onChange = { ( ) => handleTeamToggle ( team . id ) }
603+ aria-label = { t ( 'Filter by team %s' , team . name ) }
604+ size = "sm"
605+ disabled = { filterByAssignedToMe }
606+ />
607+ < FilterLabel disabled = { filterByAssignedToMe } >
608+ #{ team . name }
609+ </ FilterLabel >
610+ </ Flex >
611+ ) ) }
612+ </ Flex >
585613 </ Flex >
614+ ) }
615+
616+ < Flex gap = "sm" align = "center" >
617+ < Text size = "sm" variant = "muted" >
618+ { t ( 'Minimum fixability score (%)' ) }
619+ </ Text >
620+ < NumberInput
621+ min = { 0 }
622+ max = { 100 }
623+ value = { minFixabilityScore }
624+ onChange = { value => setMinFixabilityScore ( value ?? 0 ) }
625+ aria-label = { t ( 'Minimum fixability score' ) }
626+ size = "sm"
627+ />
586628 </ Flex >
587- ) }
588-
589- < Flex gap = "sm" align = "center" >
590- < Text size = "sm" variant = "muted" >
591- { t ( 'Minimum fixability score (%)' ) }
592- </ Text >
593- < NumberInput
594- min = { 0 }
595- max = { 100 }
596- value = { minFixabilityScore }
597- onChange = { value => setMinFixabilityScore ( value ?? 0 ) }
598- aria-label = { t ( 'Minimum fixability score' ) }
599- size = "sm"
600- />
601629 </ Flex >
602- </ Flex >
603- </ Disclosure . Content >
604- </ Disclosure >
605- </ Container >
630+ </ Disclosure . Content >
631+ </ Disclosure >
632+ </ Container >
633+ ) }
606634 </ Fragment >
607635 ) }
608636 </ HeaderSection >
0 commit comments