import {Component} from 'react'; import styled from '@emotion/styled'; import {updateProjects} from 'sentry/actionCreators/pageFilters'; import {Button, LinkButton} from 'sentry/components/button'; import ButtonBar from 'sentry/components/buttonBar'; import ErrorPanel from 'sentry/components/charts/errorPanel'; import EmptyMessage from 'sentry/components/emptyMessage'; import IdBadge from 'sentry/components/idBadge'; import ExternalLink from 'sentry/components/links/externalLink'; import Link from 'sentry/components/links/link'; import Panel from 'sentry/components/panels/panel'; import {PanelTable} from 'sentry/components/panels/panelTable'; import {IconGraph, IconSettings, IconWarning} from 'sentry/icons'; import {t, tct} from 'sentry/locale'; import {space} from 'sentry/styles/space'; import type {DataCategoryInfo} from 'sentry/types/core'; import type {WithRouterProps} from 'sentry/types/legacyReactRouter'; import type {Project} from 'sentry/types/project'; import withSentryRouter from 'sentry/utils/withSentryRouter'; import {formatUsageWithUnits, getFormatUsageOptions} from './utils'; const DOCS_URL = 'https://docs.sentry.io/product/accounts/membership/#restricting-access'; type Props = { dataCategory: DataCategoryInfo; headers: React.ReactNode[]; usageStats: TableStat[]; errors?: Record; isEmpty?: boolean; isError?: boolean; isLoading?: boolean; showStoredOutcome?: boolean; } & WithRouterProps<{}, {}>; export type TableStat = { accepted: number; accepted_stored: number; filtered: number; invalid: number; project: Project; projectLink: string; projectSettingsLink: string; rate_limited: number; total: number; }; class UsageTable extends Component { getErrorMessage = errorMessage => { if (errorMessage.projectStats.responseJSON.detail === 'No projects available') { return ( } title={t( "You don't have access to any projects, or your organization has no projects." )} description={tct('Learn more about [link:Project Access]', { link: , })} /> ); } return ; }; loadProject(projectId: number) { updateProjects([projectId], this.props.router, { save: true, environments: [], // Clear environments when switching projects }); window.scrollTo({top: 0, left: 0, behavior: 'smooth'}); } renderTableRow(stat: TableStat & {project: Project}) { const {dataCategory, showStoredOutcome} = this.props; const {project, total, accepted, accepted_stored, filtered, invalid, rate_limited} = stat; return [ , {formatUsageWithUnits( total, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , {formatUsageWithUnits( accepted, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} {showStoredOutcome && ( {`(${formatUsageWithUnits( accepted_stored, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )})`} )} , {formatUsageWithUnits( filtered, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , {formatUsageWithUnits( rate_limited, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , {formatUsageWithUnits( invalid, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , } size="xs" aria-label={t('Project Settings')} title={t('Go to project settings')} to={stat.projectSettingsLink} /> , ]; } render() { const {isEmpty, isLoading, isError, errors, headers, usageStats} = this.props; if (isError) { return ( {this.getErrorMessage(errors)} ); } return ( {usageStats.map(s => this.renderTableRow(s))} ); } } export default withSentryRouter(UsageTable); const StyledPanelTable = styled(PanelTable)` grid-template-columns: repeat(7, auto); @media (min-width: ${p => p.theme.breakpoints.small}) { grid-template-columns: 1fr repeat(6, minmax(0, auto)); } `; export const CellStat = styled('div')` display: flex; align-items: center; font-variant-numeric: tabular-nums; justify-content: right; `; export const CellProject = styled(CellStat)` justify-content: left; `; const StyledIdBadge = styled(IdBadge)` overflow: hidden; white-space: nowrap; flex-shrink: 1; `; const SubText = styled('span')` color: ${p => p.theme.subText}; margin-left: ${space(0.5)}; `;