This is an automated email from the ASF dual-hosted git repository.

machristie pushed a commit to branch AIRAVATA-3562
in repository https://gitbox.apache.org/repos/asf/airavata-django-portal.git

commit 0f94300ee1e3dc64efe013b3674e0ff350dfc657
Author: Marcus Christie <machris...@apache.org>
AuthorDate: Fri Jun 3 15:38:55 2022 -0400

    AIRAVATA-3564 Adding validation to extended user profile fields
---
 .../users/ExtendedUserProfileContainer.vue         |  11 +-
 .../ExtendedUserProfileFieldEditor.vue             | 168 +++++++++++++++------
 2 files changed, 130 insertions(+), 49 deletions(-)

diff --git 
a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfileContainer.vue
 
b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfileContainer.vue
index 66524084..e346c3cd 100644
--- 
a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfileContainer.vue
+++ 
b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/ExtendedUserProfileContainer.vue
@@ -14,6 +14,8 @@
         <div class="col">
           <extended-user-profile-field-editor
             :extendedUserProfileField="field"
+            @valid="recordValidChildComponent(field)"
+            @invalid="recordInvalidChildComponent(field)"
           />
         </div>
       </div>
@@ -36,7 +38,9 @@
     </div>
     <div class="row mt-4">
       <div class="col">
-        <b-button variant="primary" @click="save">Save</b-button>
+        <b-button variant="primary" @click="save" :disabled="!valid"
+          >Save</b-button
+        >
       </div>
     </div>
   </div>
@@ -45,7 +49,9 @@
 <script>
 import { mapActions, mapGetters } from "vuex";
 import ExtendedUserProfileFieldEditor from 
"./field-editors/ExtendedUserProfileFieldEditor.vue";
+import { mixins } from "django-airavata-common-ui";
 export default {
+  mixins: [mixins.ValidationParent],
   components: { ExtendedUserProfileFieldEditor },
   data() {
     return {};
@@ -105,6 +111,9 @@ export default {
   },
   computed: {
     ...mapGetters("extendedUserProfile", ["extendedUserProfileFields"]),
+    valid() {
+      return this.childComponentsAreValid;
+    },
   },
 };
 </script>
diff --git 
a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/field-editors/ExtendedUserProfileFieldEditor.vue
 
b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/field-editors/ExtendedUserProfileFieldEditor.vue
index e09a8d36..1e8f45ff 100644
--- 
a/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/field-editors/ExtendedUserProfileFieldEditor.vue
+++ 
b/django_airavata/apps/admin/static/django_airavata_admin/src/components/users/field-editors/ExtendedUserProfileFieldEditor.vue
@@ -1,7 +1,10 @@
 <template>
   <b-card :title="title">
     <b-form-group label="Name">
-      <b-form-input v-model="name" />
+      <b-form-input v-model="name" :state="validateState($v.name)" />
+      <b-form-invalid-feedback :state="validateState($v.name)"
+        >This field is required.</b-form-invalid-feedback
+      >
     </b-form-group>
     <b-form-group label="Help text">
       <b-form-input v-model="help_text" />
@@ -11,41 +14,56 @@
     </b-form-group>
     <b-card title="Options" v-if="extendedUserProfileField.supportsChoices">
       <transition-group name="fade">
-        <template v-for="(choice, index) in extendedUserProfileField.choices">
-          <b-input-group :key="choice.key">
-            <b-form-input
-              :value="choice.display_text"
-              @input="handleChoiceDisplayTextChanged(choice, $event)"
-            />
-            <b-input-group-append>
-              <b-button
-                @click="handleChoiceMoveUp(choice)"
-                :disabled="index === 0"
-                v-b-tooltip.hover.left
-                title="Move Up"
-              >
-                <i class="fa fa-arrow-up" aria-hidden="true"></i>
-              </b-button>
-              <b-button
-                @click="handleChoiceMoveDown(choice)"
-                :disabled="
-                  index === extendedUserProfileField.choices.length - 1
+        <template
+          v-for="({ $model: choice, display_text: $v_display_text },
+          index) in $v.choices.$each.$iter"
+        >
+          <b-form-group :key="choice.key">
+            <b-input-group>
+              <b-form-input
+                :value="choice.display_text"
+                @input="
+                  handleChoiceDisplayTextChanged(
+                    choice,
+                    $event,
+                    $v_display_text
+                  )
                 "
-                v-b-tooltip.hover.left
-                title="Move Down"
-              >
-                <i class="fa fa-arrow-down" aria-hidden="true"></i>
-              </b-button>
-              <b-button
-                @click="handleChoiceDeleted(choice)"
-                variant="danger"
-                v-b-tooltip.hover.left
-                title="Delete Option"
-              >
-                <i class="fa fa-trash" aria-hidden="true"></i>
-              </b-button>
-            </b-input-group-append>
-          </b-input-group>
+                :state="validateState($v_display_text)"
+              />
+              <b-input-group-append>
+                <b-button
+                  @click="handleChoiceMoveUp(choice)"
+                  :disabled="index === 0"
+                  v-b-tooltip.hover.left
+                  title="Move Up"
+                >
+                  <i class="fa fa-arrow-up" aria-hidden="true"></i>
+                </b-button>
+                <b-button
+                  @click="handleChoiceMoveDown(choice)"
+                  :disabled="
+                    index === extendedUserProfileField.choices.length - 1
+                  "
+                  v-b-tooltip.hover.left
+                  title="Move Down"
+                >
+                  <i class="fa fa-arrow-down" aria-hidden="true"></i>
+                </b-button>
+                <b-button
+                  @click="handleChoiceDeleted(choice)"
+                  variant="danger"
+                  v-b-tooltip.hover.left
+                  title="Delete Option"
+                >
+                  <i class="fa fa-trash" aria-hidden="true"></i>
+                </b-button>
+              </b-input-group-append>
+            </b-input-group>
+            <b-form-invalid-feedback :state="validateState($v_display_text)"
+              >This field is required.</b-form-invalid-feedback
+            >
+          </b-form-group>
         </template>
         <b-input-group :key="'other'" v-if="extendedUserProfileField.other">
           <b-form-input placeholder="Please specify" disabled />
@@ -77,29 +95,33 @@
       </b-form-checkbox>
     </b-card>
 
-    <template
-      v-if="
-        extendedUserProfileField.links &&
-        extendedUserProfileField.links.length > 0
-      "
-    >
+    <template v-if="links && links.length > 0">
       <transition-group name="fade">
         <b-card
           :title="`Link: ${link.label}`"
-          v-for="link in extendedUserProfileField.links"
+          v-for="{ $model: link, label: $v_label, url: $v_url } in $v.links
+            .$each.$iter"
           :key="link.key"
         >
           <b-form-group label="Label">
             <b-form-input
               :value="link.label"
-              @input="handleLinkLabelChanged(link, $event)"
+              @input="handleLinkLabelChanged(link, $event, $v_label)"
+              :state="validateState($v_label)"
             />
+            <b-form-invalid-feedback :state="validateState($v_label)"
+              >This field is required.</b-form-invalid-feedback
+            >
           </b-form-group>
           <b-form-group label="URL">
             <b-form-input
               :value="link.url"
-              @input="handleLinkURLChanged(link, $event)"
+              @input="handleLinkURLChanged(link, $event, $v_url)"
+              :state="validateState($v_url)"
             />
+            <b-form-invalid-feedback :state="validateState($v_url)"
+              >This field is required.</b-form-invalid-feedback
+            >
           </b-form-group>
           <b-form-group label="Show as link?">
             <b-form-checkbox
@@ -142,8 +164,12 @@
 </template>
 
 <script>
-import { mapActions, mapGetters, mapMutations } from "vuex";
+import { mapGetters, mapMutations } from "vuex";
+import { validationMixin } from "vuelidate";
+import { required } from "vuelidate/lib/validators";
+import { errors } from "django-airavata-common-ui";
 export default {
+  mixins: [validationMixin],
   props: ["extendedUserProfileField"],
   computed: {
     ...mapGetters("extendedUserProfile", ["extendedUserProfileFields"]),
@@ -153,6 +179,7 @@ export default {
       },
       set(value) {
         this.setName({ value, field: this.extendedUserProfileField });
+        this.$v.name.$touch();
       },
     },
     help_text: {
@@ -190,6 +217,39 @@ export default {
         this.name
       }`;
     },
+    choices() {
+      return this.extendedUserProfileField.choices;
+    },
+    links() {
+      return this.extendedUserProfileField.links;
+    },
+    valid() {
+      return !this.$v.$invalid;
+    },
+  },
+  validations() {
+    return {
+      name: {
+        required,
+      },
+      choices: {
+        $each: {
+          display_text: {
+            required,
+          },
+        },
+      },
+      links: {
+        $each: {
+          label: {
+            required,
+          },
+          url: {
+            required,
+          },
+        },
+      },
+    };
   },
   methods: {
     ...mapMutations("extendedUserProfile", [
@@ -210,8 +270,9 @@ export default {
       "updateFieldIndex",
       "deleteField",
     ]),
-    handleChoiceDisplayTextChanged(choice, display_text) {
+    handleChoiceDisplayTextChanged(choice, display_text, $v) {
       this.updateChoiceDisplayText({ choice, display_text });
+      $v.$touch();
     },
     handleChoiceDeleted(choice) {
       this.deleteChoice({ field: this.extendedUserProfileField, choice });
@@ -234,11 +295,13 @@ export default {
         index,
       });
     },
-    handleLinkLabelChanged(link, label) {
+    handleLinkLabelChanged(link, label, $v) {
       this.updateLinkLabel({ link, label });
+      $v.$touch();
     },
-    handleLinkURLChanged(link, url) {
+    handleLinkURLChanged(link, url, $v) {
       this.updateLinkURL({ link, url });
+      $v.$touch();
     },
     handleLinkDisplayLinkChanged(link, display_link) {
       this.updateLinkDisplayLink({ link, display_link });
@@ -264,6 +327,15 @@ export default {
         field: this.extendedUserProfileField,
       });
     },
+    validateState: errors.vuelidateHelpers.validateState,
+  },
+  watch: {
+    valid: {
+      handler(valid) {
+        this.$emit(valid ? "valid" : "invalid");
+      },
+      immediate: true,
+    },
   },
 };
 </script>

Reply via email to