123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224 |
- import {Fragment} from 'react';
- import {RouteComponentProps} from 'react-router';
- import styled from '@emotion/styled';
- import {LocationDescriptorObject} from 'history';
- import omit from 'lodash/omit';
- import pick from 'lodash/pick';
- import moment from 'moment';
- import {Client} from 'app/api';
- import {DateTimeObject} from 'app/components/charts/utils';
- import * as Layout from 'app/components/layouts/thirds';
- import LoadingIndicator from 'app/components/loadingIndicator';
- import {getParams} from 'app/components/organizations/globalSelectionHeader/getParams';
- import {ChangeData} from 'app/components/organizations/timeRangeSelector';
- import PageTimeRangeSelector from 'app/components/pageTimeRangeSelector';
- import {DEFAULT_RELATIVE_PERIODS, DEFAULT_STATS_PERIOD} from 'app/constants';
- import {t} from 'app/locale';
- import space from 'app/styles/space';
- import {DateString, Organization, RelativePeriod, TeamWithProjects} from 'app/types';
- import withApi from 'app/utils/withApi';
- import withOrganization from 'app/utils/withOrganization';
- import withTeamsForUser from 'app/utils/withTeamsForUser';
- import HeaderTabs from './headerTabs';
- import TeamKeyTransactions from './keyTransactions';
- import TeamDropdown from './teamDropdown';
- type Props = {
- api: Client;
- organization: Organization;
- teams: TeamWithProjects[];
- loadingTeams: boolean;
- error: Error | null;
- } & RouteComponentProps<{orgId: string}, {}>;
- const PAGE_QUERY_PARAMS = [
- 'pageStatsPeriod',
- 'pageStart',
- 'pageEnd',
- 'pageUtc',
- 'dataCategory',
- 'transform',
- 'sort',
- 'query',
- 'cursor',
- 'team',
- ];
- function TeamInsightsOverview({
- organization,
- teams,
- loadingTeams,
- location,
- router,
- }: Props) {
- const query = location?.query ?? {};
- const currentTeamId = query.team ?? teams[0]?.id;
- const currentTeam = teams.find(team => team.id === currentTeamId);
- const projects = currentTeam?.projects ?? [];
- function handleChangeTeam(teamId: string) {
- setStateOnUrl({team: teamId});
- }
- function handleUpdateDatetime(datetime: ChangeData): LocationDescriptorObject {
- const {start, end, relative, utc} = datetime;
- if (start && end) {
- const parser = utc ? moment.utc : moment;
- return setStateOnUrl({
- pageStatsPeriod: undefined,
- pageStart: parser(start).format(),
- pageEnd: parser(end).format(),
- pageUtc: utc ?? undefined,
- });
- }
- return setStateOnUrl({
- pageStatsPeriod: (relative as RelativePeriod) || undefined,
- pageStart: undefined,
- pageEnd: undefined,
- pageUtc: undefined,
- });
- }
- function setStateOnUrl(nextState: {
- pageStatsPeriod?: RelativePeriod;
- pageStart?: DateString;
- pageEnd?: DateString;
- pageUtc?: boolean | null;
- sort?: string;
- query?: string;
- cursor?: string;
- team?: string;
- }): LocationDescriptorObject {
- const nextQueryParams = pick(nextState, PAGE_QUERY_PARAMS);
- const nextLocation = {
- ...location,
- query: {
- ...query,
- ...nextQueryParams,
- },
- };
- router.push(nextLocation);
- return nextLocation;
- }
- function dataDatetime(): DateTimeObject {
- const {
- start,
- end,
- statsPeriod,
- utc: utcString,
- } = getParams(query, {
- allowEmptyPeriod: true,
- allowAbsoluteDatetime: true,
- allowAbsolutePageDatetime: true,
- });
- if (!statsPeriod && !start && !end) {
- return {period: DEFAULT_STATS_PERIOD};
- }
- // Following getParams, statsPeriod will take priority over start/end
- if (statsPeriod) {
- return {period: statsPeriod};
- }
- const utc = utcString === 'true';
- if (start && end) {
- return utc
- ? {
- start: moment.utc(start).format(),
- end: moment.utc(end).format(),
- utc,
- }
- : {
- start: moment(start).utc().format(),
- end: moment(end).utc().format(),
- utc,
- };
- }
- return {period: DEFAULT_STATS_PERIOD};
- }
- const {period, start, end, utc} = dataDatetime();
- return (
- <Fragment>
- <BorderlessHeader>
- <StyledHeaderContent>
- <StyledLayoutTitle>{t('Team Insights')}</StyledLayoutTitle>
- </StyledHeaderContent>
- </BorderlessHeader>
- <Layout.Header>
- <HeaderTabs organization={organization} activeTab="teamInsights" />
- </Layout.Header>
- <Layout.Body>
- {loadingTeams && <LoadingIndicator />}
- {!loadingTeams && (
- <Layout.Main fullWidth>
- <ControlsWrapper>
- <TeamDropdown
- teams={teams}
- selectedTeam={currentTeamId}
- handleChangeTeam={handleChangeTeam}
- />
- <PageTimeRangeSelector
- organization={organization}
- relative={period ?? ''}
- start={start ?? null}
- end={end ?? null}
- utc={utc ?? null}
- onUpdate={handleUpdateDatetime}
- relativeOptions={omit(DEFAULT_RELATIVE_PERIODS, ['1h'])}
- />
- </ControlsWrapper>
- <SectionTitle>{t('Performance')}</SectionTitle>
- <TeamKeyTransactions
- organization={organization}
- projects={projects}
- period={period}
- start={start?.toString()}
- end={end?.toString()}
- location={location}
- />
- </Layout.Main>
- )}
- </Layout.Body>
- </Fragment>
- );
- }
- export {TeamInsightsOverview};
- export default withApi(withOrganization(withTeamsForUser(TeamInsightsOverview)));
- const BorderlessHeader = styled(Layout.Header)`
- border-bottom: 0;
- `;
- const StyledHeaderContent = styled(Layout.HeaderContent)`
- margin-bottom: 0;
- `;
- const StyledLayoutTitle = styled(Layout.Title)`
- margin-top: ${space(0.5)};
- `;
- const ControlsWrapper = styled('div')`
- display: flex;
- align-items: center;
- gap: ${space(1)};
- margin-bottom: ${space(2)};
- `;
- const SectionTitle = styled(Layout.Title)`
- margin-bottom: ${space(1)} !important;
- `;
|