diff --git a/static/app/views/issueList/pages/dynamicGrouping.tsx b/static/app/views/issueList/pages/dynamicGrouping.tsx index eb662386769b01..e4bd414121a7b0 100644 --- a/static/app/views/issueList/pages/dynamicGrouping.tsx +++ b/static/app/views/issueList/pages/dynamicGrouping.tsx @@ -1,10 +1,12 @@ -import {Fragment, useMemo, useState} from 'react'; +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'; +import {bulkUpdate} from 'sentry/actionCreators/group'; +import {openConfirmModal} from 'sentry/components/confirm'; import {Button} from 'sentry/components/core/button'; import {ButtonBar} from 'sentry/components/core/button/buttonBar'; import {Checkbox} from 'sentry/components/core/checkbox'; @@ -31,6 +33,7 @@ import { IconClock, IconClose, IconCopy, + IconEllipsis, IconFire, IconFix, IconSeer, @@ -40,8 +43,10 @@ import { import {t, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Group} from 'sentry/types/group'; +import {GroupStatus, GroupSubstatus} from 'sentry/types/group'; import {getMessage, getTitle} from 'sentry/utils/events'; import {useApiQuery} from 'sentry/utils/queryClient'; +import useApi from 'sentry/utils/useApi'; import useCopyToClipboard from 'sentry/utils/useCopyToClipboard'; import useOrganization from 'sentry/utils/useOrganization'; import usePageFilters from 'sentry/utils/usePageFilters'; @@ -361,7 +366,9 @@ function ClusterCard({ onTagClick?: (tag: string) => void; selectedTags?: Set; }) { + const api = useApi(); const organization = useOrganization(); + const {selection} = usePageFilters(); const [showDescription, setShowDescription] = useState(false); const clusterStats = useClusterStats(cluster.group_ids); const {copy} = useCopyToClipboard(); @@ -379,9 +386,64 @@ function ClusterCard({ copy(formatClusterInfoForClipboard(cluster)); }; + const handleResolve = useCallback(() => { + openConfirmModal({ + header: t('Resolve All Issues in Cluster'), + message: t( + 'Are you sure you want to resolve all %s issues in this cluster?.', + cluster.group_ids.length + ), + confirmText: t('Resolve All'), + onConfirm: () => { + bulkUpdate( + api, + { + orgId: organization.slug, + itemIds: cluster.group_ids.map(String), + data: {status: GroupStatus.RESOLVED}, + project: selection.projects, + environment: selection.environments, + ...selection.datetime, + }, + {} + ); + }, + }); + }, [api, cluster.group_ids, organization.slug, selection]); + + const handleArchive = useCallback(() => { + openConfirmModal({ + header: t('Archive All Issues in Cluster'), + message: t( + 'Are you sure you want to archive all %s issues in this cluster?.', + cluster.group_ids.length + ), + confirmText: t('Archive All'), + onConfirm: () => { + bulkUpdate( + api, + { + orgId: organization.slug, + itemIds: cluster.group_ids.map(String), + data: { + status: GroupStatus.IGNORED, + statusDetails: {}, + substatus: GroupSubstatus.ARCHIVED_UNTIL_ESCALATING, + }, + project: selection.projects, + environment: selection.environments, + ...selection.datetime, + }, + {} + ); + }, + }); + }, [api, cluster.group_ids, organization.slug, selection]); + + const handleDismiss = () => {}; + return ( - {/* Zone 1: Title + Description (Primary Focus) */} {renderWithInlineCode(cluster.title)} {showDescription ? ( - {cluster.description} + + {cluster.description} + setShowDescription(false)}> + {t('Collapse summary')} + + ) : ( setShowDescription(true)}> {t('View summary')} @@ -402,7 +469,6 @@ function ClusterCard({ )} - {/* Zone 2: Stats (Secondary Context) */} {cluster.fixability_score !== null && cluster.fixability_score !== undefined && ( @@ -469,7 +535,6 @@ function ClusterCard({ )} - {/* Zone 3: Nested Issues (Detail Content) */} @@ -481,7 +546,6 @@ function ClusterCard({ - {/* Zone 4: Actions (Tertiary) */} + ( +