TopicContents.tsx 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. import { Table } from 'components/common/table/Table/Table.styled';
  2. import TableHeaderCell from 'components/common/table/TableHeaderCell/TableHeaderCell';
  3. import { ConsumerGroupTopicPartition, SortOrder } from 'generated-sources';
  4. import React from 'react';
  5. import { ContentBox, TopicContentWrapper } from './TopicContent.styled';
  6. interface Props {
  7. consumers: ConsumerGroupTopicPartition[];
  8. }
  9. type OrderByKey = keyof ConsumerGroupTopicPartition;
  10. interface Headers {
  11. title: string;
  12. orderBy: OrderByKey | undefined;
  13. }
  14. const TABLE_HEADERS_MAP: Headers[] = [
  15. { title: 'Partition', orderBy: 'partition' },
  16. { title: 'Consumer ID', orderBy: 'consumerId' },
  17. { title: 'Host', orderBy: 'host' },
  18. { title: 'Messages Behind', orderBy: 'messagesBehind' },
  19. { title: 'Current Offset', orderBy: 'currentOffset' },
  20. { title: 'End offset', orderBy: 'endOffset' },
  21. ];
  22. const ipV4ToNum = (ip?: string) => {
  23. if (typeof ip === 'string' && ip.length !== 0) {
  24. const withoutSlash = ip.indexOf('/') !== -1 ? ip.slice(1) : ip;
  25. return Number(
  26. withoutSlash
  27. .split('.')
  28. .map((octet) => `000${octet}`.slice(-3))
  29. .join('')
  30. );
  31. }
  32. return 0;
  33. };
  34. type ComparatorFunction<T> = (
  35. valueA: T,
  36. valueB: T,
  37. order: SortOrder,
  38. property?: keyof T
  39. ) => number;
  40. const numberComparator: ComparatorFunction<ConsumerGroupTopicPartition> = (
  41. valueA,
  42. valueB,
  43. order,
  44. property
  45. ) => {
  46. if (property !== undefined) {
  47. return order === SortOrder.ASC
  48. ? Number(valueA[property]) - Number(valueB[property])
  49. : Number(valueB[property]) - Number(valueA[property]);
  50. }
  51. return 0;
  52. };
  53. const ipComparator: ComparatorFunction<ConsumerGroupTopicPartition> = (
  54. valueA,
  55. valueB,
  56. order
  57. ) =>
  58. order === SortOrder.ASC
  59. ? ipV4ToNum(valueA.host) - ipV4ToNum(valueB.host)
  60. : ipV4ToNum(valueB.host) - ipV4ToNum(valueA.host);
  61. const consumerIdComparator: ComparatorFunction<ConsumerGroupTopicPartition> = (
  62. valueA,
  63. valueB,
  64. order
  65. ) => {
  66. if (valueA.consumerId && valueB.consumerId) {
  67. if (order === SortOrder.ASC) {
  68. if (valueA.consumerId?.toLowerCase() > valueB.consumerId?.toLowerCase()) {
  69. return 1;
  70. }
  71. }
  72. if (order === SortOrder.DESC) {
  73. if (valueB.consumerId?.toLowerCase() > valueA.consumerId?.toLowerCase()) {
  74. return -1;
  75. }
  76. }
  77. }
  78. return 0;
  79. };
  80. const TopicContents: React.FC<Props> = ({ consumers }) => {
  81. const [orderBy, setOrderBy] = React.useState<OrderByKey>('partition');
  82. const [sortOrder, setSortOrder] = React.useState<SortOrder>(SortOrder.DESC);
  83. const handleOrder = React.useCallback((columnName: string | null) => {
  84. if (typeof columnName === 'string') {
  85. setOrderBy(columnName as OrderByKey);
  86. setSortOrder((prevOrder) =>
  87. prevOrder === SortOrder.DESC ? SortOrder.ASC : SortOrder.DESC
  88. );
  89. }
  90. }, []);
  91. const sortedConsumers = React.useMemo(() => {
  92. if (orderBy && sortOrder) {
  93. const isNumberProperty =
  94. orderBy === 'partition' ||
  95. orderBy === 'currentOffset' ||
  96. orderBy === 'endOffset' ||
  97. orderBy === 'messagesBehind';
  98. let comparator: ComparatorFunction<ConsumerGroupTopicPartition>;
  99. if (isNumberProperty) {
  100. comparator = numberComparator;
  101. }
  102. if (orderBy === 'host') {
  103. comparator = ipComparator;
  104. }
  105. if (orderBy === 'consumerId') {
  106. comparator = consumerIdComparator;
  107. }
  108. return consumers.sort((a, b) => comparator(a, b, sortOrder, orderBy));
  109. }
  110. return consumers;
  111. }, [orderBy, sortOrder, consumers]);
  112. return (
  113. <TopicContentWrapper>
  114. <td colSpan={3}>
  115. <ContentBox>
  116. <Table isFullwidth>
  117. <thead>
  118. <tr>
  119. {TABLE_HEADERS_MAP.map((header) => (
  120. <TableHeaderCell
  121. key={header.orderBy}
  122. title={header.title}
  123. orderBy={orderBy}
  124. sortOrder={sortOrder}
  125. orderValue={header.orderBy}
  126. handleOrderBy={handleOrder}
  127. />
  128. ))}
  129. </tr>
  130. </thead>
  131. <tbody>
  132. {sortedConsumers.map((consumer) => (
  133. <tr key={consumer.partition}>
  134. <td>{consumer.partition}</td>
  135. <td>{consumer.consumerId}</td>
  136. <td>{consumer.host}</td>
  137. <td>{consumer.messagesBehind}</td>
  138. <td>{consumer.currentOffset}</td>
  139. <td>{consumer.endOffset}</td>
  140. </tr>
  141. ))}
  142. </tbody>
  143. </Table>
  144. </ContentBox>
  145. </td>
  146. </TopicContentWrapper>
  147. );
  148. };
  149. export default TopicContents;