AdminHome.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import type { FC } from 'react';
  2. import { useCallback, useEffect, useId } from 'react';
  3. import { useTranslation } from 'next-i18next';
  4. import { CopyToClipboard } from 'react-copy-to-clipboard';
  5. import { Tooltip } from 'reactstrap';
  6. import AdminHomeContainer from '~/client/services/AdminHomeContainer';
  7. import { toastError } from '~/client/util/toastr';
  8. import { useSWRxV5MigrationStatus } from '~/stores/page-listing';
  9. import loggerFactory from '~/utils/logger';
  10. import { withUnstatedContainers } from '../../UnstatedUtils';
  11. import { EnvVarsTable } from './EnvVarsTable';
  12. import SystemInfomationTable from './SystemInfomationTable';
  13. const logger = loggerFactory('growi:admin');
  14. type Props = {
  15. adminHomeContainer: AdminHomeContainer;
  16. };
  17. const AdminHome: FC<Props> = (props) => {
  18. const { adminHomeContainer } = props;
  19. const { t } = useTranslation();
  20. const { data: migrationStatus } = useSWRxV5MigrationStatus();
  21. const fetchAdminHomeData = useCallback(async () => {
  22. try {
  23. await adminHomeContainer.retrieveAdminHomeData();
  24. } catch (err) {
  25. toastError(err);
  26. logger.error(err);
  27. }
  28. }, [adminHomeContainer]);
  29. useEffect(() => {
  30. fetchAdminHomeData();
  31. }, [fetchAdminHomeData]);
  32. // Generate CSS-safe ID by removing colons from useId() result
  33. const prefilledHostInformationButtonIdRaw = useId();
  34. const prefilledHostInformationButtonId = `prefilled-host-info-button-${prefilledHostInformationButtonIdRaw.replace(/:/g, '')}`;
  35. return (
  36. <div data-testid="admin-home">
  37. {
  38. // Alert message will be displayed in case that the GROWI is under maintenance
  39. adminHomeContainer.state.isMaintenanceMode && (
  40. <div className="alert alert-danger alert-link" role="alert">
  41. <h3 className="alert-heading">
  42. {t('admin:maintenance_mode.maintenance_mode')}
  43. </h3>
  44. <p>{t('admin:maintenance_mode.description')}</p>
  45. <hr />
  46. <a className="btn-link" href="/admin/app" rel="noopener noreferrer">
  47. <span
  48. className="material-symbols-outlined ms-1"
  49. aria-hidden="true"
  50. >
  51. link
  52. </span>
  53. <strong>
  54. {t('admin:maintenance_mode.end_maintenance_mode')}
  55. </strong>
  56. </a>
  57. </div>
  58. )
  59. }
  60. {
  61. // Alert message will be displayed in case that V5 migration has not been compleated
  62. migrationStatus != null && !migrationStatus.isV5Compatible && (
  63. <div
  64. className={`alert ${migrationStatus.isV5Compatible == null ? 'alert-warning' : 'alert-info'}`}
  65. >
  66. {t('admin:v5_page_migration.migration_desc')}
  67. <a className="btn-link" href="/admin/app" rel="noopener noreferrer">
  68. <span
  69. className="material-symbols-outlined ms-1"
  70. aria-hidden="true"
  71. >
  72. link
  73. </span>
  74. <strong>{t('admin:v5_page_migration.upgrade_to_v5')}</strong>
  75. </a>
  76. </div>
  77. )
  78. }
  79. <p>
  80. {t('admin:admin_top.wiki_administrator')}
  81. <br></br>
  82. {t('admin:admin_top.assign_administrator')}
  83. </p>
  84. <div className="row mb-5">
  85. <div className="col-lg-12">
  86. <h2 className="admin-setting-header">
  87. {t('admin:admin_top.system_information')}
  88. </h2>
  89. <SystemInfomationTable />
  90. </div>
  91. </div>
  92. <div className="row mb-5">
  93. <div className="col-md-12">
  94. <h2 className="admin-setting-header">
  95. {t('admin:admin_top.list_of_env_vars')}
  96. </h2>
  97. <p>{t('admin:admin_top.env_var_priority')}</p>
  98. <p
  99. // biome-ignore lint/security/noDangerouslySetInnerHtml: ignore
  100. dangerouslySetInnerHTML={{
  101. __html: t('admin:admin_top.about_security'),
  102. }}
  103. />
  104. <EnvVarsTable envVars={adminHomeContainer.state.envVars} />
  105. </div>
  106. </div>
  107. <div className="row mb-5">
  108. <div className="col-md-12">
  109. <h2 className="admin-setting-header">
  110. {t('admin:admin_top.bug_report')}
  111. </h2>
  112. <div className="d-flex align-items-center">
  113. <CopyToClipboard
  114. text={adminHomeContainer.generatePrefilledHostInformationMarkdown()}
  115. onCopy={() => adminHomeContainer.onCopyPrefilledHostInformation()}
  116. >
  117. <button
  118. id={prefilledHostInformationButtonId}
  119. type="button"
  120. className="btn btn-primary"
  121. >
  122. {t('admin:admin_top:copy_prefilled_host_information:default')}
  123. </button>
  124. </CopyToClipboard>
  125. <Tooltip
  126. placement="bottom"
  127. isOpen={
  128. adminHomeContainer.state.copyState ===
  129. adminHomeContainer.copyStateValues?.DONE
  130. }
  131. target={prefilledHostInformationButtonId}
  132. fade={false}
  133. >
  134. {t('admin:admin_top:copy_prefilled_host_information:done')}
  135. </Tooltip>
  136. <span
  137. className="ms-2"
  138. // biome-ignore lint/security/noDangerouslySetInnerHtml: ignore
  139. dangerouslySetInnerHTML={{
  140. __html: t('admin:admin_top:submit_bug_report'),
  141. }}
  142. />
  143. </div>
  144. </div>
  145. </div>
  146. </div>
  147. );
  148. };
  149. const AdminHomeWrapper = withUnstatedContainers(AdminHome, [
  150. AdminHomeContainer,
  151. ]);
  152. export default AdminHomeWrapper;