CompleteUserRegistrationForm.tsx 6.4 KB

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