import {Component} from 'react'; import type {WithRouterProps} from 'react-router'; import styled from '@emotion/styled'; import {updateProjects} from 'sentry/actionCreators/pageFilters'; import {Button} from 'sentry/components/button'; 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, {PanelTableHeader} 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, Project} from 'sentry/types'; 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; } & WithRouterProps<{}, {}>; export type TableStat = { accepted: number; dropped: number; filtered: number; project: Project; projectLink: string; projectSettingsLink: string; 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} = this.props; const {project, total, accepted, filtered, dropped} = stat; return [ , {formatUsageWithUnits( total, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , {formatUsageWithUnits( accepted, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , {formatUsageWithUnits( filtered, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , {formatUsageWithUnits( dropped, dataCategory.plural, getFormatUsageOptions(dataCategory.plural) )} , , ]; } 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(6, auto); @media (min-width: ${p => p.theme.breakpoints.small}) { grid-template-columns: 1fr repeat(5, minmax(0, auto)); } ${PanelTableHeader} { height: 45px; } `; export const CellStat = styled('div')` display: flex; align-items: center; font-variant-numeric: tabular-nums; justify-content: right; padding-top: 9px; padding-bottom: 9px; `; export const CellProject = styled(CellStat)` justify-content: left; `; const StyledIdBadge = styled(IdBadge)` overflow: hidden; white-space: nowrap; flex-shrink: 1; `; const SettingsIcon = styled(IconSettings)` color: ${p => p.theme.gray300}; align-items: center; display: inline-flex; justify-content: space-between; margin-right: ${space(1.0)}; margin-left: ${space(1.0)}; transition: 0.5s opacity ease-out; &:hover { color: ${p => p.theme.textColor}; } `; const StyledIconGraph = styled(IconGraph)` color: ${p => p.theme.gray300}; margin-right: 5px; &:hover { color: ${p => p.theme.textColor}; cursor: pointer; } `; const StyledSettingsButton = styled(Button)` padding: 0px; margin-left: 7px; `;