MailSetting.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. import { useCallback, useEffect } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { useForm } from 'react-hook-form';
  4. import AdminAppContainer from '~/client/services/AdminAppContainer';
  5. import { toastError, toastSuccess } from '~/client/util/toastr';
  6. import { withUnstatedContainers } from '../../UnstatedUtils';
  7. import { OAuth2Setting } from './OAuth2Setting';
  8. import { SesSetting } from './SesSetting';
  9. import { SmtpSetting } from './SmtpSetting';
  10. type Props = {
  11. adminAppContainer: AdminAppContainer;
  12. };
  13. const MailSetting = (props: Props) => {
  14. const { t } = useTranslation(['admin', 'commons']);
  15. const { adminAppContainer } = props;
  16. const transmissionMethods = ['smtp', 'ses', 'oauth2'];
  17. const { register, handleSubmit, reset, watch } = useForm();
  18. // Watch the transmission method to dynamically switch between SMTP and SES settings
  19. const currentTransmissionMethod = watch(
  20. 'transmissionMethod',
  21. adminAppContainer.state.transmissionMethod || 'smtp',
  22. );
  23. // Reset form when adminAppContainer state changes
  24. useEffect(() => {
  25. reset({
  26. fromAddress: adminAppContainer.state.fromAddress || '',
  27. transmissionMethod: adminAppContainer.state.transmissionMethod || 'smtp',
  28. smtpHost: adminAppContainer.state.smtpHost || '',
  29. smtpPort: adminAppContainer.state.smtpPort || '',
  30. smtpUser: adminAppContainer.state.smtpUser || '',
  31. smtpPassword: adminAppContainer.state.smtpPassword || '',
  32. sesAccessKeyId: adminAppContainer.state.sesAccessKeyId || '',
  33. sesSecretAccessKey: adminAppContainer.state.sesSecretAccessKey || '',
  34. oauth2ClientId: adminAppContainer.state.oauth2ClientId || '',
  35. oauth2ClientSecret: adminAppContainer.state.oauth2ClientSecret || '',
  36. oauth2RefreshToken: adminAppContainer.state.oauth2RefreshToken || '',
  37. oauth2User: adminAppContainer.state.oauth2User || '',
  38. });
  39. }, [
  40. adminAppContainer.state.fromAddress,
  41. adminAppContainer.state.transmissionMethod,
  42. adminAppContainer.state.smtpHost,
  43. adminAppContainer.state.smtpPort,
  44. adminAppContainer.state.smtpUser,
  45. adminAppContainer.state.smtpPassword,
  46. adminAppContainer.state.sesAccessKeyId,
  47. adminAppContainer.state.sesSecretAccessKey,
  48. adminAppContainer.state.oauth2ClientId,
  49. adminAppContainer.state.oauth2ClientSecret,
  50. adminAppContainer.state.oauth2RefreshToken,
  51. adminAppContainer.state.oauth2User,
  52. reset,
  53. ]);
  54. const onSubmit = useCallback(
  55. async (data) => {
  56. try {
  57. // Await all setState completions before API call
  58. await Promise.all([
  59. adminAppContainer.changeFromAddress(data.fromAddress),
  60. adminAppContainer.changeTransmissionMethod(data.transmissionMethod),
  61. adminAppContainer.changeSmtpHost(data.smtpHost),
  62. adminAppContainer.changeSmtpPort(data.smtpPort),
  63. adminAppContainer.changeSmtpUser(data.smtpUser),
  64. adminAppContainer.changeSmtpPassword(data.smtpPassword),
  65. adminAppContainer.changeSesAccessKeyId(data.sesAccessKeyId),
  66. adminAppContainer.changeSesSecretAccessKey(data.sesSecretAccessKey),
  67. adminAppContainer.changeOAuth2ClientId(data.oauth2ClientId),
  68. adminAppContainer.changeOAuth2ClientSecret(data.oauth2ClientSecret),
  69. adminAppContainer.changeOAuth2RefreshToken(data.oauth2RefreshToken),
  70. adminAppContainer.changeOAuth2User(data.oauth2User),
  71. ]);
  72. await adminAppContainer.updateMailSettingHandler();
  73. toastSuccess(
  74. t('toaster.update_successed', {
  75. target: t('admin:app_setting.mail_settings'),
  76. ns: 'commons',
  77. }),
  78. );
  79. } catch (err) {
  80. toastError(err);
  81. }
  82. },
  83. [adminAppContainer, t],
  84. );
  85. async function sendTestEmailHandler() {
  86. const { adminAppContainer } = props;
  87. try {
  88. await adminAppContainer.sendTestEmail();
  89. toastSuccess(t('admin:app_setting.success_to_send_test_email'));
  90. } catch (err) {
  91. toastError(err);
  92. }
  93. }
  94. return (
  95. <form onSubmit={handleSubmit(onSubmit)}>
  96. {!adminAppContainer.state.isMailerSetup && (
  97. <div className="alert alert-danger">
  98. <span className="material-symbols-outlined">error</span>{' '}
  99. {t('admin:app_setting.mailer_is_not_set_up')}
  100. </div>
  101. )}
  102. <div className="row mb-4">
  103. <label
  104. className="col-md-3 col-form-label text-end"
  105. htmlFor="admin-mail-setting-from-address"
  106. >
  107. {t('admin:app_setting.from_e-mail_address')}
  108. </label>
  109. <div className="col-md-6">
  110. <input
  111. className="form-control"
  112. type="text"
  113. placeholder={`${t('eg')} mail@growi.org`}
  114. id="admin-mail-setting-from-address"
  115. {...register('fromAddress')}
  116. />
  117. </div>
  118. </div>
  119. <div className="row mb-2">
  120. <span className="form-label text-start text-md-end col-md-3 col-form-label">
  121. {t('admin:app_setting.transmission_method')}
  122. </span>
  123. <div className="col-md-6 py-2">
  124. {transmissionMethods.map((method) => {
  125. return (
  126. <div key={method} className="form-check form-check-inline">
  127. <input
  128. type="radio"
  129. className="form-check-input"
  130. id={`transmission-method-radio-${method}`}
  131. value={method}
  132. {...register('transmissionMethod')}
  133. />
  134. <label
  135. className="form-label form-check-label"
  136. htmlFor={`transmission-method-radio-${method}`}
  137. >
  138. {t(`admin:app_setting.${method}_label`)}
  139. </label>
  140. </div>
  141. );
  142. })}
  143. </div>
  144. </div>
  145. {currentTransmissionMethod === 'smtp' && (
  146. <SmtpSetting register={register} />
  147. )}
  148. {currentTransmissionMethod === 'ses' && (
  149. <SesSetting register={register} />
  150. )}
  151. {currentTransmissionMethod === 'oauth2' && (
  152. <OAuth2Setting register={register} />
  153. )}
  154. <div className="row my-3">
  155. <div className="col-md-3"></div>
  156. <div className="col-md-9">
  157. <button
  158. type="submit"
  159. className="btn btn-primary"
  160. disabled={adminAppContainer.state.retrieveError != null}
  161. >
  162. {t('Update')}
  163. </button>
  164. {adminAppContainer.state.transmissionMethod === 'smtp' && (
  165. <button
  166. type="button"
  167. className="btn btn-secondary ms-4"
  168. onClick={sendTestEmailHandler}
  169. >
  170. {t('admin:app_setting.send_test_email')}
  171. </button>
  172. )}
  173. </div>
  174. </div>
  175. </form>
  176. );
  177. };
  178. /**
  179. * Wrapper component for using unstated
  180. */
  181. const MailSettingWrapper = withUnstatedContainers(MailSetting, [
  182. AdminAppContainer,
  183. ]);
  184. export default MailSettingWrapper;