InvitedForm.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178
  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 key={err.message}>
  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 onSubmit={handleSubmit(submitHandler)} id="invited-form">
  84. {/* Email Form */}
  85. <div className="input-group">
  86. <span className="input-group-text">
  87. <span className="material-symbols-outlined">mail</span>
  88. </span>
  89. <input
  90. type="text"
  91. className="form-control"
  92. disabled
  93. placeholder={t('Email')}
  94. name="invitedForm[email]"
  95. defaultValue={user.email}
  96. required
  97. />
  98. </div>
  99. {/* UserID Form */}
  100. <div className="input-group" id="input-group-username">
  101. <span className="input-group-text">
  102. <span className="material-symbols-outlined">person</span>
  103. </span>
  104. <input
  105. type="text"
  106. className="form-control"
  107. placeholder={t('User ID')}
  108. required
  109. {...register('username', { required: true })}
  110. />
  111. </div>
  112. {/* Name Form */}
  113. <div className="input-group">
  114. <span className="input-group-text">
  115. <span className="material-symbols-outlined">sell</span>
  116. </span>
  117. <input
  118. type="text"
  119. className="form-control"
  120. placeholder={t('Name')}
  121. required
  122. {...register('name', { required: true })}
  123. />
  124. </div>
  125. {/* Password Form */}
  126. <div className="input-group">
  127. <span className="input-group-text">
  128. <span className="material-symbols-outlined">lock</span>
  129. </span>
  130. <input
  131. type="password"
  132. className="form-control"
  133. placeholder={t('Password')}
  134. required
  135. minLength={6}
  136. {...register('password', { required: true, minLength: 6 })}
  137. />
  138. </div>
  139. {/* Create Button */}
  140. <div className="input-group justify-content-center d-flex mt-4">
  141. <button
  142. type="submit"
  143. className="btn btn-fill"
  144. id="register"
  145. disabled={isLoading || isSubmitting}
  146. >
  147. <span className="btn-label">
  148. {isLoading ? (
  149. <LoadingSpinner />
  150. ) : (
  151. <span className="material-symbols-outlined">person_add</span>
  152. )}
  153. </span>
  154. <span className="btn-label-text">{t('Create')}</span>
  155. </button>
  156. </div>
  157. </form>
  158. <div className="input-group mt-4 d-flex justify-content-center">
  159. <a href="https://growi.org" className="link-growi-org">
  160. <span className="growi">GROWI</span>
  161. <span className="org">.ORG</span>
  162. </a>
  163. </div>
  164. </div>
  165. );
  166. };