InvitedForm.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. import React, { type JSX, useCallback, useState } from 'react';
  2. import { useRouter } from 'next/router';
  3. import { LoadingSpinner } from '@growi/ui/dist/components';
  4. import { useTranslation } from 'next-i18next';
  5. import { useForm } from 'react-hook-form';
  6. import { apiv3Post } from '~/client/util/apiv3-client';
  7. import { useCurrentUser } from '~/states/global';
  8. type InvitedFormProps = {
  9. invitedFormUsername: string;
  10. invitedFormName: string;
  11. };
  12. type InvitedFormValues = {
  13. name: string;
  14. username: string;
  15. password: string;
  16. };
  17. export const InvitedForm = (props: InvitedFormProps): JSX.Element => {
  18. const { t } = useTranslation();
  19. const router = useRouter();
  20. const user = useCurrentUser();
  21. const [loginErrors, setLoginErrors] = useState<Error[]>([]);
  22. const [isLoading, setIsLoading] = useState(false);
  23. const { invitedFormUsername, invitedFormName } = props;
  24. const {
  25. register,
  26. handleSubmit,
  27. formState: { isSubmitting },
  28. } = useForm<InvitedFormValues>({
  29. defaultValues: {
  30. name: invitedFormName,
  31. username: invitedFormUsername,
  32. },
  33. });
  34. const submitHandler = useCallback(
  35. async (values: InvitedFormValues) => {
  36. setIsLoading(true);
  37. const invitedForm = {
  38. name: values.name,
  39. username: values.username,
  40. password: values.password,
  41. };
  42. try {
  43. const res = await apiv3Post('/invited', { invitedForm });
  44. const { redirectTo } = res.data;
  45. router.push(redirectTo ?? '/');
  46. } catch (err) {
  47. setLoginErrors(err);
  48. setIsLoading(false);
  49. }
  50. },
  51. [router],
  52. );
  53. const formNotification = useCallback(() => {
  54. return (
  55. <>
  56. {loginErrors != null && loginErrors.length > 0 ? (
  57. <p className="alert alert-danger">
  58. {loginErrors.map((err) => {
  59. return (
  60. <span>
  61. {t(err.message)}
  62. <br />
  63. </span>
  64. );
  65. })}
  66. </p>
  67. ) : (
  68. <p className="alert alert-success">
  69. <strong>{t('invited.discription_heading')}</strong>
  70. <br></br>
  71. <small>{t('invited.discription')}</small>
  72. </p>
  73. )}
  74. </>
  75. );
  76. }, [loginErrors, t]);
  77. if (user == null) {
  78. return <></>;
  79. }
  80. return (
  81. <div className="nologin-dialog px-3 pb-3 mx-auto" id="nologin-dialog">
  82. {formNotification()}
  83. <form
  84. role="form"
  85. onSubmit={handleSubmit(submitHandler)}
  86. id="invited-form"
  87. >
  88. {/* Email Form */}
  89. <div className="input-group">
  90. <span className="input-group-text">
  91. <span className="material-symbols-outlined">mail</span>
  92. </span>
  93. <input
  94. type="text"
  95. className="form-control"
  96. disabled
  97. placeholder={t('Email')}
  98. name="invitedForm[email]"
  99. defaultValue={user.email}
  100. required
  101. />
  102. </div>
  103. {/* UserID Form */}
  104. <div className="input-group" id="input-group-username">
  105. <span className="input-group-text">
  106. <span className="material-symbols-outlined">person</span>
  107. </span>
  108. <input
  109. type="text"
  110. className="form-control"
  111. placeholder={t('User ID')}
  112. required
  113. {...register('username', { required: true })}
  114. />
  115. </div>
  116. {/* Name Form */}
  117. <div className="input-group">
  118. <span className="input-group-text">
  119. <span className="material-symbols-outlined">sell</span>
  120. </span>
  121. <input
  122. type="text"
  123. className="form-control"
  124. placeholder={t('Name')}
  125. required
  126. {...register('name', { required: true })}
  127. />
  128. </div>
  129. {/* Password Form */}
  130. <div className="input-group">
  131. <span className="input-group-text">
  132. <span className="material-symbols-outlined">lock</span>
  133. </span>
  134. <input
  135. type="password"
  136. className="form-control"
  137. placeholder={t('Password')}
  138. required
  139. minLength={6}
  140. {...register('password', { required: true, minLength: 6 })}
  141. />
  142. </div>
  143. {/* Create Button */}
  144. <div className="input-group justify-content-center d-flex mt-4">
  145. <button
  146. type="submit"
  147. className="btn btn-fill"
  148. id="register"
  149. disabled={isLoading || isSubmitting}
  150. >
  151. <span className="btn-label">
  152. {isLoading ? (
  153. <LoadingSpinner />
  154. ) : (
  155. <span className="material-symbols-outlined">person_add</span>
  156. )}
  157. </span>
  158. <span className="btn-label-text">{t('Create')}</span>
  159. </button>
  160. </div>
  161. </form>
  162. <div className="input-group mt-4 d-flex justify-content-center">
  163. <a href="https://growi.org" className="link-growi-org">
  164. <span className="growi">GROWI</span>
  165. <span className="org">.ORG</span>
  166. </a>
  167. </div>
  168. </div>
  169. );
  170. };