PageBulkExportSettings.tsx 6.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import { type JSX, useCallback, useEffect, useState } from 'react';
  2. import { LoadingSpinner } from '@growi/ui/dist/components';
  3. import { useTranslation } from 'next-i18next';
  4. import { apiv3Put } from '~/client/util/apiv3-client';
  5. import { toastError, toastSuccess } from '~/client/util/toastr';
  6. import { useSWRxAppSettings } from '~/stores/admin/app-settings';
  7. import AdminUpdateButtonRow from '../Common/AdminUpdateButtonRow';
  8. import { ConfirmModal } from './ConfirmModal';
  9. const PageBulkExportSettings = (): JSX.Element => {
  10. const { t } = useTranslation(['admin', 'commons']);
  11. const { data, error, mutate } = useSWRxAppSettings();
  12. const [isBulkExportPagesEnabled, setIsBulkExportPagesEnabled] = useState(
  13. data?.isBulkExportPagesEnabled,
  14. );
  15. const [
  16. bulkExportDownloadExpirationSeconds,
  17. setBulkExportDownloadExpirationSeconds,
  18. ] = useState(data?.bulkExportDownloadExpirationSeconds);
  19. const [isReloadModalOpen, setReloadModalOpen] = useState(false);
  20. const changeBulkExportDownloadExpirationSeconds = (
  21. bulkExportDownloadExpirationDays: number,
  22. ) => {
  23. const bulkExportDownloadExpirationSeconds =
  24. bulkExportDownloadExpirationDays * 24 * 60 * 60;
  25. setBulkExportDownloadExpirationSeconds(bulkExportDownloadExpirationSeconds);
  26. };
  27. const onSubmitHandler = useCallback(async () => {
  28. // Only the enable flag affects the page-side export menu visibility, which
  29. // is hydrated once per page load and therefore needs a reload to reflect.
  30. // The expiration period is read server-side, so a reload prompt is unneeded.
  31. const isEnabledFlagChanged =
  32. isBulkExportPagesEnabled !== data?.isBulkExportPagesEnabled;
  33. try {
  34. await apiv3Put('/app-settings/page-bulk-export-settings', {
  35. isBulkExportPagesEnabled,
  36. bulkExportDownloadExpirationSeconds,
  37. });
  38. toastSuccess(
  39. t('commons:toaster.update_successed', {
  40. target: t('app_setting.page_bulk_export_settings'),
  41. }),
  42. );
  43. if (isEnabledFlagChanged) {
  44. setReloadModalOpen(true);
  45. }
  46. } catch (err) {
  47. toastError(err);
  48. }
  49. mutate();
  50. }, [
  51. isBulkExportPagesEnabled,
  52. bulkExportDownloadExpirationSeconds,
  53. data,
  54. mutate,
  55. t,
  56. ]);
  57. useEffect(() => {
  58. if (data?.useOnlyEnvVarForFileUploadType) {
  59. setIsBulkExportPagesEnabled(data?.envIsBulkExportPagesEnabled);
  60. } else {
  61. setIsBulkExportPagesEnabled(data?.isBulkExportPagesEnabled);
  62. }
  63. setBulkExportDownloadExpirationSeconds(
  64. data?.bulkExportDownloadExpirationSeconds,
  65. );
  66. }, [data]);
  67. const isLoading = data === undefined && error === undefined;
  68. return (
  69. <>
  70. {isLoading && (
  71. <div className="text-muted text-center mb-5">
  72. <LoadingSpinner className="me-1 fs-3" />
  73. </div>
  74. )}
  75. {!isLoading && (
  76. <>
  77. <p className="card custom-card bg-warning-subtle my-3">
  78. {t('admin:app_setting.page_bulk_export_explanation')} <br />
  79. <span className="text-danger mt-1">
  80. {t('admin:app_setting.page_bulk_export_warning')}
  81. </span>
  82. </p>
  83. <div className="my-4 row">
  84. <div className="text-start text-md-end col-md-3 col-form-label"></div>
  85. <div className="col-md-6">
  86. <div className="form-check form-switch form-check-info">
  87. <input
  88. type="checkbox"
  89. className="form-check-input"
  90. id="cbIsPageBulkExportEnabled"
  91. checked={isBulkExportPagesEnabled}
  92. disabled={data?.useOnlyEnvVarsForIsBulkExportPagesEnabled}
  93. onChange={(e) =>
  94. setIsBulkExportPagesEnabled(e.target.checked)
  95. }
  96. />
  97. <label
  98. className="form-label form-check-label"
  99. htmlFor="cbIsPageBulkExportEnabled"
  100. >
  101. {t('app_setting.enable_page_bulk_export')}
  102. </label>
  103. </div>
  104. {data?.useOnlyEnvVarsForIsBulkExportPagesEnabled && (
  105. <p className="form-text text-muted">
  106. <b
  107. // biome-ignore lint/security/noDangerouslySetInnerHtml: includes markup from i18n strings
  108. dangerouslySetInnerHTML={{
  109. __html: t('admin:app_setting.fixed_by_env_var', {
  110. envKey: 'BULK_EXPORT_PAGES_ENABLED',
  111. envVar: isBulkExportPagesEnabled,
  112. }),
  113. }}
  114. />
  115. </p>
  116. )}
  117. </div>
  118. </div>
  119. <div className="mb-4">
  120. <div className="row">
  121. <label
  122. className="text-start text-md-end col-md-3 col-form-label"
  123. htmlFor="admin-page-bulk-export-expiration"
  124. >
  125. {t('app_setting.page_bulk_export_storage_period')}
  126. </label>
  127. <div className="col-md-2">
  128. <select
  129. className="form-select"
  130. id="admin-page-bulk-export-expiration"
  131. value={
  132. (bulkExportDownloadExpirationSeconds ?? 0) / (24 * 60 * 60)
  133. }
  134. onChange={(e) => {
  135. changeBulkExportDownloadExpirationSeconds(
  136. Number(e.target.value),
  137. );
  138. }}
  139. >
  140. {Array.from({ length: 7 }, (_, i) => i + 1).map((number) => (
  141. <option
  142. key={`be-download-expiration-option-${number}`}
  143. value={number}
  144. >
  145. {number} {t('admin:days')}
  146. </option>
  147. ))}
  148. </select>
  149. </div>
  150. </div>
  151. </div>
  152. <AdminUpdateButtonRow onClick={onSubmitHandler} />
  153. </>
  154. )}
  155. <ConfirmModal
  156. isModalOpen={isReloadModalOpen}
  157. title={t('admin:app_setting.page_bulk_export_reload_title')}
  158. headerClassName="text-primary"
  159. iconName="refresh"
  160. warningMessage={t('admin:app_setting.page_bulk_export_reload_prompt')}
  161. supplymentaryMessage={null}
  162. confirmButtonTitle={t('admin:app_setting.reload_page')}
  163. cancelButtonTitle={t(
  164. 'admin:app_setting.page_bulk_export_reload_dismiss',
  165. )}
  166. onConfirm={() => {
  167. window.location.reload();
  168. return Promise.resolve();
  169. }}
  170. onCancel={() => setReloadModalOpen(false)}
  171. />
  172. </>
  173. );
  174. };
  175. export default PageBulkExportSettings;