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


The following commit(s) were added to refs/heads/AIRAVATA-3562 by this push:
     new 71774e3a AIRAVATA-3565 Better handling of extended user profile 
validation
71774e3a is described below

commit 71774e3af3bdb4d65c82c5dd597fa3f169e11afc
Author: Marcus Christie <[email protected]>
AuthorDate: Tue May 17 10:44:16 2022 -0400

    AIRAVATA-3565 Better handling of extended user profile validation
---
 .../js/components/ExtendedUserProfileEditor.vue    | 11 +++++---
 .../components/ExtendedUserProfileFieldEditor.vue  |  8 ++++++
 .../ExtendedUserProfileMultiChoiceFieldEditor.vue  | 11 ++++++++
 .../ExtendedUserProfileSingleChoiceFieldEditor.vue | 11 ++++++++
 .../ExtendedUserProfileTextFieldEditor.vue         | 11 ++++++++
 ...ExtendedUserProfileUserAgreementFieldEditor.vue | 11 ++++++++
 .../js/containers/UserProfileContainer.vue         |  9 +------
 django_airavata/static/common/js/index.js          |  2 ++
 .../static/common/js/mixins/ValidationParent.js    | 30 ++++++++++++++++++++++
 9 files changed, 93 insertions(+), 11 deletions(-)

diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue
index d1a6df0d..0b0b08d8 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileEditor.vue
@@ -6,6 +6,8 @@
         :key="extendedUserProfileField.id"
         :is="getEditor(extendedUserProfileField)"
         :extended-user-profile-field="extendedUserProfileField"
+        @valid="recordValidChildComponent(extendedUserProfileField.id)"
+        @invalid="recordInvalidChildComponent(extendedUserProfileField.id)"
       />
     </template>
   </div>
@@ -17,13 +19,13 @@ import ExtendedUserProfileMultiChoiceFieldEditor from 
"./ExtendedUserProfileMult
 import ExtendedUserProfileSingleChoiceFieldEditor from 
"./ExtendedUserProfileSingleChoiceFieldEditor.vue";
 import ExtendedUserProfileTextFieldEditor from 
"./ExtendedUserProfileTextFieldEditor.vue";
 import ExtendedUserProfileUserAgreementFieldEditor from 
"./ExtendedUserProfileUserAgreementFieldEditor.vue";
+import { mixins } from "django-airavata-common-ui";
 export default {
+  mixins: [mixins.ValidationParent],
   computed: {
     ...mapGetters("extendedUserProfile", ["extendedUserProfileFields"]),
     valid() {
-      return this.$refs.extendedUserProfileFieldComponents.every(
-        (c) => c.valid
-      );
+      return this.childComponentsAreValid;
     },
   },
   methods: {
@@ -45,6 +47,9 @@ export default {
         );
       }
     },
+    touch() {
+      this.$refs.extendedUserProfileFieldComponents.forEach((c) => c.touch());
+    },
   },
 };
 </script>
diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileFieldEditor.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileFieldEditor.vue
index 1115ea7e..13b7a664 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileFieldEditor.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileFieldEditor.vue
@@ -3,6 +3,14 @@
     :label="extendedUserProfileField.name"
     :description="extendedUserProfileField.help_text"
   >
+    <template #label>
+      {{ extendedUserProfileField.name }}
+      <small
+        v-if="!extendedUserProfileField.required"
+        class="text-muted text-small"
+        >(Optional)</small
+      >
+    </template>
     <b-card
       v-for="link in extendedUserProfileField.links"
       :key="link.id"
diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue
index ff02eea7..17faef61 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileMultiChoiceFieldEditor.vue
@@ -136,6 +136,17 @@ export default {
     },
     validateState: errors.vuelidateHelpers.validateState,
     validateStateErrorOnly: errors.vuelidateHelpers.validateStateErrorOnly,
+    touch() {
+      this.$v.$touch();
+    },
+  },
+  watch: {
+    valid: {
+      handler(valid) {
+        this.$emit(valid ? "valid" : "invalid");
+      },
+      immediate: true,
+    },
   },
 };
 </script>
diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue
index 40872081..61c672f5 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileSingleChoiceFieldEditor.vue
@@ -138,6 +138,17 @@ export default {
     },
     validateState: errors.vuelidateHelpers.validateState,
     validateStateErrorOnly: errors.vuelidateHelpers.validateStateErrorOnly,
+    touch() {
+      this.$v.$touch();
+    },
+  },
+  watch: {
+    valid: {
+      handler(valid) {
+        this.$emit(valid ? "valid" : "invalid");
+      },
+      immediate: true,
+    },
   },
 };
 </script>
diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue
index 37b451d7..3e2a8e69 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileTextFieldEditor.vue
@@ -45,6 +45,17 @@ export default {
   methods: {
     ...mapMutations("extendedUserProfile", ["setTextValue"]),
     validateState: errors.vuelidateHelpers.validateState,
+    touch() {
+      this.$v.$touch();
+    },
+  },
+  watch: {
+    valid: {
+      handler(valid) {
+        this.$emit(valid ? "valid" : "invalid");
+      },
+      immediate: true,
+    },
   },
 };
 </script>
diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue
index 487d6b09..d0fe40a4 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/components/ExtendedUserProfileUserAgreementFieldEditor.vue
@@ -65,6 +65,17 @@ export default {
     },
     validateState: errors.vuelidateHelpers.validateState,
     validateStateErrorOnly: errors.vuelidateHelpers.validateStateErrorOnly,
+    touch() {
+      this.$v.$touch();
+    },
+  },
+  watch: {
+    valid: {
+      handler(valid) {
+        this.$emit(valid ? "valid" : "invalid");
+      },
+      immediate: true,
+    },
   },
 };
 </script>
diff --git 
a/django_airavata/apps/auth/static/django_airavata_auth/js/containers/UserProfileContainer.vue
 
b/django_airavata/apps/auth/static/django_airavata_auth/js/containers/UserProfileContainer.vue
index 86fbb395..3e8d101f 100644
--- 
a/django_airavata/apps/auth/static/django_airavata_auth/js/containers/UserProfileContainer.vue
+++ 
b/django_airavata/apps/auth/static/django_airavata_auth/js/containers/UserProfileContainer.vue
@@ -105,14 +105,7 @@ export default {
           })
         );
       } else {
-        // TODO: make sure to highlight which fields are invalid
-        notifications.NotificationList.add(
-          new notifications.Notification({
-            type: "WARNING",
-            message: "The form is invalid. Please fix and try again.",
-            duration: 5,
-          })
-        );
+        this.$refs.extendedUserProfileEditor.touch();
       }
     },
     async handleResendEmailVerification() {
diff --git a/django_airavata/static/common/js/index.js 
b/django_airavata/static/common/js/index.js
index b4bcc5ec..68c9719d 100644
--- a/django_airavata/static/common/js/index.js
+++ b/django_airavata/static/common/js/index.js
@@ -31,6 +31,7 @@ import * as vuelidateHelpers from 
"./errors/vuelidateHelpers.js";
 
 import ListLayout from "./layouts/ListLayout.vue";
 
+import ValidationParent from "./mixins/ValidationParent";
 import VModelMixin from "./mixins/VModelMixin";
 
 import Notification from "./notifications/Notification";
@@ -80,6 +81,7 @@ const layouts = {
 };
 
 const mixins = {
+  ValidationParent,
   VModelMixin,
 };
 
diff --git a/django_airavata/static/common/js/mixins/ValidationParent.js 
b/django_airavata/static/common/js/mixins/ValidationParent.js
new file mode 100644
index 00000000..6e89563d
--- /dev/null
+++ b/django_airavata/static/common/js/mixins/ValidationParent.js
@@ -0,0 +1,30 @@
+/**
+ * Aggregate validation state of child components. Child components should
+ * dispatch 'valid' and 'invalid' events and component using this mixin should
+ * call recordValidChildComponent or recordInvalidChildComponent, respectively.
+ */
+export default {
+  data: function () {
+    return {
+      invalidChildComponents: [],
+    };
+  },
+  computed: {
+    childComponentsAreValid() {
+      return this.invalidChildComponents.length === 0;
+    },
+  },
+  methods: {
+    recordInvalidChildComponent(childComponentId) {
+      if (!this.invalidChildComponents.includes(childComponentId)) {
+        this.invalidChildComponents.push(childComponentId);
+      }
+    },
+    recordValidChildComponent(childComponentId) {
+      if (this.invalidChildComponents.includes(childComponentId)) {
+        const index = this.invalidChildComponents.indexOf(childComponentId);
+        this.invalidChildComponents.splice(index, 1);
+      }
+    },
+  },
+};

Reply via email to