123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338 |
- import {Component, Fragment} from 'react';
- import {browserHistory, withRouter, WithRouterProps} from 'react-router';
- import styled from '@emotion/styled';
- import {Location} from 'history';
- import DropdownControl, {DropdownItem} from 'app/components/dropdownControl';
- import SearchBar from 'app/components/events/searchBar';
- import LoadingIndicator from 'app/components/loadingIndicator';
- import * as TeamKeyTransactionManager from 'app/components/performance/teamKeyTransactionsManager';
- import {MAX_QUERY_LENGTH} from 'app/constants';
- import {t} from 'app/locale';
- import space from 'app/styles/space';
- import {Organization, Project} from 'app/types';
- import {trackAnalyticsEvent} from 'app/utils/analytics';
- import EventView from 'app/utils/discover/eventView';
- import {generateAggregateFields} from 'app/utils/discover/fields';
- import {decodeScalar} from 'app/utils/queryString';
- import Teams from 'app/utils/teams';
- import {MutableSearch} from 'app/utils/tokenizeSearch';
- import Charts from '../charts/index';
- import {
- getBackendAxisOptions,
- getFrontendAxisOptions,
- getFrontendOtherAxisOptions,
- getMobileAxisOptions,
- } from '../data';
- import Table from '../table';
- import {getTransactionSearchQuery} from '../utils';
- import DoubleAxisDisplay from './display/doubleAxisDisplay';
- import {
- BACKEND_COLUMN_TITLES,
- FRONTEND_OTHER_COLUMN_TITLES,
- FRONTEND_PAGELOAD_COLUMN_TITLES,
- MOBILE_COLUMN_TITLES,
- REACT_NATIVE_COLUMN_TITLES,
- } from './data';
- import {
- getCurrentLandingDisplay,
- getDefaultDisplayFieldForPlatform,
- getDisplayAxes,
- LANDING_DISPLAYS,
- LandingDisplayField,
- LEFT_AXIS_QUERY_KEY,
- RIGHT_AXIS_QUERY_KEY,
- } from './utils';
- import {BackendCards, FrontendCards, MobileCards} from './vitalsCards';
- type Props = {
- organization: Organization;
- eventView: EventView;
- location: Location;
- projects: Project[];
- setError: (msg: string | undefined) => void;
- handleSearch: (searchQuery: string) => void;
- } & WithRouterProps;
- type State = {};
- class LandingContent extends Component<Props, State> {
- getSummaryConditions(query: string) {
- const parsed = new MutableSearch(query);
- parsed.freeText = [];
- return parsed.formatString();
- }
- handleLandingDisplayChange = (field: string) => {
- const {location, organization, eventView, projects} = this.props;
- const newQuery = {...location.query};
- delete newQuery[LEFT_AXIS_QUERY_KEY];
- delete newQuery[RIGHT_AXIS_QUERY_KEY];
- const defaultDisplay = getDefaultDisplayFieldForPlatform(projects, eventView);
- const currentDisplay = decodeScalar(location.query.landingDisplay);
- // Transaction op can affect the display and show no results if it is explicitly set.
- const query = decodeScalar(location.query.query, '');
- const searchConditions = new MutableSearch(query);
- searchConditions.removeFilter('transaction.op');
- trackAnalyticsEvent({
- eventKey: 'performance_views.landingv2.display_change',
- eventName: 'Performance Views: Landing v2 Display Change',
- organization_id: parseInt(organization.id, 10),
- change_to_display: field,
- default_display: defaultDisplay,
- current_display: currentDisplay,
- is_default: defaultDisplay === currentDisplay,
- });
- browserHistory.push({
- pathname: location.pathname,
- query: {
- ...newQuery,
- query: searchConditions.formatString(),
- landingDisplay: field,
- },
- });
- };
- renderSelectedDisplay(display) {
- switch (display) {
- case LandingDisplayField.ALL:
- return this.renderLandingAll();
- case LandingDisplayField.FRONTEND_PAGELOAD:
- return this.renderLandingFrontend(true);
- case LandingDisplayField.FRONTEND_OTHER:
- return this.renderLandingFrontend(false);
- case LandingDisplayField.BACKEND:
- return this.renderLandingBackend();
- case LandingDisplayField.MOBILE:
- return this.renderLandingMobile();
- default:
- throw new Error(`Unknown display: ${display}`);
- }
- }
- renderLandingFrontend = isPageload => {
- const {organization, location, projects, eventView, setError} = this.props;
- const columnTitles = isPageload
- ? FRONTEND_PAGELOAD_COLUMN_TITLES
- : FRONTEND_OTHER_COLUMN_TITLES;
- const axisOptions = isPageload
- ? getFrontendAxisOptions(organization)
- : getFrontendOtherAxisOptions(organization);
- const {leftAxis, rightAxis} = getDisplayAxes(axisOptions, location);
- return (
- <Fragment>
- {isPageload && (
- <FrontendCards
- eventView={eventView}
- organization={organization}
- location={location}
- projects={projects}
- />
- )}
- <DoubleAxisDisplay
- eventView={eventView}
- organization={organization}
- location={location}
- axisOptions={axisOptions}
- leftAxis={leftAxis}
- rightAxis={rightAxis}
- />
- <Table
- eventView={eventView}
- projects={projects}
- organization={organization}
- location={location}
- setError={setError}
- summaryConditions={eventView.getQueryWithAdditionalConditions()}
- columnTitles={columnTitles}
- />
- </Fragment>
- );
- };
- renderLandingBackend = () => {
- const {organization, location, projects, eventView, setError} = this.props;
- const axisOptions = getBackendAxisOptions(organization);
- const {leftAxis, rightAxis} = getDisplayAxes(axisOptions, location);
- const columnTitles = BACKEND_COLUMN_TITLES;
- return (
- <Fragment>
- <BackendCards
- eventView={eventView}
- organization={organization}
- location={location}
- />
- <DoubleAxisDisplay
- eventView={eventView}
- organization={organization}
- location={location}
- axisOptions={axisOptions}
- leftAxis={leftAxis}
- rightAxis={rightAxis}
- />
- <Table
- eventView={eventView}
- projects={projects}
- organization={organization}
- location={location}
- setError={setError}
- summaryConditions={eventView.getQueryWithAdditionalConditions()}
- columnTitles={columnTitles}
- />
- </Fragment>
- );
- };
- renderLandingMobile = () => {
- const {organization, location, projects, eventView, setError} = this.props;
- const axisOptions = getMobileAxisOptions(organization);
- const {leftAxis, rightAxis} = getDisplayAxes(axisOptions, location);
- // only react native should contain the stall percentage column
- const isReactNative = Boolean(
- eventView.getFields().find(field => field.includes('measurements.stall_percentage'))
- );
- const columnTitles = isReactNative
- ? REACT_NATIVE_COLUMN_TITLES
- : MOBILE_COLUMN_TITLES;
- return (
- <Fragment>
- <MobileCards
- eventView={eventView}
- organization={organization}
- location={location}
- showStallPercentage={isReactNative}
- />
- <DoubleAxisDisplay
- eventView={eventView}
- organization={organization}
- location={location}
- axisOptions={axisOptions}
- leftAxis={leftAxis}
- rightAxis={rightAxis}
- />
- <Table
- eventView={eventView}
- projects={projects}
- organization={organization}
- location={location}
- setError={setError}
- summaryConditions={eventView.getQueryWithAdditionalConditions()}
- columnTitles={columnTitles}
- />
- </Fragment>
- );
- };
- renderLandingAll = () => {
- const {organization, location, router, projects, eventView, setError} = this.props;
- return (
- <Fragment>
- <Charts
- eventView={eventView}
- organization={organization}
- location={location}
- router={router}
- />
- <Table
- eventView={eventView}
- projects={projects}
- organization={organization}
- location={location}
- setError={setError}
- summaryConditions={eventView.getQueryWithAdditionalConditions()}
- />
- </Fragment>
- );
- };
- render() {
- const {organization, location, eventView, projects, handleSearch} = this.props;
- const currentLandingDisplay = getCurrentLandingDisplay(location, projects, eventView);
- const filterString = getTransactionSearchQuery(location, eventView.query);
- return (
- <Fragment>
- <SearchContainer>
- <SearchBar
- searchSource="performance_landing"
- organization={organization}
- projectIds={eventView.project}
- query={filterString}
- fields={generateAggregateFields(
- organization,
- [...eventView.fields, {field: 'tps()'}],
- ['epm()', 'eps()']
- )}
- onSearch={handleSearch}
- maxQueryLength={MAX_QUERY_LENGTH}
- />
- <DropdownControl
- buttonProps={{prefix: t('Display')}}
- label={currentLandingDisplay.label}
- >
- {LANDING_DISPLAYS.filter(
- ({isShown}) => !isShown || isShown(organization)
- ).map(({label, field}) => (
- <DropdownItem
- key={field}
- onSelect={this.handleLandingDisplayChange}
- eventKey={field}
- data-test-id={field}
- isActive={field === currentLandingDisplay.field}
- >
- {label}
- </DropdownItem>
- ))}
- </DropdownControl>
- </SearchContainer>
- <Teams provideUserTeams>
- {({teams, initiallyLoaded}) =>
- initiallyLoaded ? (
- <TeamKeyTransactionManager.Provider
- organization={organization}
- teams={teams}
- selectedTeams={['myteams']}
- selectedProjects={eventView.project.map(String)}
- >
- {this.renderSelectedDisplay(currentLandingDisplay.field)}
- </TeamKeyTransactionManager.Provider>
- ) : (
- <LoadingIndicator />
- )
- }
- </Teams>
- </Fragment>
- );
- }
- }
- const SearchContainer = styled('div')`
- display: grid;
- grid-gap: ${space(2)};
- margin-bottom: ${space(2)};
- @media (min-width: ${p => p.theme.breakpoints[0]}) {
- grid-template-columns: 1fr min-content;
- }
- `;
- export default withRouter(LandingContent);
|