import {useMemo} from 'react'; import styled from '@emotion/styled'; import ActorAvatar from 'sentry/components/avatar/actorAvatar'; import Count from 'sentry/components/count'; import EventOrGroupExtraDetails from 'sentry/components/eventOrGroupExtraDetails'; import LoadingError from 'sentry/components/loadingError'; import LoadingIndicator from 'sentry/components/loadingIndicator'; import Panel from 'sentry/components/panels/panel'; import PanelHeader from 'sentry/components/panels/panelHeader'; import PanelItem from 'sentry/components/panels/panelItem'; import {IconWrapper} from 'sentry/components/sidebarSection'; import GroupChart from 'sentry/components/stream/groupChart'; import {IconUser} from 'sentry/icons'; import {t, tct, tn} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {Group, Organization} from 'sentry/types'; import type {TraceErrorOrIssue} from 'sentry/utils/performance/quickTrace/types'; import {useApiQuery} from 'sentry/utils/queryClient'; import type { TraceTree, TraceTreeNode, } from 'sentry/views/performance/newTraceDetails/traceModels/traceTree'; import {TraceDrawerComponents} from '../styles'; import {IssueSummary} from './issueSummary'; type IssueProps = { issue: TraceErrorOrIssue; organization: Organization; }; const MAX_DISPLAYED_ISSUES_COUNT = 3; const TABLE_WIDTH_BREAKPOINTS = { FIRST: 800, SECOND: 600, THIRD: 500, FOURTH: 400, }; function Issue(props: IssueProps) { const { isLoading, data: fetchedIssue, isError, } = useApiQuery( [ `/issues/${props.issue.issue_id}/`, { query: { collapse: 'release', expand: 'inbox', }, }, ], { staleTime: 2 * 60 * 1000, } ); return isLoading ? ( ) : fetchedIssue ? ( {fetchedIssue.assignedTo ? ( ) : ( )} ) : isError ? ( ) : null; } type IssueListProps = { issues: TraceErrorOrIssue[]; node: TraceTreeNode; organization: Organization; }; export function IssueList({issues, node, organization}: IssueListProps) { if (!issues.length) { return null; } return ( {issues.slice(0, MAX_DISPLAYED_ISSUES_COUNT).map((issue, index) => ( ))} ); } function IssueListHeader({node}: {node: TraceTreeNode}) { const {errors, performance_issues} = node; const [singular, plural] = useMemo((): [string, string] => { const label = [t('Issue'), t('Issues')] as [string, string]; for (const event of errors) { if (event.level === 'error' || event.level === 'fatal') { return [t('Error'), t('Errors')]; } } return label; }, [errors]); return ( {errors.size + performance_issues.size > MAX_DISPLAYED_ISSUES_COUNT ? tct(`[count]+ issues, [link]`, { count: MAX_DISPLAYED_ISSUES_COUNT, link: {t('View All')}, }) : errors.size > 0 && performance_issues.size === 0 ? tct('[count] [text]', { count: errors.size, text: errors.size > 1 ? plural : singular, }) : performance_issues.size > 0 && errors.size === 0 ? tct('[count] [text]', { count: performance_issues.size, text: tn( 'Performance issue', 'Performance Issues', performance_issues.size ), }) : tct( '[errors] [errorsText] and [performance_issues] [performanceIssuesText]', { errors: errors.size, performance_issues: performance_issues.size, errorsText: errors.size > 1 ? plural : singular, performanceIssuesText: tn( 'performance issue', 'performance issues', performance_issues.size ), } )} {t('Graph')} {t('Events')} {t('Users')} {t('Assignee')} ); } const StyledIssuesLink = styled(TraceDrawerComponents.IssuesLink)` margin-left: ${space(0.5)}; `; const Heading = styled('div')` display: flex; align-self: center; margin: 0 ${space(2)}; width: 60px; color: ${p => p.theme.subText}; `; const IssueHeading = styled(Heading)` flex: 1; width: 66.66%; @media (min-width: ${p => p.theme.breakpoints.medium}) { width: 50%; } `; const GraphHeading = styled(Heading)` width: 160px; display: flex; justify-content: center; @container (width < ${TABLE_WIDTH_BREAKPOINTS.FIRST}px) { display: none; } `; const EventsHeading = styled(Heading)` @container (width < ${TABLE_WIDTH_BREAKPOINTS.SECOND}px) { display: none; } `; const UsersHeading = styled(Heading)` display: flex; justify-content: center; @container (width < ${TABLE_WIDTH_BREAKPOINTS.THIRD}px) { display: none; } `; const AssigneeHeading = styled(Heading)` @container (width < ${TABLE_WIDTH_BREAKPOINTS.FOURTH}px) { display: none; } `; const StyledPanel = styled(Panel)` container-type: inline-size; `; const StyledPanelHeader = styled(PanelHeader)` padding-top: ${space(1)}; padding-bottom: ${space(1)}; `; const StyledLoadingIndicatorWrapper = styled('div')` display: flex; justify-content: center; width: 100%; padding: ${space(2)} 0; height: 84px; /* Add a border between two rows of loading issue states */ & + & { border-top: 1px solid ${p => p.theme.border}; } `; const StyledIconWrapper = styled(IconWrapper)` margin: 0; `; const IssueSummaryWrapper = styled('div')` overflow: hidden; flex: 1; width: 66.66%; @media (min-width: ${p => p.theme.breakpoints.medium}) { width: 50%; } `; const ColumnWrapper = styled('div')` display: flex; justify-content: flex-end; align-self: center; width: 60px; margin: 0 ${space(2)}; `; const EventsWrapper = styled(ColumnWrapper)` @container (width < ${TABLE_WIDTH_BREAKPOINTS.SECOND}px) { display: none; } `; const UserCountWrapper = styled(ColumnWrapper)` @container (width < ${TABLE_WIDTH_BREAKPOINTS.THIRD}px) { display: none; } `; const AssineeWrapper = styled(ColumnWrapper)` @container (width < ${TABLE_WIDTH_BREAKPOINTS.FOURTH}px) { display: none; } `; const ChartWrapper = styled('div')` width: 200px; align-self: center; @container (width < ${TABLE_WIDTH_BREAKPOINTS.FIRST}px) { display: none; } `; const PrimaryCount = styled(Count)` font-size: ${p => p.theme.fontSizeLarge}; font-variant-numeric: tabular-nums; `; const StyledPanelItem = styled(PanelItem)` padding-top: ${space(1)}; padding-bottom: ${space(1)}; height: 84px; `;