StatusTable.jsx 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187
  1. import React from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import PropTypes from 'prop-types';
  4. class StatusTable extends React.PureComponent {
  5. renderPreInitializedLabel() {
  6. return <span className="badge text-bg-default">――</span>;
  7. }
  8. renderConnectionStatusLabels() {
  9. const { t } = this.props;
  10. const {
  11. isErrorOccuredOnSearchService,
  12. isConnected, isConfigured,
  13. } = this.props;
  14. const errorOccuredLabel = isErrorOccuredOnSearchService
  15. ? <span className="badge text-bg-danger ms-2">{ t('full_text_search_management.connection_status_label_erroroccured') }</span>
  16. : null;
  17. let connectionStatusLabel = null;
  18. if (!isConfigured) {
  19. connectionStatusLabel = (
  20. <span className="badge text-bg-default">
  21. { t('full_text_search_management.connection_status_label_unconfigured') }
  22. </span>
  23. );
  24. }
  25. else {
  26. connectionStatusLabel = isConnected
  27. // eslint-disable-next-line max-len
  28. ? <span data-testid="connection-status-badge-connected" className="badge text-bg-success">{ t('full_text_search_management.connection_status_label_connected') }</span>
  29. : <span className="badge text-bg-danger">{ t('full_text_search_management.connection_status_label_disconnected') }</span>;
  30. }
  31. return (
  32. <>
  33. {connectionStatusLabel}{errorOccuredLabel}
  34. </>
  35. );
  36. }
  37. renderIndicesStatusLabel() {
  38. const { t, isNormalized } = this.props;
  39. return isNormalized
  40. ? <span className="badge text-bg-info">{ t('full_text_search_management.indices_status_label_normalized') }</span>
  41. : <span className="badge text-bg-warning">{ t('full_text_search_management.indices_status_label_unnormalized') }</span>;
  42. }
  43. renderIndexInfoPanel(indexName, body = {}, aliases = []) {
  44. const collapseId = `collapse-${indexName}`;
  45. const aliasLabels = aliases.map((aliasName) => {
  46. return (
  47. <span key={`badge-${indexName}-${aliasName}`} className="badge text-bg-primary me-2">
  48. <span className="material-symbols-outlined">sell</span>
  49. <span>{aliasName}</span>
  50. </span>
  51. );
  52. });
  53. return (
  54. <div className="card">
  55. <div className="card-header">
  56. <a role="button" className="text-nowrap me-2" data-bs-toggle="collapse" href={`#${collapseId}`} aria-expanded="true" aria-controls={collapseId}>
  57. <span className="material-symbols-outlined">database</span> {indexName}
  58. </a>
  59. <span className="ms-md-3">{aliasLabels}</span>
  60. </div>
  61. <div id={collapseId} className="collapse">
  62. <div className="card-body">
  63. <pre>
  64. {JSON.stringify(body, null, 2)}
  65. </pre>
  66. </div>
  67. </div>
  68. </div>
  69. );
  70. }
  71. renderIndexInfoPanels() {
  72. const {
  73. indicesData,
  74. aliasesData,
  75. } = this.props;
  76. // data is null
  77. if (indicesData == null) {
  78. return null;
  79. }
  80. /*
  81. "indices": {
  82. "growi": {
  83. ...
  84. }
  85. },
  86. */
  87. const indexNameToDataMap = {};
  88. for (const [indexName, indexData] of Object.entries(indicesData)) {
  89. indexNameToDataMap[indexName] = indexData;
  90. }
  91. // no indices
  92. if (indexNameToDataMap.length === 0) {
  93. return null;
  94. }
  95. /*
  96. "aliases": {
  97. "growi": {
  98. "aliases": {
  99. "growi-alias": {}
  100. }
  101. }
  102. },
  103. */
  104. const indexNameToAliasMap = {};
  105. for (const [indexName, aliasData] of Object.entries(aliasesData)) {
  106. indexNameToAliasMap[indexName] = Object.keys(aliasData.aliases);
  107. }
  108. return (
  109. <div className="row">
  110. { Object.keys(indexNameToDataMap).map((indexName) => {
  111. return (
  112. <div key={`col-${indexName}`} className="col-md-6">
  113. { this.renderIndexInfoPanel(indexName, indexNameToDataMap[indexName], indexNameToAliasMap[indexName]) }
  114. </div>
  115. );
  116. }) }
  117. </div>
  118. );
  119. }
  120. render() {
  121. const { t } = this.props;
  122. const {
  123. isInitialized,
  124. } = this.props;
  125. return (
  126. <table className="table table-bordered">
  127. <tbody>
  128. <tr>
  129. <th className="w-25">{t('full_text_search_management.connection_status')}</th>
  130. <td className="w-75">{ isInitialized ? this.renderConnectionStatusLabels() : this.renderPreInitializedLabel() }</td>
  131. </tr>
  132. <tr>
  133. <th className="w-25">{t('full_text_search_management.indices_status')}</th>
  134. <td className="w-75">{ isInitialized ? this.renderIndicesStatusLabel() : this.renderPreInitializedLabel() }</td>
  135. </tr>
  136. <tr>
  137. <th className="w-25">{t('full_text_search_management.indices_summary')}</th>
  138. <td className="p-4 w-75">{ isInitialized && this.renderIndexInfoPanels() }</td>
  139. </tr>
  140. </tbody>
  141. </table>
  142. );
  143. }
  144. }
  145. const StatusTableWrapperFC = (props) => {
  146. const { t } = useTranslation('admin');
  147. return <StatusTable t={t} {...props} />;
  148. };
  149. StatusTable.propTypes = {
  150. t: PropTypes.func.isRequired, // i18next
  151. isInitialized: PropTypes.bool,
  152. isErrorOccuredOnSearchService: PropTypes.bool,
  153. isConnected: PropTypes.bool,
  154. isConfigured: PropTypes.bool,
  155. isNormalized: PropTypes.bool,
  156. indicesData: PropTypes.object,
  157. aliasesData: PropTypes.object,
  158. };
  159. export default StatusTableWrapperFC;