فهرست منبع

change to FC/TS

yohei0125 3 سال پیش
والد
کامیت
93505a843e
1فایلهای تغییر یافته به همراه176 افزوده شده و 137 حذف شده
  1. 176 137
      packages/app/src/components/LoginForm.tsx

+ 176 - 137
packages/app/src/components/LoginForm.jsx → packages/app/src/components/LoginForm.tsx

@@ -1,44 +1,63 @@
-import React from 'react';
+import React, {
+  useState, useEffect,
+} from 'react';
 
 import { useTranslation } from 'next-i18next';
-import PropTypes from 'prop-types';
+import { useRouter } from 'next/router';
 import ReactCardFlip from 'react-card-flip';
 
+import { apiv3Post } from '~/client/util/apiv3-client';
 import { useCsrfToken } from '~/stores/context';
 
-class LoginForm extends React.Component {
-
-  constructor(props) {
-    super(props);
-
-    this.state = {
-      isRegistering: false,
-    };
-
-    this.switchForm = this.switchForm.bind(this);
-    this.handleLoginWithExternalAuth = this.handleLoginWithExternalAuth.bind(this);
-    this.renderLocalOrLdapLoginForm = this.renderLocalOrLdapLoginForm.bind(this);
-    this.renderExternalAuthLoginForm = this.renderExternalAuthLoginForm.bind(this);
-    this.renderExternalAuthInput = this.renderExternalAuthInput.bind(this);
-    this.renderRegisterForm = this.renderRegisterForm.bind(this);
+export type LoginFormProps = {
+  username?: string,
+  name?: string,
+  email?: string,
+  isRegistrationEnabled: boolean,
+  isEmailAuthenticationEnabled: boolean,
+  registrationMode?: string,
+  registrationWhiteList: string[],
+  isPasswordResetEnabled: boolean,
+  isLocalStrategySetup: boolean,
+  isLdapStrategySetup: boolean,
+  objOfIsExternalAuthEnableds?: any,
+  isMailerSetup?: boolean
+}
+export const LoginForm = (props: LoginFormProps): JSX.Element => {
+  const { t } = useTranslation();
+  const router = useRouter();
+  const { data: csrfToken } = useCsrfToken();
 
+  const {
+    isLocalStrategySetup, isLdapStrategySetup, isPasswordResetEnabled, isRegistrationEnabled,
+  } = props;
+  const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
+  // const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
+  const isSomeExternalAuthEnabled = true;
+
+  // states
+  const [isRegistering, setIsRegistering] = useState(false);
+  const [username, setUsername] = useState('');
+  const [name, setName] = useState('');
+  const [email, setEmail] = useState('');
+  const [password, setPassword] = useState('');
+  const [registerErrors, setRegisterErrors] = useState<Error[]>([]);
+
+  useEffect(() => {
     const { hash } = window.location;
     if (hash === '#register') {
-      this.state.isRegistering = true;
+      setIsRegistering(true);
     }
-  }
+  }, []);
 
-  switchForm() {
-    this.setState({ isRegistering: !this.state.isRegistering });
-  }
-
-  handleLoginWithExternalAuth(e) {
+  // functions
+  const handleLoginWithExternalAuth = (e) => {
     const auth = e.currentTarget.id;
-    window.location.href = `/passport/${auth}`;
-  }
 
-  renderLocalOrLdapLoginForm() {
-    const { t, csrfToken, isLdapStrategySetup } = this.props;
+    window.location.href = `/passport/${auth}`;
+  };
+  const renderLocalOrLdapLoginForm = () => {
+    const { isLdapStrategySetup } = props;
 
     return (
       <form role="form" action="/login" method="post">
@@ -79,10 +98,8 @@ class LoginForm extends React.Component {
         </div>
       </form>
     );
-  }
-
-  renderExternalAuthInput(auth) {
-    const { t } = this.props;
+  };
+  const renderExternalAuthInput = (auth) => {
     const authIconNames = {
       google: 'google',
       github: 'github',
@@ -95,7 +112,7 @@ class LoginForm extends React.Component {
 
     return (
       <div key={auth} className="col-6 my-2">
-        <button type="button" className="btn btn-fill rounded-0" id={auth} onClick={this.handleLoginWithExternalAuth}>
+        <button type="button" className="btn btn-fill rounded-0" id={auth} onClick={handleLoginWithExternalAuth}>
           <div className="eff"></div>
           <span className="btn-label">
             <i className={`fa fa-${authIconNames[auth]}`}></i>
@@ -105,10 +122,9 @@ class LoginForm extends React.Component {
         <div className="small text-right">by {auth} Account</div>
       </div>
     );
-  }
-
-  renderExternalAuthLoginForm() {
-    const { isLocalStrategySetup, isLdapStrategySetup, objOfIsExternalAuthEnableds } = this.props;
+  };
+  const renderExternalAuthLoginForm = () => {
+    const { isLocalStrategySetup, isLdapStrategySetup, objOfIsExternalAuthEnableds } = props;
     const isExternalAuthCollapsible = isLocalStrategySetup || isLdapStrategySetup;
     const collapsibleClass = isExternalAuthCollapsible ? 'collapse collapse-external-auth' : '';
 
@@ -121,7 +137,7 @@ class LoginForm extends React.Component {
                 if (!objOfIsExternalAuthEnableds[auth]) {
                   return;
                 }
-                return this.renderExternalAuthInput(auth);
+                return renderExternalAuthInput(auth);
               })}
             </div>
           </div>
@@ -140,13 +156,44 @@ class LoginForm extends React.Component {
         </div>
       </>
     );
-  }
+  };
+
+  const handleRegisterFormSubmit = async(e, requestPath) => {
+    e.preventDefault();
 
-  renderRegisterForm() {
+    const registerForm = {
+      username,
+      name,
+      email,
+      password,
+      token: csrfToken,
+    };
+    try {
+      const res = await apiv3Post(requestPath, { registerForm });
+      const { redirectTo } = res.data;
+      router.push(redirectTo);
+    }
+    catch (err) {
+      // Execute if error exists
+      if (err != null || err.length > 0) {
+        setRegisterErrors(err);
+      }
+    }
+    return;
+  };
+
+  const resetRegisterErrors = () => {
+    if (registerErrors.length === 0) return;
+    setRegisterErrors([]);
+  };
+
+  const switchForm = () => {
+    setIsRegistering(!isRegistering);
+    resetRegisterErrors();
+  };
+
+  const renderRegisterForm = () => {
     const {
-      t,
-      // appContainer,
-      csrfToken,
       isEmailAuthenticationEnabled,
       username,
       name,
@@ -154,7 +201,7 @@ class LoginForm extends React.Component {
       registrationMode,
       registrationWhiteList,
       isMailerSetup,
-    } = this.props;
+    } = props;
 
     let registerAction = '/register';
 
@@ -179,7 +226,21 @@ class LoginForm extends React.Component {
           </p>
         )}
 
-        <form role="form" action={registerAction} method="post" id="register-form">
+        {
+          registerErrors != null && registerErrors.length > 0 && (
+            <p className="alert alert-danger">
+              {registerErrors.map((err, index) => {
+                return (
+                  <span key={index}>
+                    {t(`message.${err.message}`)}<br/>
+                  </span>
+                );
+              })}
+            </p>
+          )
+        }
+
+        <form role="form" onSubmit={e => handleRegisterFormSubmit(e, registerAction) } id="register-form">
 
           {!isEmailAuthenticationEnabled && (
             <div>
@@ -189,11 +250,13 @@ class LoginForm extends React.Component {
                     <i className="icon-user"></i>
                   </span>
                 </div>
+                {/* username */}
                 <input
                   type="text"
                   className="form-control rounded-0"
+                  onChange={(e) => { setUsername(e.target.value) }}
                   placeholder={t('User ID')}
-                  name="registerForm[username]"
+                  name="username"
                   defaultValue={username}
                   required
                 />
@@ -207,7 +270,14 @@ class LoginForm extends React.Component {
                     <i className="icon-tag"></i>
                   </span>
                 </div>
-                <input type="text" className="form-control rounded-0" placeholder={t('Name')} name="registerForm[name]" defaultValue={name} required />
+                {/* name */}
+                <input type="text"
+                  className="form-control rounded-0"
+                  onChange={(e) => { setName(e.target.value) }}
+                  placeholder={t('Name')}
+                  name="name"
+                  defaultValue={name}
+                  required />
               </div>
             </div>
           )}
@@ -218,7 +288,15 @@ class LoginForm extends React.Component {
                 <i className="icon-envelope"></i>
               </span>
             </div>
-            <input type="email" className="form-control rounded-0" placeholder={t('Email')} name="registerForm[email]" defaultValue={email} required />
+            {/* email */}
+            <input type="email"
+              className="form-control rounded-0"
+              onChange={(e) => { setEmail(e.target.value) }}
+              placeholder={t('Email')}
+              name="email"
+              defaultValue={email}
+              required
+            />
           </div>
 
           {registrationWhiteList.length > 0 && (
@@ -244,14 +322,25 @@ class LoginForm extends React.Component {
                     <i className="icon-lock"></i>
                   </span>
                 </div>
-                <input type="password" className="form-control rounded-0" placeholder={t('Password')} name="registerForm[password]" required />
+                {/* Password */}
+                <input type="password"
+                  className="form-control rounded-0"
+                  onChange={(e) => { setPassword(e.target.value) }}
+                  placeholder={t('Password')}
+                  name="password"
+                  required />
               </div>
             </div>
           )}
 
+          {/* Sign up button (submit) */}
           <div className="input-group justify-content-center my-4">
             <input type="hidden" name="_csrf" value={csrfToken} />
-            <button type="submit" className="btn btn-fill rounded-0" id="register" disabled={(!isMailerSetup && isEmailAuthenticationEnabled)}>
+            <button
+              className="btn btn-fill rounded-0"
+              id="register"
+              disabled={(!isMailerSetup && isEmailAuthenticationEnabled)}
+            >
               <div className="eff"></div>
               <span className="btn-label">
                 <i className="icon-user-follow"></i>
@@ -265,7 +354,7 @@ class LoginForm extends React.Component {
 
         <div className="row">
           <div className="text-right col-12 mt-2 py-2">
-            <a href="#login" id="login" className="link-switch" onClick={this.switchForm}>
+            <a href="#login" id="login" className="link-switch" onClick={switchForm}>
               <i className="icon-fw icon-login"></i>
               {t('Sign in is here')}
             </a>
@@ -273,93 +362,43 @@ class LoginForm extends React.Component {
         </div>
       </React.Fragment>
     );
-  }
-
-  render() {
-    const {
-      t,
-      isLocalStrategySetup,
-      isLdapStrategySetup,
-      isRegistrationEnabled,
-      isPasswordResetEnabled,
-      objOfIsExternalAuthEnableds,
-    } = this.props;
-
-
-    const isLocalOrLdapStrategiesEnabled = isLocalStrategySetup || isLdapStrategySetup;
-    // const isSomeExternalAuthEnabled = Object.values(objOfIsExternalAuthEnableds).some(elem => elem);
-    const isSomeExternalAuthEnabled = true;
-
-    return (
-      <div className="noLogin-dialog mx-auto" id="noLogin-dialog">
-        <div className="row mx-0">
-          <div className="col-12">
-            <ReactCardFlip isFlipped={this.state.isRegistering} flipDirection="horizontal" cardZIndex="3">
-              <div className="front">
-                {isLocalOrLdapStrategiesEnabled && this.renderLocalOrLdapLoginForm()}
-                {isSomeExternalAuthEnabled && this.renderExternalAuthLoginForm()}
-                {isLocalOrLdapStrategiesEnabled && isPasswordResetEnabled && (
-                  <div className="text-right mb-2">
-                    <a href="/forgot-password" className="d-block link-switch">
-                      <i className="icon-key"></i> {t('forgot_password.forgot_password')}
-                    </a>
-                  </div>
-                )}
-                {isRegistrationEnabled && (
-                  <div className="text-right mb-2">
-                    <a href="#register" id="register" className="link-switch" onClick={this.switchForm}>
-                      <i className="ti ti-check-box"></i> {t('Sign up is here')}
-                    </a>
-                  </div>
-                )}
-              </div>
-              <div className="back">
-                {isRegistrationEnabled && this.renderRegisterForm()}
-              </div>
-            </ReactCardFlip>
-          </div>
+  };
+
+  return (
+    <div className="noLogin-dialog mx-auto" id="noLogin-dialog">
+      <div className="row mx-0">
+        <div className="col-12">
+          <ReactCardFlip isFlipped={isRegistering} flipDirection="horizontal" cardZIndex="3">
+            <div className="front">
+              {isLocalOrLdapStrategiesEnabled && renderLocalOrLdapLoginForm()}
+              {isSomeExternalAuthEnabled && renderExternalAuthLoginForm()}
+              {isLocalOrLdapStrategiesEnabled && isPasswordResetEnabled && (
+                <div className="text-right mb-2">
+                  <a href="/forgot-password" className="d-block link-switch">
+                    <i className="icon-key"></i> {t('forgot_password.forgot_password')}
+                  </a>
+                </div>
+              )}
+              {/* Sign up link */}
+              {isRegistrationEnabled && (
+                <div className="text-right mb-2">
+                  <a href="#register" id="register" className="link-switch" onClick={switchForm}>
+                    <i className="ti ti-check-box"></i> {t('Sign up is here')}
+                  </a>
+                </div>
+              )}
+            </div>
+            <div className="back">
+              {/* Register form for /login#register */}
+              {isRegistrationEnabled && renderRegisterForm()}
+            </div>
+          </ReactCardFlip>
         </div>
-        <a href="https://growi.org" className="link-growi-org pl-3">
-          <span className="growi">GROWI</span>.<span className="org">ORG</span>
-        </a>
       </div>
-    );
-  }
-
-}
-
-LoginForm.propTypes = {
-  // i18next
-  t: PropTypes.func.isRequired,
-  // appContainer: PropTypes.instanceOf(AppContainer).isRequired,
-
-  csrfToken: PropTypes.string,
-  isRegistering: PropTypes.bool,
-  username: PropTypes.string,
-  name: PropTypes.string,
-  email: PropTypes.string,
-  isRegistrationEnabled: PropTypes.bool,
-  registrationMode: PropTypes.string,
-  registrationWhiteList: PropTypes.array,
-  isPasswordResetEnabled: PropTypes.bool,
-  isEmailAuthenticationEnabled: PropTypes.bool,
-  isLocalStrategySetup: PropTypes.bool,
-  isLdapStrategySetup: PropTypes.bool,
-  objOfIsExternalAuthEnableds: PropTypes.object,
-  isMailerSetup: PropTypes.bool,
-};
-
-const LoginFormWrapperFC = (props) => {
-  const { t } = useTranslation();
-  const { data: csrfToken } = useCsrfToken();
+      <a href="https://growi.org" className="link-growi-org pl-3">
+        <span className="growi">GROWI</span>.<span className="org">ORG</span>
+      </a>
+    </div>
+  );
 
-  return <LoginForm t={t} csrfToken={csrfToken} {...props} />;
 };
-
-/**
- * Wrapper component for using unstated
- */
-// const LoginFormWrapper = withUnstatedContainers(LoginFormWrapperFC, [AppContainer]);
-
-// export default LoginForm;
-export default LoginFormWrapperFC;