codersforcauses/p2s-frontend

View on GitHub
src/components/admin/modals/UserInvite.vue

Summary

Maintainability
Test Coverage
<template>
  <v-dialog scrollable
            max-width=520
            v-model="showDialog"
            transition="dialog-transition"
            :fullscreen="$vuetify.breakpoint.xsOnly"
  >
    <v-card flat tile>
      <v-toolbar flat>
        <h1 class="headline"> Invite Users </h1>
        <v-spacer/>
        <v-btn flat icon @click="$emit('input')">
          <v-icon> mdi-close </v-icon>
        </v-btn>
      </v-toolbar>

      <v-card-text class="pa-4">
        <v-form v-model="valid"
                class="pt-2"
                :class="{ 'px-2': $vuetify.breakpoint.smAndUp }"
                @keyup.native.enter="valid && createUser($event) && !checkPerm && !!checkPerm"
        >
          <v-layout row wrap>
            <v-flex xs12 tag="label" class="v-label ml-4">
              NAME
            </v-flex>
            <v-flex xs6 style="padding-right: 1px;">
              <v-text-field solo-inverted
                            flat
                            persistent-hint
                            hint="Enter the user's name"
                            type="text"
                            class="mb-2 mt-1 first-name"
                            placeholder="First Name"
                            :color="primary"
                            :disabled="loading"
                            :rules="[validation.required, validation.name]"
                            v-model.trim="user.name.first"
              >
              </v-text-field>
            </v-flex>
            <v-flex xs6 style="padding-left: 1px;">
              <v-text-field solo-inverted
                            flat
                            type="text"
                            class="mb-2 mt-1 last-name"
                            placeholder="Last Name"
                            :color="primary"
                            :disabled="loading"
                            :rules="[validation.required, validation.name]"
                            v-model.trim="user.name.last"
              >
              </v-text-field>
            </v-flex>

            <v-flex xs12 tag="label" class="v-label ml-4">
              EMAIL
            </v-flex>
            <v-flex xs12>
              <v-text-field solo-inverted
                            flat
                            persistent-hint
                            validate-on-blur
                            type="email"
                            hint="Enter the user's email address"
                            class="mb-2 mt-1"
                            :color="primary"
                            :disabled="loading"
                            :rules="[validation.required, validation.email]"
                            v-model.trim="user.email"
              >
              </v-text-field>
            </v-flex>

            <v-flex xs12 tag="label" class="v-label ml-4">
              ASSIGN REGION
            </v-flex>
            <v-flex xs12>
              <v-autocomplete solo-inverted
                              flat
                              persistent-hint
                              cache-items
                              hide-selected
                              placeholder="Start typing..."
                              type="text"
                              hint="Important for coaches and regional managers"
                              class="mb-2 mt-1 select__flat"
                              v-model.trim="user.region"
                              item-text="name"
                              item-value="_id"
                              :items="listRegions"
                              :search-input.sync="search"
                              :loading="loadRegions"
                              :color="primary"
                              :disabled="loading"
                              :rules="[validation.required]"
                              :menu-props="{
                                offsetY: true,
                                light: dark,
                                dark: !dark,
                                transition: 'slide-y-transition',
                              }"
              >
                <template slot="no-data">
                  <div class="list">
                    Can't find the region you wanted? Create a new
                      <a  slot="activator"
                          href="/region"
                          target="_blank"
                          :style="{ color: primaryColor }"
                          style="text-decoration: none;"
                          @click.stop
                      >
                        region
                      <v-icon size="1rem"
                              :color="primaryInv"
                      >
                        mdi-open-in-new
                      </v-icon>
                      </a>
                      <p class="caption ma-0">
                        Please refresh the page if you do not see a created region.
                      </p>
                  </div>
                </template>
              </v-autocomplete>
            </v-flex>

            <v-flex xs12 tag="label" class="v-label ml-4">
              ROLES
            </v-flex>
            <v-flex xs12 class="ml-3">
              <v-checkbox hide-details
                          label="Administrator"
                          v-model="permissions.admin.is"
                          class="ma-0 pa-0"
                          :error="checkPerm"
                          :color="primary"
                          :disabled="permissions.manager.is"
              ></v-checkbox>
              <v-checkbox hide-details
                          label="Regional Manager"
                          v-model="permissions.manager.is"
                          class="ma-0 pa-0"
                          :error="checkPerm"
                          :color="primary"
                          :disabled="permissions.admin.is"
              ></v-checkbox>
              <v-checkbox persistent-hint
                          label="Coach"
                          v-model="permissions.coach.is"
                          class="ma-0 pa-0"
                          hint="These can be changed later by any Administrator"
                          :error-messages="permError"
                          :color="primary"
              ></v-checkbox>
            </v-flex>

            <v-flex xs12>
              <v-alert  dismissible
                        v-model="alert"
                        type="error"
                        name="alert"
                        class="alert"
                        transition="slide-y-transition"
              >
                {{ error }}
              </v-alert>
            </v-flex>

            <v-flex xs12 class="mt-3">
              <v-btn  depressed
                      round
                      class="ma-0"
                      style="float: right"
                      :light="!dark"
                      :dark="dark"
                      :color="primary"
                      :disabled="!valid || loading || checkPerm || checkPerm === undefined"
                      :loading="loading"
                      @click.stop.prevent="createUser"
              >
                <span :style="{ color: button }"> Send Invite </span>
              </v-btn>
            </v-flex>
          </v-layout>
        </v-form>
      </v-card-text>
    </v-card>
  </v-dialog>
</template>

<script>
import { mapState, mapGetters, mapActions } from 'vuex';

export default {
  props: ['value', 'dark'],
  data() {
    return {
      user: {
        email: '',
        region: '',
        name: {
          first: '',
          last: '',
        },
      },
      permissions: {
        coach: {
          is: false,
        },
        manager: {
          is: false,
        },
        admin: {
          is: false,
        },
      },
      search: undefined,
      checkPerm: undefined,
      permError: undefined,
      alert: false,
      error: '',
      valid: false,
      validation: {
        required: value => !!value || 'This field is required',
        email: (value) => {
          const pattern = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
          return pattern.test(value) || 'Invalid email address';
        },
        name: (value) => {
          const pattern = /^[a-z ,.'-]+$/i;
          return pattern.test(value) || 'Name must only contain letters';
        },
      },
    };
  },
  watch: {
    search(val) {
      // eslint-disable-next-line
      val = val || '';
      if (!this.listRegions.includes(val)) {
        this.findRegionsInStore({
          query: {
            name: {
              $gte: val,
            },
            $select: ['id', 'name'],
          },
        });
      }
    },
    permissions: {
      handler(val) {
        const admin = val.admin.is;
        const manager = val.manager.is;
        const coach = val.coach.is;

        if (!(manager || admin || coach)) {
          this.checkPerm = true;
          this.permError = 'This field is required';
        } else {
          this.checkPerm = false;
          this.permError = undefined;
        }
      },
      deep: true,
    },
  },
  computed: {
    ...mapState('admin', { createAdmin: 'isCreatePending' }),
    ...mapState('manager', { createManager: 'isCreatePending' }),
    ...mapState('coach', { createCoach: 'isCreatePending' }),
    ...mapState('regions', { loadRegions: 'isFindPending' }),
    ...mapGetters('regions', { listRegions: 'list' }),
    showDialog: {
      get() {
        return this.value;
      },
      set(value) {
        this.$emit('input', value);
      },
    },
    loading() {
      return this.createAdmin || this.createManager || this.createCoach;
    },
    primary() {
      return this.dark ? 'darkPrimary' : 'lightPrimary';
    },
    primaryInv() {
      return !this.dark ? 'darkPrimary' : 'lightPrimary';
    },
    primaryColor() {
      return !this.dark ? 'var(--v-darkPrimary-base)' : 'var(--v-lightPrimary-base)';
    },
    button() {
      return this.dark ? '#272727' : '#ebebeb';
    },
  },
  methods: {
    ...mapActions('regions', { findRegionsInStore: 'find' }),
    async createUser() {
      if (this.valid) {
        const tempUser = {
          ...this.user,
          ...this.permissions,
        };

        let user;
        if (tempUser.admin.is) {
          const { Admin } = this.$FeathersVuex;
          user = new Admin(tempUser);
        } else if (tempUser.manager.is) {
          const { Manager } = this.$FeathersVuex;
          user = new Manager(tempUser);
        } else {
          const { Coach } = this.$FeathersVuex;
          user = new Coach(tempUser);
        }

        await user.create()
          .then(() => this.$emit('input'))
          .catch((err) => {
            this.error = err.message.charAt(0).toUpperCase().concat(err.message.slice(1));
            this.alert = true;
          });
      }
    },
  },
};
</script>

<style scoped>
.alert {
  border: 0;
  border-radius: 30px;
  font-size: 12pt;
  margin: 0.5rem 0 1rem;
  min-width: calc(100% - 20vw);
  z-index: auto;
}
.v-card {
  border-top-left-radius: 0 !important;
  border-top-right-radius: 0 !important;
}
.list {
  padding: calc( 0.5 * var(--thiccness)) var(--thiccness);
}
.v-input--v-checkbox >>> .v-input__slot {
  margin: 0 !important;
}
.first-name >>> .v-input__slot {
  border-top-right-radius: 0 !important;
  border-bottom-right-radius: 0 !important;
}
.last-name >>> .v-input__slot {
  border-top-left-radius: 0 !important;
  border-bottom-left-radius: 0 !important;
}
</style>