CompleteUserRegistrationForm.tsx 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import React, { useState, useEffect, useCallback } from 'react';
  2. import { useTranslation } from 'next-i18next';
  3. import { apiv3Get, apiv3Post } from '~/client/util/apiv3-client';
  4. import { UserActivationErrorCode } from '~/interfaces/errors/user-activation';
  5. import { toastSuccess, toastError } from '../client/util/apiNotification';
  6. interface Props {
  7. email: string,
  8. token: string,
  9. errorCode?: UserActivationErrorCode,
  10. isEmailAuthenticationEnabled: boolean,
  11. }
  12. const CompleteUserRegistrationForm: React.FC<Props> = (props: Props) => {
  13. const { t } = useTranslation();
  14. const {
  15. email,
  16. token,
  17. errorCode,
  18. isEmailAuthenticationEnabled,
  19. } = props;
  20. const forceDisableForm = errorCode != null || !isEmailAuthenticationEnabled;
  21. const [usernameAvailable, setUsernameAvailable] = useState(true);
  22. const [username, setUsername] = useState('');
  23. const [name, setName] = useState('');
  24. const [password, setPassword] = useState('');
  25. const [disableForm, setDisableForm] = useState(forceDisableForm);
  26. useEffect(() => {
  27. const delayDebounceFn = setTimeout(async() => {
  28. try {
  29. const { data } = await apiv3Get('/check-username', { username });
  30. if (data.ok) {
  31. setUsernameAvailable(data.valid);
  32. }
  33. }
  34. catch (error) {
  35. toastError(error, 'Error occurred when checking username');
  36. }
  37. }, 500);
  38. return () => clearTimeout(delayDebounceFn);
  39. }, [username]);
  40. const handleSubmitRegistration = useCallback(async(e) => {
  41. e.preventDefault();
  42. setDisableForm(true);
  43. try {
  44. await apiv3Post('/complete-registration', {
  45. username, name, password, token,
  46. });
  47. toastSuccess('Registration succeed');
  48. window.location.href = '/login';
  49. }
  50. catch (err) {
  51. toastError(err, 'Registration failed');
  52. setDisableForm(false);
  53. }
  54. }, [name, password, token, username]);
  55. return (
  56. <>
  57. <div className="noLogin-dialog mx-auto" id="noLogin-dialog">
  58. <div className="row mx-0">
  59. <div className="col-12">
  60. { (errorCode != null && errorCode === UserActivationErrorCode.TOKEN_NOT_FOUND) && (
  61. <p className="alert alert-danger">
  62. <span>Token not found</span>
  63. </p>
  64. )}
  65. { (errorCode != null && errorCode === UserActivationErrorCode.USER_REGISTRATION_ORDER_IS_NOT_APPROPRIATE) && (
  66. <p className="alert alert-danger">
  67. <span>{t('message.incorrect_token_or_expired_url')}</span>
  68. </p>
  69. )}
  70. { !isEmailAuthenticationEnabled && (
  71. <p className="alert alert-danger">
  72. <span>{t('message.email_authentication_is_not_enabled')}</span>
  73. </p>
  74. )}
  75. <form role="form" onSubmit={handleSubmitRegistration} id="registration-form">
  76. <input type="hidden" name="token" value={token} />
  77. <div className="input-group">
  78. <div className="input-group-prepend">
  79. <span className="input-group-text"><i className="icon-envelope"></i></span>
  80. </div>
  81. <input type="text" className="form-control" disabled value={email} />
  82. </div>
  83. <div className="input-group" id="input-group-username">
  84. <div className="input-group-prepend">
  85. <span className="input-group-text"><i className="icon-user"></i></span>
  86. </div>
  87. <input
  88. type="text"
  89. className="form-control"
  90. placeholder={t('User ID')}
  91. name="username"
  92. onChange={e => setUsername(e.target.value)}
  93. required
  94. disabled={forceDisableForm || disableForm}
  95. />
  96. </div>
  97. {!usernameAvailable && (
  98. <p className="form-text text-red">
  99. <span id="help-block-username"><i className="icon-fw icon-ban"></i>{t('installer.unavaliable_user_id')}</span>
  100. </p>
  101. )}
  102. <div className="input-group">
  103. <div className="input-group-prepend">
  104. <span className="input-group-text"><i className="icon-tag"></i></span>
  105. </div>
  106. <input
  107. type="text"
  108. className="form-control"
  109. placeholder={t('Name')}
  110. name="name"
  111. value={name}
  112. onChange={e => setName(e.target.value)}
  113. required
  114. disabled={forceDisableForm || disableForm}
  115. />
  116. </div>
  117. <div className="input-group">
  118. <div className="input-group-prepend">
  119. <span className="input-group-text"><i className="icon-lock"></i></span>
  120. </div>
  121. <input
  122. type="password"
  123. className="form-control"
  124. placeholder={t('Password')}
  125. name="password"
  126. value={password}
  127. onChange={e => setPassword(e.target.value)}
  128. required
  129. disabled={forceDisableForm || disableForm}
  130. />
  131. </div>
  132. <div className="input-group justify-content-center d-flex mt-5">
  133. <button disabled={forceDisableForm || disableForm} className="btn btn-fill" id="register">
  134. <div className="eff"></div>
  135. <span className="btn-label"><i className="icon-user-follow"></i></span>
  136. <span className="btn-label-text">{t('Create')}</span>
  137. </button>
  138. </div>
  139. <div className="input-group mt-5 d-flex justify-content-center">
  140. <a href="https://growi.org" className="link-growi-org">
  141. <span className="growi">GROWI</span>.<span className="org">ORG</span>
  142. </a>
  143. </div>
  144. </form>
  145. </div>
  146. </div>
  147. </div>
  148. </>
  149. );
  150. };
  151. export default CompleteUserRegistrationForm;