react-icons/md#MdDone TypeScript Examples

The following examples show how to use react-icons/md#MdDone. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: index.tsx    From slice-machine with Apache License 2.0 6 votes vote down vote up
getIconAccordingToasterType = ({
  type,
}: {
  type: TypeOptions;
}): JSX.Element | undefined => {
  switch (type) {
    case toast.TYPE.INFO:
      return <MdError />;
    case toast.TYPE.ERROR:
      return <MdDangerous />;
    case toast.TYPE.SUCCESS:
      return <MdDone />;
    case toast.TYPE.WARNING:
      return <MdWarning />;
  }
}
Example #2
Source File: UserInvitation.tsx    From hub with Apache License 2.0 5 votes vote down vote up
UserInvitation = (props: Props) => {
  const [orgToConfirm] = useState(props.orgToConfirm);
  const [isAccepting, setIsAccepting] = useState(false);
  const [validInvitation, setValidInvitation] = useState<boolean | null>(null);
  const [apiError, setApiError] = useState<string | null>(null);
  const history = useHistory();

  useEffect(() => {
    async function confirmOrganizationMembership() {
      setIsAccepting(true);
      try {
        await API.confirmOrganizationMembership(orgToConfirm!);
        setValidInvitation(true);
      } catch (err: any) {
        let error = !isUndefined(err.message)
          ? err.message
          : 'An error occurred accepting your invitation, please contact us about this issue.';
        if (err.kind === ErrorKind.Unauthorized) {
          error =
            'Please sign in to accept the invitation to join the organization. You can accept it from the Control Panel, in the organizations tab, or from the link you received in the invitation email.';
        }
        setApiError(error);
        setValidInvitation(false);
      } finally {
        setIsAccepting(false);
      }
    }

    if (!isUndefined(orgToConfirm)) {
      history.replace({
        pathname: '/',
        search: '',
      });
      confirmOrganizationMembership();
    }
  }, [orgToConfirm, history]);

  if (isUndefined(orgToConfirm)) return null;

  return (
    <Modal
      data-testid="userInvitationModal"
      header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Membership confirmation</div>}
      disabledClose={isAccepting}
      open={!isUndefined(orgToConfirm)}
    >
      <div
        className={`d-flex flex-column h-100 w-100 px-3 align-items-center justify-content-center text-center position-relative ${styles.content}`}
      >
        {isAccepting ? (
          <>
            <Loading className="position-relative" spinnerClassName="mt-0" />
            <small className="text-muted">Your are accepting the invitation...</small>
          </>
        ) : (
          <>
            {validInvitation ? (
              <>
                <MdDone className="display-4 text-success mb-4" />
                You have accepted the invitation to join the organization.
              </>
            ) : (
              <>
                <MdClose className="display-4 text-danger mb-4" />
                {apiError}
              </>
            )}
          </>
        )}
      </div>
    </Modal>
  );
}
Example #3
Source File: UserConfirmation.tsx    From hub with Apache License 2.0 5 votes vote down vote up
UserConfirmation = (props: Props) => {
  const [emailCode] = useState(props.emailCode);
  const [verifying, setVerifying] = useState(false);
  const [validEmail, setValidEmail] = useState<boolean | null>(null);
  const [apiError, setApiError] = useState<string | null>(null);
  const history = useHistory();
  const siteName = getMetaTag('siteName');

  useEffect(() => {
    async function fetchEmailConfirmation() {
      setVerifying(true);
      try {
        await API.verifyEmail(emailCode!);
        setValidEmail(true);
      } catch (err: any) {
        let error = 'An error occurred verifying your email, please contact us about this issue.';
        if (!isUndefined(err.message)) {
          error = `Sorry, ${err.message}`;
        }
        setApiError(error);
        setValidEmail(false);
      } finally {
        setVerifying(false);
      }
    }

    if (!isUndefined(emailCode)) {
      history.replace({
        pathname: '/',
        search: '',
      });
      fetchEmailConfirmation();
    }
  }, [emailCode, history]);

  if (isUndefined(emailCode)) return null;

  return (
    <Modal
      data-testid="userConfirmationModal"
      header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Email confirmation</div>}
      disabledClose={verifying}
      modalClassName={styles.modal}
      open={!isUndefined(emailCode)}
    >
      <div
        className={`d-flex flex-column h-100 w-100 px-3 align-items-center justify-content-center text-center position-relative ${styles.content}`}
      >
        {verifying ? (
          <>
            <Loading className="position-relative" spinnerClassName="mt-0" />
            <small className="text-muted">We are verifying your email...</small>
          </>
        ) : (
          <>
            {validEmail ? (
              <>
                <MdDone className="display-4 text-success mb-4" />
                You email has been verified! Please, login to {siteName}.
              </>
            ) : (
              <>
                <MdClose className="display-4 text-danger mb-4" />
                {apiError}
              </>
            )}
          </>
        )}
      </div>
    </Modal>
  );
}
Example #4
Source File: EnableModal.tsx    From hub with Apache License 2.0 4 votes vote down vote up
EnableTwoFactorAuthenticationModal = (props: Props) => {
  const [openStatus, setOpenStatus] = useState<boolean>(false);
  const [isLoading, setIsLoading] = useState<boolean>(false);
  const [isProcessing, setIsProcessing] = useState<boolean>(false);
  const [setUp, setSetUp] = useState<TwoFactorAuth | null | undefined>();
  const [activeStep, setActiveStep] = useState<number>(1);
  const [passcode, setPasscode] = useState<string>('');
  const [apiError, setApiError] = useState<null | string>(null);

  const onPasscodeChange = (e: ChangeEvent<HTMLInputElement>) => {
    setPasscode(e.target.value);
    if (!isNull(apiError)) {
      setApiError(null);
    }
  };

  const onClose = () => {
    setOpenStatus(false);
    if (activeStep === 3) {
      props.onChange();
    }
    setSetUp(undefined);
    setApiError(null);
    setPasscode('');
    setActiveStep(1);
  };

  async function setUpTFA() {
    try {
      setIsLoading(true);
      setSetUp(await API.setUpTFA());
      setOpenStatus(true);
      setIsLoading(false);
    } catch (err: any) {
      setIsLoading(false);
      setSetUp(null);
      if (err.kind !== ErrorKind.Unauthorized) {
        let error = compoundErrorMessage(err, 'An error occurred turning on two-factor authentication');
        alertDispatcher.postAlert({
          type: 'danger',
          message: error,
        });
      } else {
        props.onAuthError();
      }
    }
  }

  async function enableTFA() {
    try {
      setIsProcessing(true);
      await API.enableTFA(passcode);
      setApiError(null);
      setActiveStep(3);
    } catch (err: any) {
      setIsProcessing(false);
      if (err.kind !== ErrorKind.Unauthorized) {
        let error = compoundErrorMessage(err, 'An error occurred turning on two-factor authentication');
        setApiError(error);
      } else {
        props.onAuthError();
      }
    }
  }

  return (
    <>
      <button className="btn btn-success btn-sm" onClick={setUpTFA} disabled={isLoading} aria-label="Open modal">
        <div className="d-flex flex-row align-items-center">
          {isLoading ? (
            <>
              <span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
              <span className="ms-2">Enabling two-factor authentication...</span>
            </>
          ) : (
            <>
              <FaLock className="me-2" />
              <span>Enable two-factor authentication</span>
            </>
          )}
        </div>
      </button>

      <Modal
        modalClassName={styles.modal}
        header={<div className={`h3 m-2 flex-grow-1 text-truncate ${styles.title}`}>Setup 2FA</div>}
        open={openStatus}
        onClose={onClose}
        closeButton={
          <>
            <button
              className="btn btn-sm btn-outline-secondary text-uppercase"
              onClick={onClose}
              aria-label={activeStep === 3 ? 'Close' : 'Cancel'}
            >
              <div className="d-flex flex-row align-items-center">
                <IoMdCloseCircle className="me-2" />
                <span>{activeStep === 3 ? 'Close' : 'Cancel'}</span>
              </div>
            </button>

            {(() => {
              switch (activeStep) {
                case 1:
                  return (
                    <button
                      className="btn btn-sm btn-outline-secondary ms-3"
                      onClick={(e) => {
                        e.preventDefault();
                        setActiveStep(2);
                      }}
                      aria-label="Open next step"
                    >
                      <div className="d-flex flex-row align-items-center text-uppercase">
                        <MdNavigateNext className="me-2" />
                        <span>Next</span>
                      </div>
                    </button>
                  );
                case 2:
                  return (
                    <button
                      className="btn btn-sm btn-success ms-3"
                      onClick={(e) => {
                        e.preventDefault();
                        enableTFA();
                      }}
                      disabled={passcode === '' || isProcessing}
                      aria-label="Enable two-factor authentication"
                    >
                      <div className="d-flex flex-row align-items-center text-uppercase">
                        {isProcessing ? (
                          <>
                            <span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
                            <span className="ms-2">Enabling...</span>
                          </>
                        ) : (
                          <>
                            <FaLock className="me-2" />
                            <span>Enable</span>
                          </>
                        )}
                      </div>
                    </button>
                  );
                default:
                  return null;
              }
            })()}
          </>
        }
        error={apiError}
        cleanError={() => setApiError(null)}
      >
        <div className="mw-100 h-100 position-relative">
          {setUp && (
            <>
              {(() => {
                switch (activeStep) {
                  case 1:
                    return (
                      <>
                        <div className="h4">Recovery codes</div>
                        <div className={`mt-3 mb-4 ${styles.label}`}>
                          These codes can be used if you lose access to your 2FA credentials. Each of the codes can only
                          be used once.{' '}
                          <span className="fw-bold">Please treat them as passwords and store them safely</span>.
                        </div>
                        <div className={`border rounded position-relative p-2 p-sm-4 ${styles.codesWrapper}`}>
                          <BlockCodeButtons
                            filename="artifacthub-recovery-codes.txt"
                            content={setUp.recoveryCodes.join('\n')}
                            hiddenCopyBtn
                          />

                          <div className="d-flex flex-column align-items-center overflow-auto">
                            {setUp.recoveryCodes.map((code: string) => (
                              <div className={`font-monospace ${styles.code}`} key={`code_${code}`}>
                                {code}
                              </div>
                            ))}
                          </div>
                        </div>

                        <div className="mt-4 alert alert-warning">
                          We strongly recommend you to download and print your recovery codes before proceeding with the
                          2FA setup.
                        </div>
                      </>
                    );
                  case 2:
                    return (
                      <>
                        <div className="h4">Authentication app</div>
                        <div className={`mt-3 mb-4 ${styles.label}`}>
                          Please scan the image below with your 2FA authentication app. If you can't scan it, you can
                          use{' '}
                          <ButtonCopyToClipboard
                            text={setUp.secret}
                            wrapperClassName="d-inline-block"
                            icon={<></>}
                            visibleBtnText
                            contentBtn="this text code"
                            className={`btn-link text-reset p-0 text-secondary fw-bold ${styles.copyBtn}`}
                            label="Copy 2FA code to clipboard"
                          />
                          to set it up manually.
                        </div>

                        <div className="text-center mb-4">
                          <div className="border rounded d-inline-block p-1 my-1">
                            <img className={styles.qrCode} src={setUp.qrCode} alt="QR code" />
                          </div>
                        </div>

                        <div className={`mb-4 ${styles.label}`}>
                          Please enter one of the codes from the 2FA authentication app to confirm your account has been
                          setup successfully before completing the process.
                        </div>

                        <InputField
                          className={styles.inputWrapper}
                          type="text"
                          name="passcode"
                          autoComplete="off"
                          value={passcode}
                          onChange={onPasscodeChange}
                          invalidText={{
                            default: 'This field is required',
                          }}
                          validateOnBlur
                          required
                        />
                      </>
                    );
                  case 3:
                    return (
                      <div className="d-flex flex-column h-100 w-100 px-3 align-items-center justify-content-center text-center position-relative">
                        {isNull(apiError) && (
                          <>
                            <MdDone className="display-4 text-success mb-4" />
                            Two-factor authentication has been successfully enabled. We recommend you to sign out and
                            back in to your account.
                          </>
                        )}
                      </div>
                    );
                  default:
                    return null;
                }
              })()}
            </>
          )}
        </div>
      </Modal>
    </>
  );
}
Example #5
Source File: ResetPasswordModal.tsx    From hub with Apache License 2.0 4 votes vote down vote up
ResetPasswordModal = (props: Props) => {
  const [code, setCode] = useState(props.code);
  const [verifying, setVerifying] = useState<boolean | null>(null);
  const [validCode, setValidCode] = useState<boolean | null>(null);
  const form = useRef<HTMLFormElement>(null);
  const passwordInput = useRef<RefInputField>(null);
  const repeatPasswordInput = useRef<RefInputField>(null);
  const [password, setPassword] = useState<Password>({ value: '', isValid: false });
  const [displayResetPwd, setDisplayResetPwd] = useState<boolean>(false);
  const history = useHistory();
  const [isSending, setIsSending] = useState<boolean>(false);
  const [isValidated, setIsValidated] = useState<boolean>(false);
  const [apiError, setApiError] = useState<string | null>(null);
  const [apiPwdError, setApiPwdError] = useState<string | null>(null);
  const [isSuccess, setIsSuccess] = useState<boolean>(false);

  const onPasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
    setPassword({ value: e.target.value, isValid: e.currentTarget.checkValidity() });
  };

  // Clean API error when form is focused after validation
  const cleanApiError = () => {
    if (!isNull(apiError)) {
      setApiError(null);
    }
  };

  async function resetPassword(password: string) {
    try {
      await API.resetPassword(code!, password);
      setIsSuccess(true);
      setIsSending(false);
    } catch (err: any) {
      let error = compoundErrorMessage(err, 'An error occurred resetting the password');
      setApiError(error);
      setIsSending(false);
    }
  }

  const submitForm = () => {
    cleanApiError();
    setIsSending(true);
    if (form.current) {
      validateForm(form.current).then((validation: FormValidation) => {
        if (validation.isValid) {
          resetPassword(validation.password!);
          setIsValidated(true);
        } else {
          setIsSending(false);
        }
      });
    }
  };

  const validateForm = async (form: HTMLFormElement): Promise<FormValidation> => {
    let currentPassword: string | undefined;
    return validateAllFields().then((isValid: boolean) => {
      if (isValid) {
        const formData = new FormData(form);

        currentPassword = formData.get('password') as string;
      }
      setIsValidated(true);
      return { isValid, password: currentPassword };
    });
  };

  const validateAllFields = async (): Promise<boolean> => {
    return Promise.all([passwordInput.current!.checkIsValid(), repeatPasswordInput.current!.checkIsValid()]).then(
      (res: boolean[]) => {
        return every(res, (isValid: boolean) => isValid);
      }
    );
  };

  useEffect(() => {
    async function verifyPasswordResetCode() {
      setVerifying(true);
      try {
        await API.verifyPasswordResetCode(code!);
        setValidCode(true);
      } catch (err: any) {
        if (err.kind === ErrorKind.Gone) {
          setApiError('This password reset link is no longer valid, please get a new one.');
          setDisplayResetPwd(true);
        } else {
          let error = 'An error occurred with your password reset code.';
          if (!isUndefined(err.message)) {
            error = `Sorry, ${err.message}`;
          }
          setApiPwdError(error);
        }
        setValidCode(false);
      } finally {
        setVerifying(false);
      }
    }

    if (!isUndefined(code)) {
      history.replace({
        pathname: '/',
        search: '',
      });
      if (code !== props.code) {
        setCode(code);
      }
      verifyPasswordResetCode();
    }
  }, [code, history]); /* eslint-disable-line react-hooks/exhaustive-deps */

  if (isUndefined(code) || isNull(verifying)) return null;

  const closeButton = (
    <button
      className="btn btn-sm btn-outline-secondary"
      type="button"
      disabled={isSending}
      onClick={submitForm}
      aria-label="Reset password"
    >
      <div className="d-flex flex-row align-items-center">
        {isSending ? (
          <>
            <span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
            <span className="ms-2">Resetting password...</span>
          </>
        ) : (
          <>
            <CgLastpass className="me-2" />
            <span className="text-uppercase">Reset password</span>
          </>
        )}
      </div>
    </button>
  );

  return (
    <Modal
      data-testid="resetPwdModal"
      header={<div className={`h3 m-2 flex-grow-1 ${styles.title}`}>Reset password</div>}
      disabledClose={verifying}
      modalClassName={styles.modal}
      open={!isUndefined(code)}
      closeButton={validCode && !isSuccess ? closeButton : undefined}
      error={apiError}
      cleanError={cleanApiError}
    >
      <div
        className={`d-flex flex-column h-100 w-100 align-items-center justify-content-center text-center position-relative ${styles.content}`}
      >
        {verifying ? (
          <>
            <Loading className="position-relative" spinnerClassName="mt-0" />
            <small className="text-muted">We are verifying your code...</small>
          </>
        ) : (
          <div className="text-start w-100">
            {validCode ? (
              <>
                {isSuccess ? (
                  <div className="d-flex flex-column text-center h5">
                    <div className="display-4 text-success mb-4">
                      <MdDone />
                    </div>
                    Your password has been reset successfully. You can now log in using the new credentials.
                  </div>
                ) : (
                  <form
                    ref={form}
                    data-testid="resetPwdForm"
                    className={classnames(
                      'w-100',
                      { 'needs-validation': !isValidated },
                      { 'was-validated': isValidated }
                    )}
                    onFocus={cleanApiError}
                    autoComplete="off"
                    noValidate
                  >
                    <InputField
                      ref={passwordInput}
                      type="password"
                      label="Password"
                      name="password"
                      minLength={6}
                      invalidText={{
                        default: 'This field is required',
                        customError: 'Insecure password',
                      }}
                      onChange={onPasswordChange}
                      autoComplete="new-password"
                      checkPasswordStrength
                      validateOnChange
                      validateOnBlur
                      required
                    />

                    <InputField
                      ref={repeatPasswordInput}
                      type="password"
                      label="Confirm password"
                      labelLegend={<small className="ms-1 fst-italic">(Required)</small>}
                      name="confirmPassword"
                      pattern={password.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}
                      invalidText={{
                        default: 'This field is required',
                        patternMismatch: "Passwords don't match",
                      }}
                      autoComplete="new-password"
                      validateOnBlur={password.isValid}
                      required
                    />
                  </form>
                )}
              </>
            ) : (
              <>
                {displayResetPwd && <ResetPassword visibleTitle={false} onFinish={cleanApiError} />}
                {apiPwdError && (
                  <div className="d-flex flex-column text-center h5">
                    <div className="display-4 text-danger mb-4">
                      <MdClose />
                    </div>
                    {apiPwdError}
                  </div>
                )}
              </>
            )}
          </div>
        )}
      </div>
    </Modal>
  );
}
Example #6
Source File: CreateAnAccount.tsx    From hub with Apache License 2.0 4 votes vote down vote up
CreateAnAccount = forwardRef<HTMLFormElement, Props>((props, ref) => {
  const usernameInput = useRef<RefInputField>(null);
  const emailInput = useRef<RefInputField>(null);
  const passwordInput = useRef<RefInputField>(null);
  const repeatPasswordInput = useRef<RefInputField>(null);
  const [isValidated, setIsValidated] = useState(false);
  const [isValidatingField, setIsValidatingField] = useState(false);
  const [password, setPassword] = useState<Password>({ value: '', isValid: false });

  const onPasswordChange = (e: ChangeEvent<HTMLInputElement>) => {
    setPassword({ value: e.target.value, isValid: e.currentTarget.checkValidity() });
  };

  // Clean API error when form is focused after validation
  const cleanApiError = () => {
    if (!isNull(props.apiError)) {
      props.setApiError(null);
    }
  };

  async function registerUser(user: User) {
    try {
      await API.register(user);
      props.setSuccess(true);
      props.setIsLoading({ status: false });
    } catch (err: any) {
      let error = compoundErrorMessage(err, 'An error occurred registering the user');
      props.setApiError(error);
      props.setIsLoading({ status: false });
    }
  }

  const submitForm = (e: FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    e.stopPropagation();

    if (!isValidatingField) {
      cleanApiError();
      props.setIsLoading({ type: 'log', status: true });
      if (e.currentTarget) {
        validateForm(e.currentTarget).then((validation: FormValidation) => {
          if (validation.isValid && !isNull(validation.user)) {
            registerUser(validation.user);
          } else {
            props.setIsLoading({ status: false });
          }
        });
      }
    }
  };

  const validateForm = async (form: HTMLFormElement): Promise<FormValidation> => {
    let user: User | null = null;

    return validateAllFields().then((isValid: boolean) => {
      if (isValid) {
        const formData = new FormData(form);
        user = {
          alias: formData.get('alias') as string,
          email: formData.get('email') as string,
          password: formData.get('password') as string,
        };

        if (formData.get('firstName') !== '') {
          user['firstName'] = formData.get('firstName') as string;
        }

        if (formData.get('lastName') !== '') {
          user['lastName'] = formData.get('lastName') as string;
        }
      }
      setIsValidated(true);
      return { isValid, user };
    });
  };

  const validateAllFields = async (): Promise<boolean> => {
    return Promise.all([
      usernameInput.current!.checkIsValid(),
      emailInput.current!.checkIsValid(),
      passwordInput.current!.checkIsValid(),
      repeatPasswordInput.current!.checkIsValid(),
    ]).then((res: boolean[]) => {
      return every(res, (isValid: boolean) => isValid);
    });
  };

  return (
    <>
      {props.success ? (
        <div className="d-flex h-100 w-100 align-items-center justify-content-center">
          <div className="alert" role="alert" aria-live="assertive" aria-atomic="true">
            <div className="d-flex flex-sm-column flex-md-row align-items-center">
              <div className="me-3">
                <MdDone className="h1 text-success mb-3 mb-md-0" />
              </div>
              <h4 className="alert-heading">A verification link has been sent to your email account</h4>
            </div>
            <hr />
            <p>
              Please click on the link that has just been sent to your email account to verify your email and finish the
              registration process.
            </p>
            <p className="mb-0">
              Please note that the verification code <span className="fw-bold">is only valid for 24 hours</span>. If you
              haven't verified your account by then you'll need to sign up again.
            </p>
          </div>
        </div>
      ) : (
        <>
          <form
            ref={ref}
            data-testid="createAnAccountForm"
            className={classnames('w-100', { 'needs-validation': !isValidated }, { 'was-validated': isValidated })}
            onFocus={cleanApiError}
            onSubmit={(e: FormEvent<HTMLFormElement>) => submitForm(e)}
            autoComplete="on"
            noValidate
          >
            <InputField
              ref={usernameInput}
              type="text"
              label="Username"
              labelLegend={<small className="ms-1 fst-italic">(Required)</small>}
              name="alias"
              invalidText={{
                default: 'This field is required',
                customError: 'Username not available',
              }}
              checkAvailability={{
                isAvailable: true,
                resourceKind: ResourceKind.userAlias,
                excluded: [],
              }}
              setValidationStatus={setIsValidatingField}
              validateOnBlur
              autoComplete="username"
              required
            />

            <InputField
              ref={emailInput}
              type="email"
              label="Email"
              labelLegend={<small className="ms-1 fst-italic">(Required)</small>}
              name="email"
              invalidText={{
                default: 'This field is required',
                typeMismatch: 'Please enter a valid email address',
              }}
              validateOnBlur
              autoComplete="email"
              required
            />

            <InputField type="text" label="First Name" name="firstName" autoComplete="given-name" />

            <InputField type="text" label="Last Name" name="lastName" autoComplete="family-name" />

            <InputField
              ref={passwordInput}
              type="password"
              label="Password"
              name="password"
              invalidText={{
                default: 'This field is required',
                customError: 'Insecure password',
              }}
              onChange={onPasswordChange}
              autoComplete="new-password"
              checkPasswordStrength
              validateOnChange
              validateOnBlur
              required
            />

            <InputField
              ref={repeatPasswordInput}
              type="password"
              label="Confirm password"
              labelLegend={<small className="ms-1 fst-italic">(Required)</small>}
              name="confirmPassword"
              pattern={password.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}
              invalidText={{
                default: 'This field is required',
                patternMismatch: "Passwords don't match",
              }}
              autoComplete="new-password"
              validateOnBlur={password.isValid}
              required
            />
          </form>
        </>
      )}
    </>
  );
})
Example #7
Source File: ResetPassword.tsx    From hub with Apache License 2.0 4 votes vote down vote up
ResetPassword = (props: Props) => {
  const resetPwdForm = useRef<HTMLFormElement>(null);
  const resetPwdEmailInput = useRef<RefInputField>(null);
  const [isValidated, setIsValidated] = useState(false);
  const [resetPwdEmail, setResetPwdEmail] = useState('');
  const [isSending, setIsSending] = useState<boolean>(false);
  const [isSuccess, setIsSuccess] = useState<boolean>(false);

  const onResetPwdEmailChange = (e: ChangeEvent<HTMLInputElement>) => {
    setResetPwdEmail(e.target.value);
  };

  async function requestPasswordResetCode(email: string) {
    try {
      await API.requestPasswordResetCode(email);
      setIsSuccess(true);
      setIsSending(false);
    } catch (err: any) {
      setIsSending(false);
    } finally {
      if (props.onFinish) {
        props.onFinish();
      }
    }
  }

  const submitForm = () => {
    if (resetPwdForm.current) {
      setIsSending(true);
      validateForm().then((validation: FormValidation) => {
        if (validation.isValid && !isUndefined(validation.resetPwdEmail)) {
          requestPasswordResetCode(validation.resetPwdEmail);
        } else {
          setIsSending(false);
        }
      });
    }
  };

  const validateForm = (): Promise<FormValidation> => {
    let email: undefined | string;

    return resetPwdEmailInput.current!.checkIsValid().then((isValid: boolean) => {
      if (isValid) {
        email = resetPwdEmail;
      }
      setIsValidated(true);
      return { isValid: isValid, resetPwdEmail: email };
    });
  };

  return (
    <>
      {isSuccess ? (
        <div className="d-flex flex-column text-center">
          <div className="display-4 text-success mb-4">
            <MdDone />
          </div>
          We have sent a password reset link to your email, please check your inbox (and the spam folder if needed).
        </div>
      ) : (
        <form
          data-testid="resetPasswordForm"
          ref={resetPwdForm}
          className={classnames('w-100', { 'needs-validation': !isValidated }, { 'was-validated': isValidated })}
          autoComplete="on"
          onFocus={() => {
            if (props.onFocus) {
              props.onFocus();
            }
          }}
          noValidate
        >
          <div className="my-auto">
            {props.visibleTitle && <div className="h6 mb-3 fw-bold">Forgot Password?</div>}
            <p>Please enter your email address and we will send you a password reset link.</p>
            <InputField
              ref={resetPwdEmailInput}
              type="email"
              label="Email"
              name="resetPwdEmail"
              value=""
              invalidText={{
                default: 'This field is required',
                typeMismatch: 'Please enter a valid email address',
              }}
              autoComplete="email"
              onChange={onResetPwdEmailChange}
              validateOnBlur={resetPwdEmail !== ''}
              required
            />

            <div className="text-end">
              <button
                className="btn btn-sm btn-outline-secondary"
                type="button"
                disabled={isSending || resetPwdEmail === ''}
                onClick={submitForm}
                aria-label="Send password reset email"
              >
                <div className="d-flex flex-row align-items-center">
                  {isSending ? (
                    <>
                      <span className="spinner-grow spinner-grow-sm" role="status" aria-hidden="true" />
                      <span className="ms-2">Sending email...</span>
                    </>
                  ) : (
                    <>
                      <CgLastpass className="me-2" />
                      <>Send password reset email</>
                    </>
                  )}
                </div>
              </button>
            </div>
          </div>
        </form>
      )}
    </>
  );
}