Skip to content
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

feat(metric-issue): Display open periods table instead of all events #83852

Merged
merged 9 commits into from
Jan 24, 2025
12 changes: 10 additions & 2 deletions static/app/components/duration.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,21 @@ interface Props extends React.HTMLAttributes<HTMLSpanElement> {
abbreviation?: boolean;
exact?: boolean;
fixedDigits?: number;
precision?: 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'years';
}

function Duration({seconds, fixedDigits, abbreviation, exact, ...props}: Props) {
function Duration({
seconds,
fixedDigits,
abbreviation,
exact,
precision,
...props
}: Props) {
return (
<span {...props}>
{exact
? getExactDuration(seconds, abbreviation)
? getExactDuration(seconds, abbreviation, precision)
: getDuration(seconds, fixedDigits, abbreviation)}
</span>
);
Expand Down
6 changes: 6 additions & 0 deletions static/app/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -1956,6 +1956,12 @@ function buildRoutes() {
path={TabPaths[Tab.EVENTS]}
component={hoc(make(() => import('sentry/views/issueDetails/groupEvents')))}
/>
<Route
path={TabPaths[Tab.OPEN_PERIODS]}
component={hoc(
make(() => import('sentry/views/issueDetails/groupOpenPeriods'))
)}
/>
<Route
path={TabPaths[Tab.TAGS]}
component={hoc(
Expand Down
1 change: 1 addition & 0 deletions static/app/utils/issueTypeConfig/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ const BASE_CONFIG: IssueTypeConfig = {
regression: {enabled: false},
replays: {enabled: false},
showFeedbackWidget: false,
showOpenPeriods: false,
similarIssues: {enabled: false},
spanEvidence: {enabled: false},
stacktrace: {enabled: true},
Expand Down
1 change: 1 addition & 0 deletions static/app/utils/issueTypeConfig/metricIssueConfig.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const metricIssueConfig: IssueCategoryConfigMapping = {
mergedIssues: {enabled: false},
replays: {enabled: false},
similarIssues: {enabled: false},
showOpenPeriods: true,
userFeedback: {enabled: false},
usesIssuePlatform: true,
stats: {enabled: false},
Expand Down
4 changes: 4 additions & 0 deletions static/app/utils/issueTypeConfig/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,10 @@ export type IssueTypeConfig = {
* Should the page show the feedback widget
*/
showFeedbackWidget: boolean;
/**
* showOpenPeriods
*/
showOpenPeriods: boolean;
/**
* Is the Similar Issues tab shown for this issue
*/
Expand Down
104 changes: 104 additions & 0 deletions static/app/views/issueDetails/groupOpenPeriods.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import {useState} from 'react';
import styled from '@emotion/styled';

import {DateTime} from 'sentry/components/dateTime';
import Duration from 'sentry/components/duration';
import GridEditable, {
COL_WIDTH_UNDEFINED,
type GridColumnOrder,
} from 'sentry/components/gridEditable';
import LoadingError from 'sentry/components/loadingError';
import LoadingIndicator from 'sentry/components/loadingIndicator';
import {t} from 'sentry/locale';
import {useParams} from 'sentry/utils/useParams';
import {useGroup} from 'sentry/views/issueDetails/useGroup';

type OpenPeriodDisplayData = {
duration: React.ReactNode;
end: React.ReactNode;
start: React.ReactNode;
title: React.ReactNode;
};

// TODO(snigdha): make this work for the old UI
// TODO(snigdha): support pagination
function IssueOpenPeriodsList() {
const [now] = useState(() => new Date());
const params = useParams<{groupId: string}>();
const {
data: group,
isPending: isGroupPending,
isError: isGroupError,
refetch: refetchGroup,
} = useGroup({groupId: params.groupId});

if (isGroupError) {
return <LoadingError onRetry={refetchGroup} />;
}

if (isGroupPending) {
return <LoadingIndicator />;
}

// update the open periods to have date objects
const openPeriods = group?.openPeriods?.map(period => ({
...period,
start: new Date(period.start),
end: period.end ? new Date(period.end) : null,
}));
snigdhas marked this conversation as resolved.
Show resolved Hide resolved

const getDuration = (start: Date, end?: Date) => {
const duration = end
? (end.getTime() - start.getTime()) / 1000
: (now.getTime() - start.getTime()) / 1000;

return <Duration seconds={duration} precision="minutes" exact />;
};

if (!openPeriods) {
return <LoadingError onRetry={refetchGroup} />;
}

const data: OpenPeriodDisplayData[] = openPeriods.map(period => ({
title: <DateTime date={period.start} />,
start: <DateTime date={period.start} />,
end: period.end ? <DateTime date={period.end} /> : '—',
duration: getDuration(period.start, period.end ?? undefined),
}));

const renderHeadCell = (col: GridColumnOrder) => {
return <AlignLeft>{col.name}</AlignLeft>;
};

const renderBodyCell = (
col: GridColumnOrder<string>,
dataRow: OpenPeriodDisplayData
) => {
const column = col.key as keyof OpenPeriodDisplayData;
return <AlignLeft>{dataRow[column]}</AlignLeft>;
};

return (
<GridEditable
isLoading={isGroupPending}
data={data}
columnOrder={[
{key: 'title', width: COL_WIDTH_UNDEFINED, name: t('Title')},
{key: 'start', width: COL_WIDTH_UNDEFINED, name: t('Start')},
{key: 'end', width: COL_WIDTH_UNDEFINED, name: t('End')},
{key: 'duration', width: COL_WIDTH_UNDEFINED, name: t('Duration')},
]}
columnSortBy={[]}
grid={{
renderHeadCell,
renderBodyCell,
}}
/>
);
}

const AlignLeft = styled('span')`
text-align: left;
width: 100%;
`;
export default IssueOpenPeriodsList;
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps)
const hasManyAttachments =
attachmentPagination.next?.results || attachmentPagination.previous?.results;

const allEventsPath = issueTypeConfig.showOpenPeriods
? `${baseUrl}${TabPaths[Tab.OPEN_PERIODS]}`
: `${baseUrl}${TabPaths[Tab.EVENTS]}`;

return (
<EventNavigationWrapper role="navigation">
<LargeDropdownButtonWrapper>
Expand Down Expand Up @@ -184,7 +188,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps)
<IssueDetailsEventNavigation event={event} group={group} />
<LinkButton
to={{
pathname: `${baseUrl}${TabPaths[Tab.EVENTS]}`,
pathname: allEventsPath,
query: location.query,
}}
size="xs"
Expand All @@ -195,8 +199,7 @@ export function IssueEventNavigation({event, group}: IssueEventNavigationProps)
</LinkButton>
</Fragment>
)}

{currentTab === Tab.EVENTS && (
{(currentTab === Tab.EVENTS || currentTab === Tab.OPEN_PERIODS) && (
<ButtonBar gap={1}>
<LinkButton
to={discoverUrl}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ describe('SolutionsSection', () => {
regression: {enabled: false},
replays: {enabled: false},
showFeedbackWidget: false,
showOpenPeriods: false,
similarIssues: {enabled: false},
spanEvidence: {enabled: false},
stacktrace: {enabled: false},
Expand Down
2 changes: 2 additions & 0 deletions static/app/views/issueDetails/types.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export enum Tab {
MERGED = 'merged',
SIMILAR_ISSUES = 'similar-issues',
REPLAYS = 'Replays',
OPEN_PERIODS = 'open-periods',
}

export const TabPaths: Record<Tab, string> = {
Expand All @@ -20,4 +21,5 @@ export const TabPaths: Record<Tab, string> = {
[Tab.MERGED]: 'merged/',
[Tab.SIMILAR_ISSUES]: 'similar/',
[Tab.REPLAYS]: 'replays/',
[Tab.OPEN_PERIODS]: 'open-periods/',
};
Loading