Files
2024-02-15 17:01:50 +00:00

129 lines
3.6 KiB
TypeScript

import styled from '@emotion/styled';
import {Alert} from 'sentry/components/alert';
import {SectionHeading} from 'sentry/components/charts/styles';
import DeprecatedAsyncComponent from 'sentry/components/deprecatedAsyncComponent';
import Times from 'sentry/components/group/times';
import ProjectBadge from 'sentry/components/idBadge/projectBadge';
import Link from 'sentry/components/links/link';
import Placeholder from 'sentry/components/placeholder';
import SeenByList from 'sentry/components/seenByList';
import ShortId from 'sentry/components/shortId';
import GroupChart from 'sentry/components/stream/groupChart';
import {t} from 'sentry/locale';
import {space} from 'sentry/styles/space';
import type {Group} from 'sentry/types';
type Props = {
eventId: string;
groupId: string;
};
type State = {
group: Group;
};
class LinkedIssue extends DeprecatedAsyncComponent<
Props & DeprecatedAsyncComponent['props'],
State & DeprecatedAsyncComponent['state']
> {
getEndpoints(): ReturnType<DeprecatedAsyncComponent['getEndpoints']> {
const {groupId} = this.props;
const groupUrl = `/issues/${groupId}/`;
return [['group', groupUrl]];
}
renderLoading() {
return <Placeholder height="120px" bottomGutter={2} />;
}
renderError(error?: Error, disableLog = false): React.ReactNode {
const {errors} = this.state;
const hasNotFound = Object.values(errors).find(resp => resp && resp.status === 404);
if (hasNotFound) {
return (
<Alert type="warning" showIcon>
{t('The linked issue cannot be found. It may have been deleted, or merged.')}
</Alert>
);
}
return super.renderError(error, disableLog);
}
renderBody() {
const {eventId} = this.props;
const {group} = this.state;
const issueUrl = `${group.permalink}events/${eventId}/`;
return (
<Section>
<SectionHeading>{t('Event Issue')}</SectionHeading>
<StyledIssueCard>
<IssueCardHeader>
<StyledLink to={issueUrl} data-test-id="linked-issue">
<StyledShortId
shortId={group.shortId}
avatar={
<ProjectBadge
project={group.project}
avatarSize={16}
hideName
disableLink
/>
}
/>
</StyledLink>
<SeenByList seenBy={group.seenBy} maxVisibleAvatars={5} />
</IssueCardHeader>
<IssueCardBody>
<GroupChart statsPeriod="30d" data={group} height={56} />
</IssueCardBody>
<IssueCardFooter>
<Times lastSeen={group.lastSeen} firstSeen={group.firstSeen} />
</IssueCardFooter>
</StyledIssueCard>
</Section>
);
}
}
const Section = styled('div')`
margin-bottom: ${space(4)};
`;
const StyledIssueCard = styled('div')`
border: 1px solid ${p => p.theme.border};
border-radius: ${p => p.theme.borderRadius};
`;
const IssueCardHeader = styled('div')`
display: flex;
align-items: center;
justify-content: space-between;
padding: ${space(1)};
`;
const StyledLink = styled(Link)`
justify-content: flex-start;
`;
const IssueCardBody = styled('div')`
background: ${p => p.theme.backgroundSecondary};
padding-top: ${space(1)};
`;
const StyledShortId = styled(ShortId)`
font-size: ${p => p.theme.fontSizeMedium};
color: ${p => p.theme.textColor};
`;
const IssueCardFooter = styled('div')`
color: ${p => p.theme.gray300};
font-size: ${p => p.theme.fontSizeSmall};
padding: ${space(0.5)} ${space(1)};
`;
export default LinkedIssue;