CompleteUserRegistrationForm.tsx 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  1. import React, { useState, useEffect, useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { useRouter } from 'next/router';
  4. import { apiv3Get, apiv3Post } from '~/client/util/apiv3-client';
  5. import { UserActivationErrorCode } from '~/interfaces/errors/user-activation';
  6. import { RegistrationMode } from '~/interfaces/registration-mode';
  7. import { toastError } from '../client/util/toastr';
  8. import { CompleteUserRegistration } from './CompleteUserRegistration';
  9. import styles from './CompleteUserRegistrationForm.module.scss';
  10. const moduleClass = styles['complete-user-registration-form'] ?? '';
  11. interface Props {
  12. email: string,
  13. token: string,
  14. errorCode?: UserActivationErrorCode,
  15. registrationMode: RegistrationMode,
  16. isEmailAuthenticationEnabled: boolean,
  17. }
  18. const CompleteUserRegistrationForm: React.FC<Props> = (props: Props) => {
  19. const { t } = useTranslation();
  20. const {
  21. email,
  22. token,
  23. errorCode,
  24. registrationMode,
  25. isEmailAuthenticationEnabled,
  26. } = props;
  27. const forceDisableForm = errorCode != null || !isEmailAuthenticationEnabled;
  28. const [usernameAvailable, setUsernameAvailable] = useState(true);
  29. const [username, setUsername] = useState('');
  30. const [name, setName] = useState('');
  31. const [password, setPassword] = useState('');
  32. const [disableForm, setDisableForm] = useState(forceDisableForm);
  33. const [isSuccessToRagistration, setIsSuccessToRagistration] = useState(false);
  34. const router = useRouter();
  35. useEffect(() => {
  36. const delayDebounceFn = setTimeout(async() => {
  37. try {
  38. const { data } = await apiv3Get('/check-username', { username });
  39. if (data.ok) {
  40. setUsernameAvailable(data.valid);
  41. }
  42. }
  43. catch (error) {
  44. toastError(error);
  45. }
  46. }, 500);
  47. return () => clearTimeout(delayDebounceFn);
  48. }, [username]);
  49. const handleSubmitRegistration = useCallback(async(e) => {
  50. e.preventDefault();
  51. setDisableForm(true);
  52. try {
  53. const res = await apiv3Post('/complete-registration', {
  54. username, name, password, token,
  55. });
  56. setIsSuccessToRagistration(true);
  57. const { redirectTo } = res.data;
  58. if (redirectTo != null) {
  59. router.push(redirectTo);
  60. }
  61. }
  62. catch (err) {
  63. toastError(err);
  64. setDisableForm(false);
  65. setIsSuccessToRagistration(false);
  66. }
  67. }, [username, name, password, token, router]);
  68. if (isSuccessToRagistration && registrationMode === RegistrationMode.RESTRICTED) {
  69. return <CompleteUserRegistration />;
  70. }
  71. return (
  72. <>
  73. <div className={`${moduleClass} nologin-dialog mx-auto rounded-4 rounded-top-0`} id="nologin-dialog">
  74. <div className="row mx-0">
  75. <div className="col-12 px-4">
  76. { (errorCode != null && errorCode === UserActivationErrorCode.TOKEN_NOT_FOUND) && (
  77. <p className="alert alert-danger">
  78. <span>Token not found</span>
  79. </p>
  80. )}
  81. { (errorCode != null && errorCode === UserActivationErrorCode.USER_REGISTRATION_ORDER_IS_NOT_APPROPRIATE) && (
  82. <p className="alert alert-danger">
  83. <span>{t('message.incorrect_token_or_expired_url')}</span>
  84. </p>
  85. )}
  86. { !isEmailAuthenticationEnabled && (
  87. <p className="alert alert-danger">
  88. <span>{t('message.email_authentication_is_not_enabled')}</span>
  89. </p>
  90. )}
  91. <form role="form" onSubmit={handleSubmitRegistration} id="registration-form">
  92. <input type="hidden" name="token" value={token} />
  93. <div className="input-group">
  94. <span className="p-2 text-white opacity-75">
  95. <span className="material-symbols-outlined">mail</span>
  96. </span>
  97. <input type="text" className="form-control rounded" placeholder={t('Email')} disabled value={email} />
  98. </div>
  99. <div className="input-group" id="input-group-username">
  100. <span className="p-2 text-white opacity-75">
  101. <span className="material-symbols-outlined">person</span>
  102. </span>
  103. <input
  104. type="text"
  105. className="form-control rounded"
  106. placeholder={t('User ID')}
  107. name="username"
  108. onChange={e => setUsername(e.target.value)}
  109. required
  110. disabled={forceDisableForm || disableForm}
  111. />
  112. </div>
  113. {!usernameAvailable && (
  114. <p className="form-text text-red">
  115. <span id="help-block-username">
  116. <span className="p-2 text-white opacity-75">
  117. <span className="material-symbols-outlined">block</span>
  118. </span>
  119. {t('installer.unavaliable_user_id')}
  120. </span>
  121. </p>
  122. )}
  123. <div className="input-group">
  124. <span className="p-2 text-white opacity-75">
  125. <span className="material-symbols-outlined">sell</span>
  126. </span>
  127. <input
  128. type="text"
  129. className="form-control rounded"
  130. placeholder={t('Name')}
  131. name="name"
  132. value={name}
  133. onChange={e => setName(e.target.value)}
  134. required
  135. disabled={forceDisableForm || disableForm}
  136. />
  137. </div>
  138. <div className="input-group">
  139. <span className="p-2 text-white opacity-75">
  140. <span className="material-symbols-outlined">lock</span>
  141. </span>
  142. <input
  143. type="password"
  144. className="form-control rounded"
  145. placeholder={t('Password')}
  146. name="password"
  147. value={password}
  148. onChange={e => setPassword(e.target.value)}
  149. required
  150. disabled={forceDisableForm || disableForm}
  151. />
  152. </div>
  153. <div className="input-group justify-content-center mt-4">
  154. <button
  155. type="button"
  156. disabled={forceDisableForm || disableForm}
  157. className="btn btn-secondary register-btn col-6 mx-auto d-flex justify-content-between"
  158. >
  159. <span>
  160. <span className="material-symbols-outlined">person_add</span>
  161. </span>
  162. <span className="flex-grow-1">{t('Create')}</span>
  163. </button>
  164. </div>
  165. <div className="input-group mt-5 d-flex">
  166. <a href="https://growi.org" className="link-growi-org">
  167. <span className="growi">GROWI</span><span className="org">.org</span>
  168. </a>
  169. </div>
  170. </form>
  171. </div>
  172. </div>
  173. </div>
  174. </>
  175. );
  176. };
  177. export default CompleteUserRegistrationForm;