projectAllocationsTable.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import {useMemo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {Tooltip} from 'sentry/components/tooltip';
  4. import {t} from 'sentry/locale';
  5. import {space} from 'sentry/styles/space';
  6. import {SINGULAR_DATA_CATEGORY} from 'getsentry/utils/dataCategory';
  7. import AllocationRow from './components/allocationRow';
  8. import {Centered, Divider, HalvedWithDivider} from './components/styles';
  9. import type {SpendAllocation} from './components/types';
  10. import type {BigNumUnits} from './utils';
  11. import {midPeriod} from './utils';
  12. type Props = {
  13. deleteSpendAllocation: (
  14. selectedMetric: string,
  15. targetId: number,
  16. targetType: string,
  17. timestamp: number
  18. ) => (e: React.MouseEvent) => void;
  19. metricUnit: BigNumUnits;
  20. openForm: (formData?: SpendAllocation) => (e: React.MouseEvent) => void;
  21. selectedMetric: string;
  22. spendAllocations?: SpendAllocation[];
  23. };
  24. function ProjectAllocationsTable({
  25. deleteSpendAllocation,
  26. metricUnit,
  27. openForm,
  28. selectedMetric,
  29. spendAllocations = [],
  30. }: Props) {
  31. const filteredMetrics: SpendAllocation[] = useMemo(() => {
  32. const filtered = spendAllocations.filter(
  33. allocation =>
  34. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  35. allocation.billingMetric === SINGULAR_DATA_CATEGORY[selectedMetric] &&
  36. allocation.targetType === 'Project'
  37. );
  38. // NOTE: This will NOT work once we include multiple layers. We'll need to construct a tree
  39. return filtered;
  40. }, [spendAllocations, selectedMetric]);
  41. return (
  42. <Wrapper>
  43. <Table data-test-id="allocations-table">
  44. <colgroup>
  45. <col />
  46. <col style={{width: '15%'}} />
  47. <col style={{width: '15%'}} />
  48. <col style={{width: '15%'}} />
  49. <col style={{width: '15%'}} />
  50. <col style={{width: '15%'}} />
  51. </colgroup>
  52. <tbody>
  53. <tr>
  54. <HeaderCell>{t('Project')}</HeaderCell>
  55. <HeaderCell style={{textAlign: 'right'}}>
  56. <Tooltip title="Allocated events are guaranteed for your specified projects. If your project goes past its allocated amount, the extra events will consume the root allocation for the organization">
  57. {t('Allocated')}
  58. </Tooltip>
  59. </HeaderCell>
  60. <HeaderCell>
  61. <HalvedWithDivider>
  62. <Centered>{t('Spend')}</Centered>
  63. <Centered>
  64. <Divider />
  65. </Centered>
  66. <Centered>{t('Events')}</Centered>
  67. </HalvedWithDivider>
  68. </HeaderCell>
  69. <HeaderCell style={{textAlign: 'right'}}>
  70. <Tooltip title="Consumed events indicate your usage per allocation">
  71. {t('Consumed')}
  72. </Tooltip>
  73. </HeaderCell>
  74. <HeaderCell>
  75. <HalvedWithDivider>
  76. <Centered>Spend</Centered>
  77. <Centered>
  78. <Divider />
  79. </Centered>
  80. <Centered>Events</Centered>
  81. </HalvedWithDivider>
  82. </HeaderCell>
  83. <HeaderCell />
  84. </tr>
  85. {filteredMetrics.map(a => (
  86. <AllocationRow
  87. key={a.id}
  88. allocation={a}
  89. deleteAction={deleteSpendAllocation(
  90. // @ts-expect-error TS(7053): Element implicitly has an 'any' type because expre... Remove this comment to see the full error message
  91. SINGULAR_DATA_CATEGORY[selectedMetric],
  92. a.targetId,
  93. a.targetType,
  94. midPeriod(a.period)
  95. )}
  96. openForm={openForm(a)}
  97. metricUnit={metricUnit}
  98. />
  99. ))}
  100. {!filteredMetrics.length && (
  101. <tr>
  102. <TableData data-test-id="no-allocations">
  103. {t('No allocations set')}
  104. </TableData>
  105. </tr>
  106. )}
  107. </tbody>
  108. </Table>
  109. </Wrapper>
  110. );
  111. }
  112. export default ProjectAllocationsTable;
  113. const Wrapper = styled('div')`
  114. margin: ${space(2)} 0;
  115. `;
  116. const Table = styled('table')`
  117. background: ${p => p.theme.background};
  118. border-radius: ${p => p.theme.borderRadius};
  119. border-collapse: separate;
  120. border: 1px ${p => 'solid ' + p.theme.border};
  121. box-shadow: ${p => p.theme.dropShadowMedium};
  122. margin-bottom: ${space(2)};
  123. width: 100%;
  124. `;
  125. const HeaderCell = styled('th')`
  126. color: ${p => p.theme.subText};
  127. font-size: ${p => p.theme.fontSizeSmall};
  128. font-weight: 600;
  129. text-transform: uppercase;
  130. border-radius: ${p => p.theme.borderRadius} ${p => p.theme.borderRadius} 0 0;
  131. background: ${p => p.theme.backgroundSecondary};
  132. padding: ${space(1)} ${space(2)};
  133. `;
  134. const TableData = styled('td')`
  135. padding: ${space(2)};
  136. `;