projectMetricsDetails.tsx 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. import {Fragment} from 'react';
  2. import {RouteComponentProps} from 'react-router';
  3. import styled from '@emotion/styled';
  4. import {LinkButton} from 'sentry/components/button';
  5. import MiniBarChart from 'sentry/components/charts/miniBarChart';
  6. import EmptyMessage from 'sentry/components/emptyMessage';
  7. import FieldGroup from 'sentry/components/forms/fieldGroup';
  8. import Panel from 'sentry/components/panels/panel';
  9. import PanelBody from 'sentry/components/panels/panelBody';
  10. import PanelHeader from 'sentry/components/panels/panelHeader';
  11. import PanelTable from 'sentry/components/panels/panelTable';
  12. import Placeholder from 'sentry/components/placeholder';
  13. import SentryDocumentTitle from 'sentry/components/sentryDocumentTitle';
  14. import {CHART_PALETTE} from 'sentry/constants/chartPalette';
  15. import {t} from 'sentry/locale';
  16. import {MetricsOperation, MetricType, MRI, Organization, Project} from 'sentry/types';
  17. import {getDdmUrl, getReadableMetricType, MetricDisplayType} from 'sentry/utils/metrics';
  18. import {formatMRI, formatMRIField, MRIToField, parseMRI} from 'sentry/utils/metrics/mri';
  19. import {useMetricsData} from 'sentry/utils/metrics/useMetricsData';
  20. import {useMetricsTags} from 'sentry/utils/metrics/useMetricsTags';
  21. import routeTitleGen from 'sentry/utils/routeTitle';
  22. import {CodeLocations} from 'sentry/views/ddm/codeLocations';
  23. import SettingsPageHeader from 'sentry/views/settings/components/settingsPageHeader';
  24. function getSettingsOperationForType(type: MetricType): MetricsOperation {
  25. switch (type) {
  26. case 'c':
  27. return 'sum';
  28. case 's':
  29. return 'count_unique';
  30. case 'd':
  31. return 'count';
  32. case 'g':
  33. return 'count';
  34. default:
  35. return 'sum';
  36. }
  37. }
  38. type Props = {
  39. organization: Organization;
  40. project: Project;
  41. } & RouteComponentProps<{mri: MRI; projectId: string}, {}>;
  42. function ProjectMetricsDetails({project, params, organization}: Props) {
  43. const {mri} = params;
  44. const {type, name, unit} = parseMRI(mri) ?? {};
  45. const operation = getSettingsOperationForType(type ?? 'c');
  46. const field = MRIToField(mri, operation);
  47. const {data: tagsData = []} = useMetricsTags(mri, [parseInt(project.id, 10)]);
  48. const {data: metricsData, isLoading} = useMetricsData(
  49. {
  50. datetime: {
  51. period: '30d',
  52. start: '',
  53. end: '',
  54. utc: false,
  55. },
  56. environments: [],
  57. mri,
  58. projects: [parseInt(project.id, 10)],
  59. op: operation,
  60. },
  61. {interval: '1d'}
  62. );
  63. const series = [
  64. {
  65. seriesName: formatMRIField(field) ?? 'Metric',
  66. data:
  67. metricsData?.intervals.map((interval, index) => ({
  68. name: interval,
  69. value: metricsData.groups[0].series[field][index] ?? 0,
  70. })) ?? [],
  71. },
  72. ];
  73. const isChartEmpty = series[0].data.every(({value}) => value === 0);
  74. const tags = tagsData.sort((a, b) => a.key.localeCompare(b.key));
  75. return (
  76. <Fragment>
  77. <SentryDocumentTitle title={routeTitleGen(formatMRI(mri), project.slug, false)} />
  78. <SettingsPageHeader
  79. title={t('Metric Details')}
  80. action={
  81. <LinkButton
  82. to={getDdmUrl(organization.slug, {
  83. statsPeriod: '30d',
  84. project: [project.id],
  85. widgets: [
  86. {
  87. mri,
  88. displayType: MetricDisplayType.BAR,
  89. op: operation,
  90. query: '',
  91. groupBy: undefined,
  92. },
  93. ],
  94. })}
  95. size="sm"
  96. >
  97. {t('Open in Metrics')}
  98. </LinkButton>
  99. }
  100. />
  101. <Panel>
  102. <PanelHeader>{t('Metric Details')}</PanelHeader>
  103. <PanelBody>
  104. <FieldGroup
  105. label={t('Name')}
  106. help={t('Name of the metric (invoked in your code).')}
  107. >
  108. <MetricName>
  109. <strong>{name}</strong>
  110. </MetricName>
  111. </FieldGroup>
  112. <FieldGroup
  113. label={t('Type')}
  114. help={t('Either counter, distribution, gauge, or set.')}
  115. >
  116. <div>{getReadableMetricType(type)}</div>
  117. </FieldGroup>
  118. <FieldGroup
  119. label={t('Unit')}
  120. help={t('Unit specified in the code - affects formatting.')}
  121. >
  122. <div>{unit}</div>
  123. </FieldGroup>
  124. </PanelBody>
  125. </Panel>
  126. <Panel>
  127. <PanelHeader>{t('Activity in the last 30 days (by day)')}</PanelHeader>
  128. <PanelBody withPadding>
  129. {isLoading && <Placeholder height="100px" />}
  130. {!isLoading && (
  131. <MiniBarChart
  132. series={series}
  133. colors={CHART_PALETTE[0]}
  134. height={100}
  135. isGroupedByDate
  136. stacked
  137. labelYAxisExtents
  138. />
  139. )}
  140. {!isLoading && isChartEmpty && (
  141. <EmptyMessage
  142. title={t('No activity.')}
  143. description={t("We don't have data for this metric in the last 30 days.")}
  144. />
  145. )}
  146. </PanelBody>
  147. </Panel>
  148. <PanelTable
  149. headers={[<TableHeading key="tags"> {t('Tags')}</TableHeading>]}
  150. emptyMessage={t('There are no tags for this metric.')}
  151. isEmpty={tags.length === 0}
  152. isLoading={isLoading}
  153. >
  154. {tags.map(({key}) => (
  155. <div key={key}>{key}</div>
  156. ))}
  157. </PanelTable>
  158. <Panel>
  159. <PanelHeader>{t('Code Location')}</PanelHeader>
  160. <PanelBody withPadding>
  161. <CodeLocations mri={mri} />
  162. </PanelBody>
  163. </Panel>
  164. </Fragment>
  165. );
  166. }
  167. const TableHeading = styled('div')`
  168. color: ${p => p.theme.textColor};
  169. `;
  170. const MetricName = styled('div')`
  171. word-break: break-word;
  172. `;
  173. export default ProjectMetricsDetails;