Overview.tsx 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import React from 'react';
  2. import { Partition, Replica } from 'generated-sources';
  3. import { ClusterName, TopicName } from 'redux/interfaces';
  4. import Dropdown from 'components/common/Dropdown/Dropdown';
  5. import DropdownItem from 'components/common/Dropdown/DropdownItem';
  6. import ClusterContext from 'components/contexts/ClusterContext';
  7. import BytesFormatted from 'components/common/BytesFormatted/BytesFormatted';
  8. import { Table } from 'components/common/table/Table/Table.styled';
  9. import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell';
  10. import VerticalElipsisIcon from 'components/common/Icons/VerticalElipsisIcon';
  11. import * as Metrics from 'components/common/Metrics';
  12. import { Tag } from 'components/common/Tag/Tag.styled';
  13. import { useAppSelector } from 'lib/hooks/redux';
  14. import { getTopicByName } from 'redux/reducers/topics/selectors';
  15. import { ReplicaCell } from 'components/Topics/Topic/Details/Details.styled';
  16. import { RouteParamsClusterTopic } from 'lib/paths';
  17. import useAppParams from 'lib/hooks/useAppParams';
  18. export interface Props {
  19. clearTopicMessages(params: {
  20. clusterName: ClusterName;
  21. topicName: TopicName;
  22. partitions?: number[];
  23. }): void;
  24. }
  25. const Overview: React.FC<Props> = ({ clearTopicMessages }) => {
  26. const { clusterName, topicName } = useAppParams<RouteParamsClusterTopic>();
  27. const {
  28. partitions,
  29. underReplicatedPartitions,
  30. inSyncReplicas,
  31. replicas,
  32. partitionCount,
  33. internal,
  34. replicationFactor,
  35. segmentSize,
  36. segmentCount,
  37. cleanUpPolicy,
  38. } = useAppSelector((state) => {
  39. const res = getTopicByName(state, topicName);
  40. return res || {};
  41. });
  42. const { isReadOnly } = React.useContext(ClusterContext);
  43. const messageCount = React.useMemo(
  44. () =>
  45. (partitions || []).reduce((memo, partition) => {
  46. return memo + partition.offsetMax - partition.offsetMin;
  47. }, 0),
  48. [partitions]
  49. );
  50. return (
  51. <>
  52. <Metrics.Wrapper>
  53. <Metrics.Section>
  54. <Metrics.Indicator label="Partitions">
  55. {partitionCount}
  56. </Metrics.Indicator>
  57. <Metrics.Indicator label="Replication Factor">
  58. {replicationFactor}
  59. </Metrics.Indicator>
  60. <Metrics.Indicator
  61. label="URP"
  62. title="Under replicated partitions"
  63. isAlert
  64. alertType={underReplicatedPartitions === 0 ? 'success' : 'error'}
  65. >
  66. {underReplicatedPartitions === 0 ? (
  67. <Metrics.LightText>{underReplicatedPartitions}</Metrics.LightText>
  68. ) : (
  69. <Metrics.RedText>{underReplicatedPartitions}</Metrics.RedText>
  70. )}
  71. </Metrics.Indicator>
  72. <Metrics.Indicator
  73. label="In Sync Replicas"
  74. isAlert
  75. alertType={inSyncReplicas === replicas ? 'success' : 'error'}
  76. >
  77. {inSyncReplicas && replicas && inSyncReplicas < replicas ? (
  78. <Metrics.RedText>{inSyncReplicas}</Metrics.RedText>
  79. ) : (
  80. inSyncReplicas
  81. )}
  82. <Metrics.LightText> of {replicas}</Metrics.LightText>
  83. </Metrics.Indicator>
  84. <Metrics.Indicator label="Type">
  85. <Tag color="gray">{internal ? 'Internal' : 'External'}</Tag>
  86. </Metrics.Indicator>
  87. <Metrics.Indicator label="Segment Size" title="">
  88. <BytesFormatted value={segmentSize} />
  89. </Metrics.Indicator>
  90. <Metrics.Indicator label="Segment Count">
  91. {segmentCount}
  92. </Metrics.Indicator>
  93. <Metrics.Indicator label="Clean Up Policy">
  94. <Tag color="gray">{cleanUpPolicy || 'Unknown'}</Tag>
  95. </Metrics.Indicator>
  96. <Metrics.Indicator label="Message Count">
  97. {messageCount}
  98. </Metrics.Indicator>
  99. </Metrics.Section>
  100. </Metrics.Wrapper>
  101. <div>
  102. <Table isFullwidth>
  103. <thead>
  104. <tr>
  105. <TableHeaderCell title="Partition ID" />
  106. <TableHeaderCell title="Replicas" />
  107. <TableHeaderCell title="First Offset" />
  108. <TableHeaderCell title="Next Offset" />
  109. <TableHeaderCell title="Message Count" />
  110. <TableHeaderCell title=" " />
  111. </tr>
  112. </thead>
  113. <tbody>
  114. {partitions?.map((partition: Partition) => (
  115. <tr key={`partition-list-item-key-${partition.partition}`}>
  116. <td>{partition.partition}</td>
  117. <td>
  118. {partition.replicas?.map((replica: Replica) => (
  119. <ReplicaCell
  120. leader={replica.leader}
  121. key={`replica-list-item-key-${replica.broker}`}
  122. >
  123. {replica.broker}
  124. </ReplicaCell>
  125. ))}
  126. </td>
  127. <td>{partition.offsetMin}</td>
  128. <td>{partition.offsetMax}</td>
  129. <td>{partition.offsetMax - partition.offsetMin}</td>
  130. <td style={{ width: '5%' }}>
  131. {!internal && !isReadOnly && cleanUpPolicy === 'DELETE' ? (
  132. <Dropdown label={<VerticalElipsisIcon />} right>
  133. <DropdownItem
  134. onClick={() =>
  135. clearTopicMessages({
  136. clusterName,
  137. topicName,
  138. partitions: [partition.partition],
  139. })
  140. }
  141. danger
  142. >
  143. Clear Messages
  144. </DropdownItem>
  145. </Dropdown>
  146. ) : null}
  147. </td>
  148. </tr>
  149. ))}
  150. {partitions?.length === 0 && (
  151. <tr>
  152. <td colSpan={10}>No Partitions found</td>
  153. </tr>
  154. )}
  155. </tbody>
  156. </Table>
  157. </div>
  158. </>
  159. );
  160. };
  161. export default Overview;