0

When I click outside the modal currently, my page goes into loading state infinitely as shown in the image below

This is the code for AppSignup.vue component if it helps

<!-- https://uistore.dev/framework/buefy/form/login-form-with-image-on-left -->
<template lang="pug">
.signup__container.is-flex.justify-content-center.is-align-items-center
  .section.is-flex-grow-1
    .columns.is-centered
      .column.is-two-thirds-tablet.is-half-desktop.is-one-third-widescreen.is-one-quarter-fullhd
        form(
          @submit.prevent='onSubmit',
          method='POST'
        )
          p.subtitle.has-text-centered Sign Up
          b-notification(
            aria-close-label='Close Notification',
            id='error',
            role='alert',
            type='is-danger',
            v-if='errorMessage'
          ) {{ errorMessage }}
          b-field.has-text-left(
            :label='formData.email.label',
            :message='emailErrorMessage',
            :type='emailErrorType',
            id='emailField'
          )
            b-input(
              :disabled='formData.email.disabled',
              :has-counter='false',
              :maxlength='formData.email.maxLength',
              :minlength='formData.email.minLength',
              :placeholder='formData.email.placeholder',
              :use-html5-validation='false',
              id='email'
              key='email',
              ref='email',
              required,
              type='email',
              v-model='formData.email.value'
            )
          b-field.has-text-left(
            :label='formData.password.label',
            :message='passwordErrorMessage',
            :type='passwordErrorType',
            id='passwordField'
          )
            b-input(
              :disabled='formData.password.disabled',
              :has-counter='false',
              :maxlength='formData.password.maxLength',
              :minlength='formData.password.minLength',
              :placeholder='formData.password.placeholder',
              :use-html5-validation='false',
              id='password',
              key='password',
              password-reveal,
              ref='password',
              required,
              type='password',
              v-model='formData.password.value'
            )
          .field.is-horizontal.is-justify-content-space-between
            .control
              label.checkbox
              input(
                required,
                type='checkbox'
              )
              p.help.is-inline-block &nbsp;I agree to the&nbsp;
              n-link.help.is-inline-block(to='/terms') terms&nbsp;
              p.help.is-inline-block and&nbsp;
              n-link.help.is-inline-block(to='/terms') conditions
          .field
            .control
              recaptcha(
                @recaptcha-error='onRecaptchaError', 
                @recaptcha-expired='onRecaptchaExpired'
              )
          .field
            .control
              b-button(
                :class='{ "is-loading": state === "loading" }',
                :disabled='isSignupButtonDisabled',
                :type='signupButtonType',
                expanded,
                id='signup',
                native-type='submit'
              ) {{ signupButtonText }}
          .field 
            .control.has-text-centered or
          hr
          .field
            .control.has-text-centered
              n-link.button.is-fullwidth.is-text.signup(to='/login') Login
          .field 
            .control.has-text-centered
              n-link(to='/privacy') Privacy Policy
              |  and 
              n-link(to='/terms') Terms of Use
</template>

<script>
const SIGNUP_STATES = {
  ERROR: 'error',
  IDLE: 'idle',
  LOADING: 'loading',
  SUCCESS: 'success',
}

const RECAPTCHA_SERVER_ERROR_CODES = {
  'bad-request': 'Server had an issue, please try again',
  'invalid-input-response': 'Server had an issue, please try again',
  'invalid-input-secret':
    'Server had an issue, please try again after some time',
  'missing-input-response': 'Server had an issue, please try again',
  'missing-input-secret':
    'Server had an issue, please try again after some time',
  'timeout-or-duplicate': 'Captcha timed out, please try again',
}

export default {
  name: 'AppSignup',

  // https://github.com/vuejs/vue-router/issues/1103
  beforeRouteEnter(to, from, next) {
    next((vm) => {
      vm.$store.commit('setRedirectTo', from)
    })
  },

  data() {
    return {
      formData: this.getDefaultFormData(),
      state: SIGNUP_STATES.IDLE,
      errorMessage: null,
    }
  },

  computed: {
    emailErrorMessage() {
      return this.formData.email.errorMessages.join('\n')
    },

    emailErrorType() {
      const hasError =
        (typeof this.emailErrorMessage === 'string' &&
          this.emailErrorMessage.length > 0) ||
        (typeof this.errorMessage === 'string' && this.errorMessage.length > 0)
      return hasError ? 'is-danger' : 'is-light'
    },

    isSignupButtonDisabled() {
      const hasEmailAndPassword =
        this.formData.email.value.length && this.formData.password.value.length
      return !hasEmailAndPassword
    },

    signupButtonText() {
      if (this.state === SIGNUP_STATES.ERROR) {
        return 'Create Account'
      } else if (this.state === SIGNUP_STATES.LOADING) {
        return ''
      } else if (this.state === SIGNUP_STATES.SUCCESS) {
        return 'Signed In'
      } else {
        return 'Create Account'
      }
    },

    signupButtonType() {
      if (this.state === SIGNUP_STATES.ERROR) {
        return 'is-warning'
      } else if (this.state === SIGNUP_STATES.LOADING) {
        return 'is-warning is-light'
      } else if (this.state === SIGNUP_STATES.SUCCESS) {
        return 'is-success'
      } else {
        return 'is-warning'
      }
    },

    passwordErrorMessage() {
      return this.formData.password.errorMessages.join('\n')
    },

    passwordErrorType() {
      const hasError =
        (typeof this.passwordErrorMessage === 'string' &&
          this.passwordErrorMessage.length > 0) ||
        (typeof this.errorMessage === 'string' && this.errorMessage.length > 0)
      return hasError ? 'is-danger' : 'is-light'
    },
  },

  // https://stackoverflow.com/a/72105354/5371505
  async mounted() {
    try {
      await this.$recaptcha.init()
    } catch (error) {
      throw new Error(`index# Problem initializing ReCaptcha: ${error}.`)
    }
  },

  // https://stackoverflow.com/a/72105354/5371505
  beforeDestroy() {
    this.$recaptcha.destroy()
  },

  methods: {
    // {"status_code":400,"type":"validation_error","param":"Bad Request","message":"Validation Failed","details":[{"recaptchaToken":"\"recaptchaToken\" is required"}]}
    // {"status_code":400,"type":"validation_error","param":"Bad Request","message":"Validation Failed","details":[{"email":"\"email\" must be a valid email"}]}
    // {"status_code":400,"type":"validation_error","param":"Bad Request","message":"Validation Failed","details":[{"password":"\"password\" length must be at least 8 characters long"}]}
    // 400 bad request is generated for backend recaptcha errors like {"success":false,"error-codes":["invalid-input-secret"]}
    // {"status_code":401,"type":"not_authenticated","param":null,"message":{"message":"Incorrect email or password"},"details":null}
    // {"status_code":422,"type":"validation_error","param":"authentication_type_id,email","message":"authentication_type_id must be unique,email must be unique","details":null}
    async doSignup() {
      try {
        const email = this.formData.email.value
        const password = this.formData.password.value
        const recaptchaToken = await this.$recaptcha.getResponse()
        await this.$store.dispatch('auth/signup', {
          email,
          password,
          recaptchaToken,
        })
        this.resetFormData()
        this.state = SIGNUP_STATES.SUCCESS
        this.formData.email.disabled = true
        this.formData.password.disabled = true
        this.$buefy.toast.open({
          duration: 5000,
          message:
            'Account created successfully and logged in as ' +
              this.$store.state.auth.user.username ||
            this.$store.state.auth.user.email,
          position: 'is-top',
          type: 'is-success',
        })
        await this.$utils.timeout(100)
        this.$router.replace(this.$store.state.redirectTo)
      } catch (error) {
        this.state = SIGNUP_STATES.ERROR
        // Clear existing error messages before adding new ones
        this.errorMessage = null
        this.formData.email.errorMessages = []
        this.formData.password.errorMessages = []
        this.handleAxiosError(error)
        this.state = SIGNUP_STATES.IDLE
      } finally {
        this.$recaptcha.reset()
      }
    },

    getDefaultFormData() {
      return {
        email: {
          disabled: false,
          errorMessages: [],
          label: '',
          maxLength: 320,
          minLength: 3,
          placeholder: '[email protected]',
          value: '',
        },
        password: {
          disabled: false,
          errorMessages: [],
          label: '',
          maxLength: 255,
          minLength: 8,
          placeholder: '8+ characters',
          value: '',
        },
      }
    },

    // https://axios-http.com/docs/handling_errors
    // https://stackoverflow.com/a/58386844/5371505
    // https://developers.google.com/recaptcha/docs/verify#error_code_reference
    handleAxiosError(error) {
      if (error.response) {
        // The request was made and the server responded with a status code
        // that falls out of the range of 2xx
        if (Array.isArray(error.response.data.details)) {
          const errorMessages = error.response.data.details
          for (let i = 0; i < errorMessages.length; i++) {
            const errorMessage = errorMessages[i]
            if (!(typeof errorMessage === 'object')) {
              continue
            }
            if (errorMessage.email) {
              this.formData.email.errorMessages.push(errorMessage.email)
            } else if (errorMessage.password) {
              this.formData.password.errorMessages.push(errorMessage.password)
            } else {
              const messages = []
              for (const key in errorMessage) {
                if (errorMessage[key]) {
                  messages.push(errorMessage[key])
                }
              }
              this.errorMessage = messages.join('\n')
            }
          }
        } else if (Array.isArray(error.response.data['error-codes'])) {
          const errorCode = error.response.data['error-codes'][0]
          this.errorMessage = RECAPTCHA_SERVER_ERROR_CODES[errorCode]
        } else if (
          error.response.data.param === 'authentication_type_id,email'
        ) {
          this.errorMessage = 'Account with this email already exists'
        } else {
          this.errorMessage =
            typeof error.response.data.message === 'object' &&
            error.response.data.message !== null &&
            typeof error.response.data.message.message === 'string'
              ? error.response.data.message.message
              : error.response.data.message ||
                'Please try again, something went wrong'
        }
      } else if (error.request) {
        // Sample error when backend is unavailable {"message":"Network Error","name":"Error","fileName":"http://localhost:3000/_nuxt/commons/app.js line 424 > eval","lineNumber":16,"columnNumber":15,"stack":"createError@webpack-internal:///./node_modules/axios/lib/core/createError.js:16:15\nhandleError@webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:99:14\nEventHandlerNonNull*dispatchXhrRequest@webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:96:5\nxhrAdapter@webpack-internal:///./node_modules/axios/lib/adapters/xhr.js:13:10\ndispatchRequest@webpack-internal:///./node_modules/axios/lib/core/dispatchRequest.js:53:10\npromise callback*request@webpack-internal:///./node_modules/axios/lib/core/Axios.js:88:25\nforEachMethodWithData/Axios.prototype[method]@webpack-internal:///./node_modules/axios/lib/core/Axios.js:140:17\nwrap@webpack-internal:///./node_modules/axios/lib/helpers/bind.js:9:15\n_callee2$@webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./components/auth/AppSignup.vue?vue&type=script&lang=js:95:36\ntryCatch@webpack-internal:///./node_modules/regenerator-runtime/runtime.js:64:40\ninvoke@webpack-internal:///./node_modules/regenerator-runtime/runtime.js:299:30\ndefineIteratorMethods/</<@webpack-internal:///./node_modules/regenerator-runtime/runtime.js:124:21\nasyncGeneratorStep@webpack-internal:///./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:5:24\n_next@webpack-internal:///./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:24:27\npromise callback*asyncGeneratorStep@webpack-internal:///./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:14:28\n_next@webpack-internal:///./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:24:27\n_asyncToGenerator/</<@webpack-internal:///./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:29:12\n_asyncToGenerator/<@webpack-internal:///./node_modules/@babel/runtime/helpers/esm/asyncToGenerator.js:21:12\ndoSignup@webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./components/auth/AppSignup.vue?vue&type=script&lang=js:125:10\nonSubmit@webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/index.js?!./components/auth/AppSignup.vue?vue&type=script&lang=js:195:19\nsubmit@webpack-internal:///./node_modules/babel-loader/lib/index.js?!./node_modules/vue-loader/lib/loaders/templateLoader.js?!./node_modules/pug-plain-loader/index.js?!./node_modules/vue-loader/lib/index.js?!./components/auth/AppSignup.vue?vue&type=template&id=1b11a5c4&lang=pug:22:29\ninvokeWithErrorHandling@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3056:30\ninvoker@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1857:20\nadd/original_1._wrapper@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7513:35\nEventListener.handleEvent*add@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7517:12\nupdateListeners@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:1880:16\nupdateDOMListeners@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7534:20\ninvokeCreateHooks@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:6692:28\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7071:42\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7044:34\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7044:34\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7044:34\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7044:34\npatch@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7126:36\nlifecycleMixin/Vue.prototype._update@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3804:25\nupdateComponent@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3907:16\nWatcher.prototype.get@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3485:33\nWatcher@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3475:51\nmountComponent@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3931:5\nVue.prototype.$mount@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:8812:12\ninit@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4448:19\nmerged@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4602:11\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7007:18\npatch@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7126:36\nlifecycleMixin/Vue.prototype._update@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3804:25\nupdateComponent@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3907:16\nWatcher.prototype.get@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3485:33\nWatcher@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3475:51\nmountComponent@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3931:5\nVue.prototype.$mount@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:8812:12\ninit@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:4448:19\nhydrate@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7007:18\npatch@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:7126:36\nlifecycleMixin/Vue.prototype._update@webpack-internal:///./node_modules/vue/dist/vue.runtime.esm.js:3804:25\n","config":{"url":"/api/v1/auth/login","method":"post","data":"{\"email\":\"abc@example\",\"password\":\"4564849\",\"recaptchaToken\":\"03AFcWeA5fuPzu8jMiTWj_9JbB_Rdx2ewMy7hoEvGusuTT0CLHXONAxObXxP7cPYeTE8RxqrM_Kf3dkVVwLB0wpK_gYUGjmHsmF9Y5uE-Sc_zleXLrpuGkVnOAt0R99mA5cDVlXN-tia5yBdBdC42JyhIvQchzL6tCDbm2Yg7oIm_jpozydQb94tXx2wxPWj4LxumvXPRN9Ws2CCoAv9cC6E85U7G9vG8faEYlL2za_JpenPMOT4sdvUQDwHdE1RNNkJe1iotBpjkucyD-_AsndfX2h09xwi8aNlCdwnjIu_SEJuXgmZnh9Kz4X-Eu1hvi1XMlYSKeSOmAQDmYSYlnzb4HTjP8yeTOudbhwwiy5rgC2gZ9kf9knFyvigdFMPfFq86rJpM_4hoRm6FZQ6q8cwEikq1aR0pCHKXps87vFG2g3m7YNdowqGLDjuajzikSQX2g9a9AAMH_g98hmOf5imU-X6dlxoEtL0P9Ni3jiNBGG3rgN7ObE6cDiWDoPm33JFv3cF8fi5T_S04klD7Uypn1REm3_uVC6fDtilyw5sSORYiDWo95cnvcldC-j6wQD8qk4HxvQlpY\"}","headers":{"Accept":"application/json, text/plain, */*","Content-Type":"application/json"},"baseURL":"http://localhost:8000","transformRequest":[null],"transformResponse":[null],"timeout":0,"xsrfCookieName":"XSRF-TOKEN","xsrfHeaderName":"X-XSRF-TOKEN","maxContentLength":-1,"maxBodyLength":-1,"transitional":{"silentJSONParsing":true,"forcedJSONParsing":true,"clarifyTimeoutError":false}}}
        // The request was made but no response was received
        // `error.request` is an instance of XMLHttpRequest in the browser and an instance of
        // http.ClientRequest in node.js
        this.errorMessage =
          error.request.message ||
          error.request.statusText ||
          error.message ||
          'Please try again, something went wrong'
      } else {
        // Something happened in setting up the request that triggered an Error
        this.errorMessage =
          error.message || 'Please try again, something went wrong'
      }
    },

    onRecaptchaError(error) {
      this.state = SIGNUP_STATES.ERROR
      this.errorMessage = error.message
    },

    onRecaptchaExpired() {
      this.state = SIGNUP_STATES.ERROR
      this.errorMessage = 'Captcha has expired, please try again'
    },

    onSubmit() {
      this.state = SIGNUP_STATES.LOADING
      return this.doSignup()
    },

    resetFormData() {
      this.formData = this.getDefaultFormData()
      this.errorMessage = null
    },
  },
}
</script>

<style lang="scss">
.signup__container {
  height: 100%;
}
button[type='submit'] {
  transition-property: background-color;
  transition-duration: 0.5s;
}
</style>

Does anyone know how I can fix this issue?

enter image description here

0