| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240 |
- import type { FormEventHandler } from 'react';
- import { memo, useCallback, useState } from 'react';
- import { Lang, AllLang } from '@growi/core';
- import { LoadingSpinner } from '@growi/ui/dist/components';
- import { useTranslation } from 'next-i18next';
- import { useRouter } from 'next/router';
- import { i18n as i18nConfig } from '^/config/next-i18next.config';
- import { apiv3Post } from '~/client/util/apiv3-client';
- import { toastError } from '~/client/util/toastr';
- const InstallerForm = memo((): JSX.Element => {
- const { t, i18n } = useTranslation();
- const router = useRouter();
- const isSupportedLang = AllLang.includes(i18n.language as Lang);
- const [isValidUserName, setValidUserName] = useState(true);
- const [isLoading, setIsLoading] = useState(false);
- const [currentLocale, setCurrentLocale] = useState(isSupportedLang ? i18n.language : Lang.en_US);
- const checkUserName = useCallback(async(event) => {
- const axios = require('axios').create({
- headers: {
- 'Content-Type': 'application/json',
- 'X-Requested-With': 'XMLHttpRequest',
- },
- responseType: 'json',
- });
- const res = await axios.get('/_api/v3/check-username', { params: { username: event.target.value } });
- setValidUserName(res.data.valid);
- }, []);
- const onClickLanguageItem = useCallback((locale) => {
- i18n.changeLanguage(locale);
- setCurrentLocale(locale);
- }, [i18n]);
- const submitHandler: FormEventHandler = useCallback(async(e: any) => {
- e.preventDefault();
- setIsLoading(true);
- const formData = e.target.elements;
- const {
- 'registerForm[username]': { value: username },
- 'registerForm[name]': { value: name },
- 'registerForm[email]': { value: email },
- 'registerForm[password]': { value: password },
- } = formData;
- const data = {
- registerForm: {
- username,
- name,
- email,
- password,
- 'app:globalLang': currentLocale,
- },
- };
- try {
- await apiv3Post('/installer', data);
- router.push('/');
- }
- catch (errs) {
- const err = errs[0];
- const code = err.code;
- setIsLoading(false);
- if (code === 'failed_to_login_after_install') {
- toastError(t('installer.failed_to_login_after_install'));
- setTimeout(() => { router.push('/login') }, 700); // Wait 700 ms to show toastr
- }
- toastError(t('installer.failed_to_install'));
- }
- }, [currentLocale, router, t]);
- const hasErrorClass = isValidUserName ? '' : ' has-error';
- const unavailableUserId = isValidUserName
- ? ''
- : <span><span className="material-symbols-outlined">block</span>{ t('installer.unavaliable_user_id') }</span>;
- return (
- <div data-testid="installerForm" className={`nologin-dialog py-3 px-4 rounded-4 rounded-top-0 mx-auto${hasErrorClass}`}>
- <div className="row mt-3">
- <div className="col-md-12">
- <p className="alert alert-success">
- <strong>{ t('installer.create_initial_account') }</strong><br />
- <small>{ t('installer.initial_account_will_be_administrator_automatically') }</small>
- </p>
- </div>
- </div>
- <div className="row mt-2">
- <form role="form" id="register-form" className="ps-1" onSubmit={submitHandler}>
- <div className="dropdown mb-3">
- <div className="input-group dropdown-with-icon">
- <span className="p-2 text-white opacity-75">
- <span className="material-symbols-outlined">language</span>
- </span>
- <button
- type="button"
- className="btn btn-secondary dropdown-toggle form-control text-end rounded"
- id="dropdownLanguage"
- data-testid="dropdownLanguage"
- data-bs-toggle="dropdown"
- aria-haspopup="true"
- aria-expanded="true"
- >
- <span className="float-start">
- {t('meta.display_name')}
- </span>
- </button>
- <input
- type="hidden"
- name="registerForm[app:globalLang]"
- />
- <div className="dropdown-menu" aria-labelledby="dropdownLanguage">
- {
- i18nConfig.locales.map((locale) => {
- let fixedT;
- if (i18n != null) {
- fixedT = i18n.getFixedT(locale);
- i18n.loadLanguages(i18nConfig.locales);
- }
- return (
- <button
- key={locale}
- data-testid={`dropdownLanguageMenu-${locale}`}
- className="dropdown-item"
- type="button"
- onClick={() => { onClickLanguageItem(locale) }}
- >
- {fixedT?.('meta.display_name')}
- </button>
- );
- })
- }
- </div>
- </div>
- </div>
- <div className={`input-group mb-3${hasErrorClass}`}>
- <span className="p-2 text-white opacity-75">
- <span className="material-symbols-outlined">person</span>
- </span>
- <input
- data-testid="tiUsername"
- type="text"
- className="form-control rounded"
- placeholder={t('User ID')}
- name="registerForm[username]"
- // onBlur={checkUserName} // need not to check username before installation -- 2020.07.24 Yuki Takei
- required
- />
- </div>
- <p className="form-text">{ unavailableUserId }</p>
- <div className="input-group mb-3">
- <span className="p-2 text-white opacity-75">
- <span className="material-symbols-outlined">sell</span>
- </span>
- <input
- data-testid="tiName"
- type="text"
- className="form-control rounded"
- placeholder={t('Name')}
- name="registerForm[name]"
- required
- />
- </div>
- <div className="input-group mb-3">
- <span className="p-2 text-white opacity-75">
- <span className="material-symbols-outlined">mail</span>
- </span>
- <input
- data-testid="tiEmail"
- type="email"
- className="form-control rounded"
- placeholder={t('Email')}
- name="registerForm[email]"
- required
- />
- </div>
- <div className="input-group mb-3">
- <span className="p-2 text-white opacity-75">
- <span className="material-symbols-outlined">lock</span>
- </span>
- <input
- data-testid="tiPassword"
- type="password"
- className="form-control rounded"
- placeholder={t('Password')}
- name="registerForm[password]"
- required
- />
- </div>
- <div className="input-group mt-4 justify-content-center">
- <button
- data-testid="btnSubmit"
- type="submit"
- className="btn btn-secondary register-btn col-6 d-flex justify-content-between"
- disabled={isLoading}
- >
- <span>
- {isLoading ? (
- <LoadingSpinner />
- ) : (
- <span className="material-symbols-outlined">person_add</span>
- )}
- </span>
- <span className="flex-grow-1">{ t('Create') }</span>
- </button>
- </div>
- <div>
- <a href="https://growi.org" className="link-growi-org">
- <span className="growi">GROWI</span><span className="org">.org</span>
- </a>
- </div>
- </form>
- </div>
- </div>
- );
- });
- InstallerForm.displayName = 'InstallerForm';
- export default InstallerForm;
|