-
-
Notifications
You must be signed in to change notification settings - Fork 4.5k
feat(issues): Allow disabling of filters in top group demo, add tags #104037
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,6 +1,7 @@ | ||
| import {Fragment, useCallback, useMemo, useState} from 'react'; | ||
| import styled from '@emotion/styled'; | ||
|
|
||
| import {Tag} from '@sentry/scraps/badge'; | ||
| import {Container, Flex} from '@sentry/scraps/layout'; | ||
| import {Heading, Text} from '@sentry/scraps/text'; | ||
|
|
||
|
|
@@ -243,6 +244,13 @@ function ClusterCard({ | |
| )} | ||
| </Fragment> | ||
| )} | ||
| {cluster.tags && cluster.tags.length > 0 && ( | ||
| <Flex wrap="wrap" gap="xs"> | ||
| {cluster.tags.map(tag => ( | ||
| <Tag key={tag}>{tag}</Tag> | ||
| ))} | ||
| </Flex> | ||
| )} | ||
| </Flex> | ||
| <IssueCountBadge> | ||
| <IssueCountNumber>{issueCount}</IssueCountNumber> | ||
|
|
@@ -346,6 +354,7 @@ function DynamicGrouping() { | |
| null | ||
| ); | ||
| const [jsonError, setJsonError] = useState<string | null>(null); | ||
| const [disableFilters, setDisableFilters] = useState(false); | ||
|
|
||
| // Fetch cluster data from API | ||
| const {data: topIssuesResponse, isPending} = useApiQuery<TopIssuesResponse>( | ||
|
|
@@ -377,6 +386,7 @@ function DynamicGrouping() { | |
| setCustomClusterData(null); | ||
| setJsonInputValue(''); | ||
| setJsonError(null); | ||
| setDisableFilters(false); | ||
| }, []); | ||
|
|
||
| const clusterData = customClusterData ?? topIssuesResponse?.data ?? []; | ||
|
|
@@ -419,32 +429,36 @@ function DynamicGrouping() { | |
| setRemovedClusterIds(prev => new Set([...prev, clusterId])); | ||
| }; | ||
|
|
||
| const filteredAndSortedClusters = clusterData | ||
| .filter(cluster => { | ||
| if (removedClusterIds.has(cluster.cluster_id)) return false; | ||
|
|
||
| const fixabilityScore = (cluster.fixability_score ?? 0) * 100; | ||
| if (fixabilityScore < minFixabilityScore) return false; | ||
|
|
||
| if (filterByAssignedToMe) { | ||
| if (!cluster.assignedTo?.length) return false; | ||
| return cluster.assignedTo.some( | ||
| entity => | ||
| (entity.type === 'user' && entity.id === user.id) || | ||
| (entity.type === 'team' && userTeams.some(team => team.id === entity.id)) | ||
| ); | ||
| } | ||
|
|
||
| if (isTeamFilterActive) { | ||
| if (!cluster.assignedTo?.length) return false; | ||
| return cluster.assignedTo.some( | ||
| entity => entity.type === 'team' && selectedTeamIds.has(entity.id) | ||
| ); | ||
| } | ||
|
|
||
| return true; | ||
| }) | ||
| .sort((a, b) => (b.fixability_score ?? 0) - (a.fixability_score ?? 0)); | ||
| // When using custom JSON data with filters disabled, skip all filtering and sorting | ||
| const shouldSkipFilters = isUsingCustomData && disableFilters; | ||
| const filteredAndSortedClusters = shouldSkipFilters | ||
| ? clusterData.filter(cluster => !removedClusterIds.has(cluster.cluster_id)) | ||
| : clusterData | ||
| .filter(cluster => { | ||
| if (removedClusterIds.has(cluster.cluster_id)) return false; | ||
|
|
||
| const fixabilityScore = (cluster.fixability_score ?? 0) * 100; | ||
| if (fixabilityScore < minFixabilityScore) return false; | ||
|
|
||
| if (filterByAssignedToMe) { | ||
| if (!cluster.assignedTo?.length) return false; | ||
| return cluster.assignedTo.some( | ||
| entity => | ||
| (entity.type === 'user' && entity.id === user.id) || | ||
| (entity.type === 'team' && userTeams.some(team => team.id === entity.id)) | ||
| ); | ||
| } | ||
|
|
||
| if (isTeamFilterActive) { | ||
| if (!cluster.assignedTo?.length) return false; | ||
| return cluster.assignedTo.some( | ||
| entity => entity.type === 'team' && selectedTeamIds.has(entity.id) | ||
| ); | ||
| } | ||
|
|
||
| return true; | ||
| }) | ||
| .sort((a, b) => (b.fixability_score ?? 0) - (a.fixability_score ?? 0)); | ||
|
|
||
| const totalIssues = filteredAndSortedClusters.flatMap(c => c.group_ids).length; | ||
|
|
||
|
|
@@ -506,6 +520,17 @@ function DynamicGrouping() { | |
| {jsonError} | ||
| </Text> | ||
| )} | ||
| <Flex gap="sm" align="center" style={{marginTop: space(1.5)}}> | ||
| <Checkbox | ||
| checked={disableFilters} | ||
| onChange={e => setDisableFilters(e.target.checked)} | ||
| aria-label={t('Disable filters and sorting')} | ||
| size="sm" | ||
| /> | ||
| <Text size="sm" variant="muted"> | ||
| {t('Disable filters and sorting')} | ||
| </Text> | ||
| </Flex> | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: Disable filters checkbox inaccessible after loading dataThe |
||
| <Flex gap="sm" style={{marginTop: space(1)}}> | ||
| <Button size="sm" priority="primary" onClick={handleParseJson}> | ||
| {t('Parse and Load')} | ||
|
|
@@ -532,77 +557,80 @@ function DynamicGrouping() { | |
| totalIssues, | ||
| filteredAndSortedClusters.length | ||
| )} | ||
| {shouldSkipFilters && ` ${t('(filters disabled)')}`} | ||
| </Text> | ||
|
|
||
| <Container | ||
| padding="sm" | ||
| border="primary" | ||
| radius="md" | ||
| background="primary" | ||
| marginTop="md" | ||
| > | ||
| <Disclosure> | ||
| <Disclosure.Title> | ||
| <Text size="sm" bold> | ||
| {t('More Filters')} | ||
| </Text> | ||
| </Disclosure.Title> | ||
| <Disclosure.Content> | ||
| <Flex direction="column" gap="md" paddingTop="md"> | ||
| <Flex gap="sm" align="center"> | ||
| <Checkbox | ||
| checked={filterByAssignedToMe} | ||
| onChange={e => handleAssignedToMeChange(e.target.checked)} | ||
| aria-label={t('Show only issues assigned to me')} | ||
| size="sm" | ||
| disabled={isTeamFilterActive} | ||
| /> | ||
| <FilterLabel disabled={isTeamFilterActive}> | ||
| {t('Only show issues assigned to me')} | ||
| </FilterLabel> | ||
| </Flex> | ||
|
|
||
| {teamsInData.length > 0 && ( | ||
| <Flex direction="column" gap="sm"> | ||
| <FilterLabel disabled={filterByAssignedToMe}> | ||
| {t('Filter by teams')} | ||
| {!shouldSkipFilters && ( | ||
| <Container | ||
| padding="sm" | ||
| border="primary" | ||
| radius="md" | ||
| background="primary" | ||
| marginTop="md" | ||
| > | ||
| <Disclosure> | ||
| <Disclosure.Title> | ||
| <Text size="sm" bold> | ||
| {t('More Filters')} | ||
| </Text> | ||
| </Disclosure.Title> | ||
| <Disclosure.Content> | ||
| <Flex direction="column" gap="md" paddingTop="md"> | ||
| <Flex gap="sm" align="center"> | ||
| <Checkbox | ||
| checked={filterByAssignedToMe} | ||
| onChange={e => handleAssignedToMeChange(e.target.checked)} | ||
| aria-label={t('Show only issues assigned to me')} | ||
| size="sm" | ||
| disabled={isTeamFilterActive} | ||
| /> | ||
| <FilterLabel disabled={isTeamFilterActive}> | ||
| {t('Only show issues assigned to me')} | ||
| </FilterLabel> | ||
| <Flex direction="column" gap="xs" style={{paddingLeft: 8}}> | ||
| {teamsInData.map(team => ( | ||
| <Flex key={team.id} gap="sm" align="center"> | ||
| <Checkbox | ||
| checked={selectedTeamIds.has(team.id)} | ||
| onChange={() => handleTeamToggle(team.id)} | ||
| aria-label={t('Filter by team %s', team.name)} | ||
| size="sm" | ||
| disabled={filterByAssignedToMe} | ||
| /> | ||
| <FilterLabel disabled={filterByAssignedToMe}> | ||
| #{team.name} | ||
| </FilterLabel> | ||
| </Flex> | ||
| ))} | ||
| </Flex> | ||
|
|
||
| {teamsInData.length > 0 && ( | ||
| <Flex direction="column" gap="sm"> | ||
| <FilterLabel disabled={filterByAssignedToMe}> | ||
| {t('Filter by teams')} | ||
| </FilterLabel> | ||
| <Flex direction="column" gap="xs" style={{paddingLeft: 8}}> | ||
| {teamsInData.map(team => ( | ||
| <Flex key={team.id} gap="sm" align="center"> | ||
| <Checkbox | ||
| checked={selectedTeamIds.has(team.id)} | ||
| onChange={() => handleTeamToggle(team.id)} | ||
| aria-label={t('Filter by team %s', team.name)} | ||
| size="sm" | ||
| disabled={filterByAssignedToMe} | ||
| /> | ||
| <FilterLabel disabled={filterByAssignedToMe}> | ||
| #{team.name} | ||
| </FilterLabel> | ||
| </Flex> | ||
| ))} | ||
| </Flex> | ||
| </Flex> | ||
| )} | ||
|
|
||
| <Flex gap="sm" align="center"> | ||
| <Text size="sm" variant="muted"> | ||
| {t('Minimum fixability score (%)')} | ||
| </Text> | ||
| <NumberInput | ||
| min={0} | ||
| max={100} | ||
| value={minFixabilityScore} | ||
| onChange={value => setMinFixabilityScore(value ?? 0)} | ||
| aria-label={t('Minimum fixability score')} | ||
| size="sm" | ||
| /> | ||
| </Flex> | ||
| )} | ||
|
|
||
| <Flex gap="sm" align="center"> | ||
| <Text size="sm" variant="muted"> | ||
| {t('Minimum fixability score (%)')} | ||
| </Text> | ||
| <NumberInput | ||
| min={0} | ||
| max={100} | ||
| value={minFixabilityScore} | ||
| onChange={value => setMinFixabilityScore(value ?? 0)} | ||
| aria-label={t('Minimum fixability score')} | ||
| size="sm" | ||
| /> | ||
| </Flex> | ||
| </Flex> | ||
| </Disclosure.Content> | ||
| </Disclosure> | ||
| </Container> | ||
| </Disclosure.Content> | ||
| </Disclosure> | ||
| </Container> | ||
| )} | ||
| </Fragment> | ||
| )} | ||
| </HeaderSection> | ||
|
Comment on lines
+626
to
636
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Bug: 🔍 Detailed AnalysisThe 💡 Suggested FixModify the 🤖 Prompt for AI AgentDid we get this right? 👍 / 👎 to inform future reviews. |
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug:
removedClusterIdsstate is not reset when custom data is cleared.Severity: CRITICAL | Confidence: High
🔍 Detailed Analysis
When
handleClearCustomDatais called, theremovedClusterIdsstate is not reset. This allows previously 'resolved' cluster IDs to persist, causing clusters with those IDs to be unexpectedly hidden if they appear in newly loaded custom JSON data, leading to incorrect filtering.💡 Suggested Fix
Add
setRemovedClusterIds(new Set())to thehandleClearCustomDatafunction to ensureremovedClusterIdsis cleared upon custom data removal.🤖 Prompt for AI Agent
Did we get this right? 👍 / 👎 to inform future reviews.
Reference ID:
3642060