Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,214 +1,95 @@
import * as React from 'react';
import {Fragment} from 'react';
import {browserHistory} from 'react-router';
import styled from '@emotion/styled';
import {Location} from 'history';
import omit from 'lodash/omit';

import Alert from 'app/components/alert';
import Button from 'app/components/button';
import {CreateAlertFromViewButton} from 'app/components/createAlertButton';
import DropdownControl, {DropdownItem} from 'app/components/dropdownControl';
import SearchBar from 'app/components/events/searchBar';
import GlobalSdkUpdateAlert from 'app/components/globalSdkUpdateAlert';
import * as Layout from 'app/components/layouts/thirds';
import LoadingIndicator from 'app/components/loadingIndicator';
import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
import {IconFlag} from 'app/icons';
import {t} from 'app/locale';
import space from 'app/styles/space';
import {Organization, Project} from 'app/types';
import {Organization} from 'app/types';
import EventView from 'app/utils/discover/eventView';
import {WebVital} from 'app/utils/discover/fields';
import {decodeScalar} from 'app/utils/queryString';
import {MutableSearch} from 'app/utils/tokenizeSearch';
import {Actions, updateQuery} from 'app/views/eventsV2/table/cellAction';
import {TableColumn} from 'app/views/eventsV2/table/types';

import Filter, {filterToSearchConditions, SpanOperationBreakdownFilter} from '../filter';
import TransactionHeader from '../header';
import Tab from '../tabs';
import {SetStateAction} from '../types';

import EventsTable from './eventsTable';
import {EventsDisplayFilterName, getEventsFilterOptions} from './utils';

type Props = {
location: Location;
organization: Organization;
eventView: EventView;
transactionName: string;
organization: Organization;
projects: Project[];
spanOperationBreakdownFilter: SpanOperationBreakdownFilter;
onChangeSpanOperationBreakdownFilter: (newFilter: SpanOperationBreakdownFilter) => void;
eventsDisplayFilterName: EventsDisplayFilterName;
onChangeEventsDisplayFilter: (eventsDisplayFilterName: EventsDisplayFilterName) => void;
percentileValues?: Record<EventsDisplayFilterName, number>;
isLoading: boolean;
webVital?: WebVital;
setError: SetStateAction<string | undefined>;
};

type State = {
incompatibleAlertNotice: React.ReactNode;
error: string | undefined;
};

class EventsPageContent extends React.Component<Props, State> {
state: State = {
incompatibleAlertNotice: null,
error: undefined,
};

handleCellAction = (column: TableColumn<React.ReactText>) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method was unused

return (action: Actions, value: React.ReactText) => {
const {eventView, location} = this.props;

const searchConditions = new MutableSearch(eventView.query);

// remove any event.type queries since it is implied to apply to only transactions
searchConditions.removeFilter('event.type');

// no need to include transaction as its already in the query params
searchConditions.removeFilter('transaction');

updateQuery(searchConditions, action, column, value);

browserHistory.push({
pathname: location.pathname,
query: {
...location.query,
cursor: undefined,
query: searchConditions.formatString(),
},
});
};
};

handleIncompatibleQuery: React.ComponentProps<
typeof CreateAlertFromViewButton
>['onIncompatibleQuery'] = (incompatibleAlertNoticeFn, _errors) => {
const incompatibleAlertNotice = incompatibleAlertNoticeFn(() =>
this.setState({incompatibleAlertNotice: null})
);
this.setState({incompatibleAlertNotice});
};
function EventsContent(props: Props) {
const {
location,
organization,
eventView: originalEventView,
transactionName,
spanOperationBreakdownFilter,
webVital,
setError,
} = props;

renderError() {
const {error} = this.state;
const eventView = originalEventView.clone();

if (!error) {
return null;
}
const transactionsListTitles = [
t('event id'),
t('user'),
t('operation duration'),
t('total duration'),
t('trace id'),
t('timestamp'),
];

return (
<StyledAlert type="error" icon={<IconFlag size="md" />}>
{error}
</StyledAlert>
);
if (webVital) {
transactionsListTitles.splice(3, 0, t(webVital));
}

setError = (error: string | undefined) => {
this.setState({error});
};
const spanOperationBreakdownConditions = filterToSearchConditions(
spanOperationBreakdownFilter,
location
);

render() {
const {eventView, location, organization, projects, transactionName, isLoading} =
this.props;
const {incompatibleAlertNotice} = this.state;
if (spanOperationBreakdownConditions) {
eventView.query = `${eventView.query} ${spanOperationBreakdownConditions}`.trim();
transactionsListTitles.splice(2, 1, t(`${spanOperationBreakdownFilter} duration`));
}

return (
<Fragment>
<TransactionHeader
return (
<Layout.Main fullWidth>
<Search {...props} />
<StyledTable>
<EventsTable
eventView={eventView}
location={location}
organization={organization}
projects={projects}
location={location}
setError={setError}
columnTitles={transactionsListTitles}
transactionName={transactionName}
currentTab={Tab.Events}
hasWebVitals="maybe"
handleIncompatibleQuery={this.handleIncompatibleQuery}
/>
<Layout.Body>
<StyledSdkUpdatesAlert />
{this.renderError()}
{incompatibleAlertNotice && (
<Layout.Main fullWidth>{incompatibleAlertNotice}</Layout.Main>
)}
<Layout.Main fullWidth>
{isLoading ? (
<LoadingIndicator />
) : (
<Body {...this.props} setError={this.setError} />
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

After lifting the header, alerts, and loading states to the parent component, we don't need this component anymore and can just directly use the Body

)}
</Layout.Main>
</Layout.Body>
</Fragment>
);
}
}

class Body extends React.Component<
Props & {setError: (error: string | undefined) => void},
State
> {
render() {
let {eventView} = this.props;
const {
location,
organization,
transactionName,
spanOperationBreakdownFilter,
eventsDisplayFilterName,
onChangeEventsDisplayFilter,
setError,
webVital,
} = this.props;
const transactionsListTitles = [
t('event id'),
t('user'),
t('operation duration'),
t('total duration'),
t('trace id'),
t('timestamp'),
];

if (webVital) {
transactionsListTitles.splice(3, 0, t(webVital));
}

const spanOperationBreakdownConditions = filterToSearchConditions(
spanOperationBreakdownFilter,
location
);

if (spanOperationBreakdownConditions) {
eventView = eventView.clone();
eventView.query = `${eventView.query} ${spanOperationBreakdownConditions}`.trim();
transactionsListTitles.splice(2, 1, t(`${spanOperationBreakdownFilter} duration`));
}

return (
<React.Fragment>
<Search
{...this.props}
onChangeEventsDisplayFilter={onChangeEventsDisplayFilter}
eventsDisplayFilterName={eventsDisplayFilterName}
/>
<StyledTable>
<EventsTable
eventView={eventView}
organization={organization}
location={location}
setError={setError}
columnTitles={transactionsListTitles}
transactionName={transactionName}
/>
</StyledTable>
</React.Fragment>
);
}
</StyledTable>
</Layout.Main>
);
}

const Search = (props: Props) => {
function Search(props: Props) {
const {
eventView,
location,
Expand Down Expand Up @@ -283,19 +164,14 @@ const Search = (props: Props) => {
</SearchRowMenuItem>
</SearchWrapper>
);
};
}

const SearchWrapper = styled('div')`
display: flex;
width: 100%;
margin-bottom: ${space(3)};
`;

const StyledAlert = styled(Alert)`
grid-column: 1/3;
margin: 0;
`;

const StyledSearchBar = styled(SearchBar)`
flex-grow: 1;
`;
Expand All @@ -319,4 +195,4 @@ StyledSdkUpdatesAlert.defaultProps = {
Wrapper: p => <Layout.Main fullWidth {...p} />,
};

export default EventsPageContent;
export default EventsContent;
Loading