|
@@ -1,4 +1,4 @@
|
|
|
-import {Component, Fragment} from 'react';
|
|
|
+import {useState} from 'react';
|
|
|
import styled from '@emotion/styled';
|
|
|
import type {Location} from 'history';
|
|
|
|
|
@@ -8,12 +8,11 @@ import GridEditable, {
|
|
|
} from 'sentry/components/gridEditable';
|
|
|
import type {Alignments} from 'sentry/components/gridEditable/sortLink';
|
|
|
import Link from 'sentry/components/links/link';
|
|
|
-import {Organization, Project} from 'sentry/types';
|
|
|
+import type {Organization, Project} from 'sentry/types';
|
|
|
import DiscoverQuery, {
|
|
|
TableData,
|
|
|
TableDataRow,
|
|
|
} from 'sentry/utils/discover/discoverQuery';
|
|
|
-import EventView from 'sentry/utils/discover/eventView';
|
|
|
import {getFieldRenderer} from 'sentry/utils/discover/fieldRenderers';
|
|
|
import {fieldAlignment} from 'sentry/utils/discover/fields';
|
|
|
import type {MetricRule} from 'sentry/views/alerts/rules/metric/types';
|
|
@@ -24,29 +23,36 @@ import {getProjectID} from 'sentry/views/performance/utils';
|
|
|
|
|
|
import type {TimePeriodType} from './constants';
|
|
|
|
|
|
-type TableProps = {
|
|
|
- eventView: EventView;
|
|
|
+interface RelatedTransactionsProps {
|
|
|
+ filter: string;
|
|
|
location: Location;
|
|
|
organization: Organization;
|
|
|
projects: Project[];
|
|
|
- summaryConditions: string;
|
|
|
-};
|
|
|
-
|
|
|
-type TableState = {
|
|
|
- widths: number[];
|
|
|
-};
|
|
|
-class Table extends Component<TableProps, TableState> {
|
|
|
- state: TableState = {
|
|
|
- widths: [],
|
|
|
- };
|
|
|
+ rule: MetricRule;
|
|
|
+ timePeriod: TimePeriodType;
|
|
|
+}
|
|
|
|
|
|
- renderBodyCell(
|
|
|
+function RelatedTransactions({
|
|
|
+ organization,
|
|
|
+ projects,
|
|
|
+ timePeriod,
|
|
|
+ rule,
|
|
|
+ filter,
|
|
|
+ location,
|
|
|
+}: RelatedTransactionsProps) {
|
|
|
+ const [widths, setWidths] = useState<number[]>([]);
|
|
|
+ const eventView = getMetricRuleDiscoverQuery({
|
|
|
+ rule,
|
|
|
+ timePeriod,
|
|
|
+ projects,
|
|
|
+ });
|
|
|
+ const summaryConditions = `${rule.query} ${filter}`;
|
|
|
+
|
|
|
+ function renderBodyCell(
|
|
|
tableData: TableData | null,
|
|
|
column: TableColumn<keyof TableDataRow>,
|
|
|
dataRow: TableDataRow
|
|
|
): React.ReactNode {
|
|
|
- const {eventView, organization, projects, location, summaryConditions} = this.props;
|
|
|
-
|
|
|
if (!tableData || !tableData.meta) {
|
|
|
return dataRow[column.key];
|
|
|
}
|
|
@@ -58,7 +64,7 @@ class Table extends Component<TableProps, TableState> {
|
|
|
|
|
|
if (field === 'transaction') {
|
|
|
const projectID = getProjectID(dataRow, projects);
|
|
|
- const summaryView = eventView.clone();
|
|
|
+ const summaryView = eventView!.clone();
|
|
|
summaryView.query = summaryConditions;
|
|
|
|
|
|
const target = transactionSummaryRouteWithQuery({
|
|
@@ -74,14 +80,14 @@ class Table extends Component<TableProps, TableState> {
|
|
|
return rendered;
|
|
|
}
|
|
|
|
|
|
- renderBodyCellWithData = (tableData: TableData | null) => {
|
|
|
+ const renderBodyCellWithData = (tableData: TableData | null) => {
|
|
|
return (
|
|
|
column: TableColumn<keyof TableDataRow>,
|
|
|
dataRow: TableDataRow
|
|
|
- ): React.ReactNode => this.renderBodyCell(tableData, column, dataRow);
|
|
|
+ ): React.ReactNode => renderBodyCell(tableData, column, dataRow);
|
|
|
};
|
|
|
|
|
|
- renderHeadCell(
|
|
|
+ function renderHeadCell(
|
|
|
tableMeta: TableData['meta'],
|
|
|
column: TableColumn<keyof TableDataRow>,
|
|
|
title: React.ReactNode
|
|
@@ -92,107 +98,61 @@ class Table extends Component<TableProps, TableState> {
|
|
|
return <HeaderCell align={align}>{title || field.field}</HeaderCell>;
|
|
|
}
|
|
|
|
|
|
- renderHeadCellWithMeta = (tableMeta: TableData['meta'], columnName: string) => {
|
|
|
+ const renderHeadCellWithMeta = (tableMeta: TableData['meta'], columnName: string) => {
|
|
|
const columnTitles = ['transactions', 'project', columnName, 'users', 'user misery'];
|
|
|
|
|
|
return (column: TableColumn<keyof TableDataRow>, index: number): React.ReactNode =>
|
|
|
- this.renderHeadCell(tableMeta, column, columnTitles[index]);
|
|
|
+ renderHeadCell(tableMeta, column, columnTitles[index]);
|
|
|
};
|
|
|
|
|
|
- handleResizeColumn = (columnIndex: number, nextColumn: GridColumn) => {
|
|
|
- const widths: number[] = [...this.state.widths];
|
|
|
- widths[columnIndex] = nextColumn.width
|
|
|
+ const handleResizeColumn = (columnIndex: number, nextColumn: GridColumn) => {
|
|
|
+ const newWidths: number[] = [...widths];
|
|
|
+ newWidths[columnIndex] = nextColumn.width
|
|
|
? Number(nextColumn.width)
|
|
|
: COL_WIDTH_UNDEFINED;
|
|
|
- this.setState({widths});
|
|
|
+ setWidths(newWidths);
|
|
|
};
|
|
|
|
|
|
- getSortedEventView() {
|
|
|
- const {eventView} = this.props;
|
|
|
- return eventView.withSorts([...eventView.sorts]);
|
|
|
- }
|
|
|
-
|
|
|
- render() {
|
|
|
- const {eventView, organization, location} = this.props;
|
|
|
-
|
|
|
- const {widths} = this.state;
|
|
|
- const columnOrder = eventView
|
|
|
- .getColumns()
|
|
|
- .map((col: TableColumn<React.ReactText>, i: number) => {
|
|
|
- if (typeof widths[i] === 'number') {
|
|
|
- return {...col, width: widths[i]};
|
|
|
- }
|
|
|
- return col;
|
|
|
- });
|
|
|
-
|
|
|
- const sortedEventView = this.getSortedEventView();
|
|
|
- const columnSortBy = sortedEventView.getSorts();
|
|
|
-
|
|
|
- return (
|
|
|
- <Fragment>
|
|
|
- <DiscoverQuery
|
|
|
- eventView={sortedEventView}
|
|
|
- orgSlug={organization.slug}
|
|
|
- location={location}
|
|
|
- >
|
|
|
- {({isLoading, tableData}) => (
|
|
|
- <GridEditable
|
|
|
- isLoading={isLoading}
|
|
|
- data={tableData ? tableData.data.slice(0, 5) : []}
|
|
|
- columnOrder={columnOrder}
|
|
|
- columnSortBy={columnSortBy}
|
|
|
- grid={{
|
|
|
- onResizeColumn: this.handleResizeColumn,
|
|
|
- renderHeadCell: this.renderHeadCellWithMeta(
|
|
|
- tableData?.meta,
|
|
|
- columnOrder[2].name as string
|
|
|
- ) as any,
|
|
|
- renderBodyCell: this.renderBodyCellWithData(tableData) as any,
|
|
|
- }}
|
|
|
- location={location}
|
|
|
- />
|
|
|
- )}
|
|
|
- </DiscoverQuery>
|
|
|
- </Fragment>
|
|
|
- );
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-interface Props {
|
|
|
- filter: string;
|
|
|
- location: Location;
|
|
|
- organization: Organization;
|
|
|
- projects: Project[];
|
|
|
- rule: MetricRule;
|
|
|
- timePeriod: TimePeriodType;
|
|
|
-}
|
|
|
-
|
|
|
-function RelatedTransactions({
|
|
|
- rule,
|
|
|
- projects,
|
|
|
- filter,
|
|
|
- location,
|
|
|
- organization,
|
|
|
- timePeriod,
|
|
|
-}: Props) {
|
|
|
- const eventView = getMetricRuleDiscoverQuery({
|
|
|
- rule,
|
|
|
- timePeriod,
|
|
|
- projects,
|
|
|
- });
|
|
|
-
|
|
|
if (!eventView) {
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
+ const columnOrder = eventView
|
|
|
+ .getColumns()
|
|
|
+ .map((col: TableColumn<React.ReactText>, i: number) => {
|
|
|
+ if (typeof widths[i] === 'number') {
|
|
|
+ return {...col, width: widths[i]};
|
|
|
+ }
|
|
|
+ return col;
|
|
|
+ });
|
|
|
+
|
|
|
+ const sortedEventView = eventView.withSorts([...eventView.sorts]);
|
|
|
+ const columnSortBy = sortedEventView.getSorts();
|
|
|
+
|
|
|
return (
|
|
|
- <Table
|
|
|
- eventView={eventView}
|
|
|
- projects={projects}
|
|
|
- organization={organization}
|
|
|
+ <DiscoverQuery
|
|
|
+ eventView={sortedEventView}
|
|
|
+ orgSlug={organization.slug}
|
|
|
location={location}
|
|
|
- summaryConditions={`${rule.query} ${filter}`}
|
|
|
- />
|
|
|
+ >
|
|
|
+ {({isLoading, tableData}) => (
|
|
|
+ <GridEditable
|
|
|
+ isLoading={isLoading}
|
|
|
+ data={tableData ? tableData.data.slice(0, 5) : []}
|
|
|
+ columnOrder={columnOrder}
|
|
|
+ columnSortBy={columnSortBy}
|
|
|
+ grid={{
|
|
|
+ onResizeColumn: handleResizeColumn,
|
|
|
+ renderHeadCell: renderHeadCellWithMeta(
|
|
|
+ tableData?.meta,
|
|
|
+ columnOrder[2]!.name
|
|
|
+ ) as any,
|
|
|
+ renderBodyCell: renderBodyCellWithData(tableData) as any,
|
|
|
+ }}
|
|
|
+ location={location}
|
|
|
+ />
|
|
|
+ )}
|
|
|
+ </DiscoverQuery>
|
|
|
);
|
|
|
}
|
|
|
|