From b866e8f1560ec3a847107d5bdbe742a35e110be0 Mon Sep 17 00:00:00 2001
From: Bram Wiepjes <bramw@protonmail.com>
Date: Fri, 11 Oct 2019 11:43:24 +0200
Subject: [PATCH] moved the selected group to the group store instead of the
 application

---
 web-frontend/components/group/GroupForm.vue   |  3 +
 .../components/group/GroupsContextItem.vue    | 16 ++---
 .../components/sidebar/ApplicationForm.vue    |  3 +
 web-frontend/components/sidebar/Sidebar.vue   |  4 +-
 .../components/sidebar/SidebarApplication.vue |  4 +-
 web-frontend/middleware/group.js              |  6 +-
 web-frontend/pages/app/index.vue              |  2 +-
 web-frontend/store/application.js             | 53 +++++-----------
 web-frontend/store/auth.js                    |  2 +-
 web-frontend/store/group.js                   | 63 ++++++++++++++++---
 web-frontend/utils/error.js                   |  4 +-
 11 files changed, 94 insertions(+), 66 deletions(-)

diff --git a/web-frontend/components/group/GroupForm.vue b/web-frontend/components/group/GroupForm.vue
index ad46180e6..a5714eead 100644
--- a/web-frontend/components/group/GroupForm.vue
+++ b/web-frontend/components/group/GroupForm.vue
@@ -42,6 +42,9 @@ export default {
     values: {
       name: { required }
     }
+  },
+  mounted() {
+    this.$refs.name.focus()
   }
 }
 </script>
diff --git a/web-frontend/components/group/GroupsContextItem.vue b/web-frontend/components/group/GroupsContextItem.vue
index 4036d7f09..a5dbde24b 100644
--- a/web-frontend/components/group/GroupsContextItem.vue
+++ b/web-frontend/components/group/GroupsContextItem.vue
@@ -2,7 +2,7 @@
   <li
     class="select-item"
     :class="{
-      active: selectedGroup.id == group.id,
+      active: group._.selected,
       'select-item-loading': group._.loading
     }"
   >
@@ -41,8 +41,6 @@
 </template>
 
 <script>
-import { mapState } from 'vuex'
-
 export default {
   name: 'GroupsContextItem',
   props: {
@@ -51,11 +49,6 @@ export default {
       required: true
     }
   },
-  computed: {
-    ...mapState({
-      selectedGroup: state => state.application.selectedGroup
-    })
-  },
   methods: {
     setLoading(group, value) {
       this.$store.dispatch('group/setItemLoading', { group, value: value })
@@ -69,7 +62,7 @@ export default {
 
       this.$store
         .dispatch('group/update', {
-          id: group.id,
+          group,
           values: {
             name: event.value
           }
@@ -85,17 +78,16 @@ export default {
     selectGroup(group) {
       this.setLoading(group, true)
 
-      this.$store.dispatch('application/selectGroup', group).then(() => {
+      this.$store.dispatch('group/select', group).then(() => {
         this.setLoading(group, false)
         this.$emit('selected')
       })
     },
     deleteGroup(group) {
       this.$refs.context.hide()
-      this.$store.dispatch('application/unselectGroup')
       this.setLoading(group, true)
 
-      this.$store.dispatch('group/delete', group.id).then(() => {
+      this.$store.dispatch('group/delete', group).then(() => {
         this.setLoading(group, false)
       })
     }
diff --git a/web-frontend/components/sidebar/ApplicationForm.vue b/web-frontend/components/sidebar/ApplicationForm.vue
index e49a9cc3c..aa7522cbb 100644
--- a/web-frontend/components/sidebar/ApplicationForm.vue
+++ b/web-frontend/components/sidebar/ApplicationForm.vue
@@ -38,6 +38,9 @@ export default {
       }
     }
   },
+  mounted() {
+    this.$refs.name.focus()
+  },
   validations: {
     values: {
       name: { required }
diff --git a/web-frontend/components/sidebar/Sidebar.vue b/web-frontend/components/sidebar/Sidebar.vue
index e3749ec5d..6f9c1185d 100644
--- a/web-frontend/components/sidebar/Sidebar.vue
+++ b/web-frontend/components/sidebar/Sidebar.vue
@@ -43,11 +43,11 @@ export default {
   computed: {
     ...mapState({
       applications: state => state.application.items,
-      selectedGroup: state => state.application.selectedGroup
+      selectedGroup: state => state.group.selected
     }),
     ...mapGetters({
       isLoading: 'application/isLoading',
-      hasSelectedGroup: 'application/hasSelectedGroup'
+      hasSelectedGroup: 'group/hasSelected'
     })
   }
 }
diff --git a/web-frontend/components/sidebar/SidebarApplication.vue b/web-frontend/components/sidebar/SidebarApplication.vue
index a77da8ab9..5d33da4e7 100644
--- a/web-frontend/components/sidebar/SidebarApplication.vue
+++ b/web-frontend/components/sidebar/SidebarApplication.vue
@@ -70,7 +70,7 @@ export default {
 
       this.$store
         .dispatch('application/update', {
-          id: application.id,
+          application,
           values: {
             name: event.value
           }
@@ -87,7 +87,7 @@ export default {
       this.$refs.context.hide()
       this.setLoading(application, true)
 
-      this.$store.dispatch('application/delete', application.id).then(() => {
+      this.$store.dispatch('application/delete', application).then(() => {
         this.setLoading(application, false)
       })
     }
diff --git a/web-frontend/middleware/group.js b/web-frontend/middleware/group.js
index 8f45a39be..4b5ea661b 100644
--- a/web-frontend/middleware/group.js
+++ b/web-frontend/middleware/group.js
@@ -12,12 +12,12 @@ export default function({ store, req, app }) {
   // Get the selected group id
   const groupId = getGroupCookie(app.$cookies)
 
-  // If a group id cookie is set, the user is authenticated and a selectedGroup
+  // If a group id cookie is set, the user is authenticated and a selected group
   // is not already set then we will fetch the groups and select that group.
   if (
     groupId &&
     store.getters['auth/isAuthenticated'] &&
-    !store.getters['application/hasSelectedGroup']
+    !store.getters['group/hasSelected']
   ) {
     return store
       .dispatch('group/fetchAll')
@@ -27,7 +27,7 @@ export default function({ store, req, app }) {
       .then(() => {
         const group = store.getters['group/get'](groupId)
         if (group) {
-          return store.dispatch('application/selectGroup', group)
+          return store.dispatch('group/select', group)
         }
       })
   }
diff --git a/web-frontend/pages/app/index.vue b/web-frontend/pages/app/index.vue
index 13ea3bd8d..035ffa406 100644
--- a/web-frontend/pages/app/index.vue
+++ b/web-frontend/pages/app/index.vue
@@ -22,7 +22,7 @@ export default {
     ...mapState({
       user: state => state.auth.user,
       groups: state => state.group.items,
-      selectedGroup: state => state.application.selectedGroup,
+      selectedGroup: state => state.group.selected,
       applications: state => state.application.applications,
       groupApplications: state => state.application.items
     })
diff --git a/web-frontend/store/application.js b/web-frontend/store/application.js
index 1866b8413..905dfa0e2 100644
--- a/web-frontend/store/application.js
+++ b/web-frontend/store/application.js
@@ -1,6 +1,5 @@
 import { Application } from '@/core/applications'
 import ApplicationService from '@/services/application'
-import { setGroupCookie } from '@/utils/group'
 import { notify404, notifyError } from '@/utils/error'
 
 function populateApplication(application, getters) {
@@ -14,17 +13,13 @@ function populateApplication(application, getters) {
 export const state = () => ({
   applications: {},
   loading: false,
-  items: [],
-  selectedGroup: {}
+  items: []
 })
 
 export const mutations = {
   REGISTER(state, application) {
     state.applications[application.type] = application
   },
-  SET_SELECTED_GROUP(state, group) {
-    state.selectedGroup = group
-  },
   SET_ITEMS(state, applications) {
     state.items = applications
   },
@@ -66,18 +61,14 @@ export const actions = {
     commit('SET_ITEM_LOADING', { application, value })
   },
   /**
-   * Choose an existing group. It will fetch all the applications of that group,
-   * sets a cookie so the next time the page loads the group is still selected
-   * and populates each item.
+   * Fetches all the applications of a given group. The is mostly called when
+   * the user selects a different group.
    */
-  selectGroup({ commit, getters, dispatch }, group) {
+  fetchAll({ commit, getters, dispatch }, group) {
     commit('SET_LOADING', true)
 
     return ApplicationService.fetchAll(group.id)
       .then(({ data }) => {
-        commit('SET_SELECTED_GROUP', group)
-        setGroupCookie(group.id, this.app.$cookies)
-
         data.forEach((part, index, d) => {
           populateApplication(data[index], getters)
         })
@@ -89,8 +80,9 @@ export const actions = {
         notify404(
           dispatch,
           error,
-          'Unable to select group',
-          "You're unable to select the group. This could be because you're not part of the group."
+          'Unable to fetch applications',
+          "You're unable to fetch the application of this group. " +
+            "This could be because you're not part of the group."
         )
       })
       .then(() => {
@@ -98,19 +90,17 @@ export const actions = {
       })
   },
   /**
-   * If a selected group is deleted or for example the user logs off the current
-   * group must be unselected which means that all the fetched items will be
-   * forgotten.
+   * Clears all the currently selected applications, this could be called when
+   * the group is deleted of when the user logs off.
    */
-  unselectGroup({ commit }) {
-    commit('SET_SELECTED_GROUP', {})
+  clearAll({ commit }) {
     commit('SET_ITEMS', [])
   },
   /**
    * Creates a new application with the given type and values for the currently
    * selected group.
    */
-  create({ commit, getters, dispatch }, { type, values }) {
+  create({ commit, getters, rootGetters, dispatch }, { type, values }) {
     if (values.hasOwnProperty('type')) {
       throw new Error(
         'The key "type" is a reserved, but is already set on the ' +
@@ -123,7 +113,7 @@ export const actions = {
     }
 
     values.type = type
-    return ApplicationService.create(getters.selectedGroupId, values)
+    return ApplicationService.create(rootGetters['group/selectedId'], values)
       .then(({ data }) => {
         populateApplication(data, getters)
         commit('ADD_ITEM', data)
@@ -141,8 +131,8 @@ export const actions = {
   /**
    * Updates the values of an existing application.
    */
-  update({ commit, dispatch }, { id, values }) {
-    return ApplicationService.update(id, values)
+  update({ commit, dispatch }, { application, values }) {
+    return ApplicationService.update(application.id, values)
       .then(({ data }) => {
         commit('UPDATE_ITEM', data)
       })
@@ -160,10 +150,10 @@ export const actions = {
   /**
    * Deletes an existing application.
    */
-  delete({ commit, dispatch }, id) {
-    return ApplicationService.delete(id)
+  delete({ commit, dispatch }, application) {
+    return ApplicationService.delete(application.id)
       .then(() => {
-        commit('DELETE_ITEM', id)
+        commit('DELETE_ITEM', application.id)
       })
       .catch(error => {
         notifyError(
@@ -179,15 +169,6 @@ export const actions = {
 }
 
 export const getters = {
-  selectedGroupId(state) {
-    return state.selectedGroup.id
-  },
-  hasSelectedGroup(state) {
-    return state.selectedGroup.hasOwnProperty('id')
-  },
-  applications(state) {
-    return state.applications
-  },
   isLoading(state) {
     return state.loading
   },
diff --git a/web-frontend/store/auth.js b/web-frontend/store/auth.js
index 7801eadec..00bff617c 100644
--- a/web-frontend/store/auth.js
+++ b/web-frontend/store/auth.js
@@ -59,7 +59,7 @@ export const actions = {
     unsetGroupCookie(this.app.$cookies)
     commit('CLEAR_USER_DATA')
     dispatch('group/clearAll', {}, { root: true })
-    dispatch('application/unselectGroup', {}, { root: true })
+    dispatch('group/unselect', {}, { root: true })
   },
   /**
    * Refresh the existing token. If successful commit the new token and start a
diff --git a/web-frontend/store/group.js b/web-frontend/store/group.js
index 4d3235e92..4ee74ae17 100644
--- a/web-frontend/store/group.js
+++ b/web-frontend/store/group.js
@@ -1,15 +1,17 @@
 import GroupService from '@/services/group'
 import { notify404 } from '@/utils/error'
+import { setGroupCookie, unsetGroupCookie } from '@/utils/group'
 
 function populateGroup(group) {
-  group._ = { loading: false }
+  group._ = { loading: false, selected: false }
   return group
 }
 
 export const state = () => ({
   loaded: false,
   loading: false,
-  items: []
+  items: [],
+  selected: {}
 })
 
 export const mutations = {
@@ -27,6 +29,9 @@ export const mutations = {
     })
   },
   SET_ITEM_LOADING(state, { group, value }) {
+    if (!group.hasOwnProperty('_')) {
+      return
+    }
     group._.loading = value
   },
   ADD_ITEM(state, item) {
@@ -40,6 +45,19 @@ export const mutations = {
   DELETE_ITEM(state, id) {
     const index = state.items.findIndex(item => item.id === id)
     state.items.splice(index, 1)
+  },
+  SET_SELECTED(state, group) {
+    Object.values(state.items).forEach(item => {
+      item._.selected = false
+    })
+    group._.selected = true
+    state.selected = group
+  },
+  UNSELECT(state) {
+    Object.values(state.items).forEach(item => {
+      item._.selected = false
+    })
+    state.selected = {}
   }
 }
 
@@ -95,8 +113,8 @@ export const actions = {
   /**
    * Updates the values of the group with the provided id.
    */
-  update({ commit, dispatch }, { id, values }) {
-    return GroupService.update(id, values)
+  update({ commit, dispatch }, { group, values }) {
+    return GroupService.update(group.id, values)
       .then(({ data }) => {
         commit('UPDATE_ITEM', data)
       })
@@ -113,10 +131,15 @@ export const actions = {
   /**
    * Deletes an existing group with the provided id.
    */
-  delete({ commit, dispatch }, id) {
-    return GroupService.delete(id)
+  delete({ commit, dispatch }, group) {
+    return GroupService.delete(group.id)
       .then(() => {
-        commit('DELETE_ITEM', id)
+        if (group._.selected) {
+          console.log('calling unselect')
+          dispatch('unselect', group)
+        }
+
+        commit('DELETE_ITEM', group.id)
       })
       .catch(error => {
         notify404(
@@ -127,6 +150,22 @@ export const actions = {
             "you're not part of the group."
         )
       })
+  },
+  /**
+   * Select a group and fetch all the applications related to that group.
+   */
+  select({ commit, dispatch }, group) {
+    commit('SET_SELECTED', group)
+    setGroupCookie(group.id, this.app.$cookies)
+    return dispatch('application/fetchAll', group, { root: true })
+  },
+  /**
+   * Unselect a group if selected and clears all the fetched applications.
+   */
+  unselect({ commit, dispatch, getters }, group) {
+    commit('UNSELECT', {})
+    unsetGroupCookie(this.app.$cookies)
+    return dispatch('application/clearAll', group, { root: true })
   }
 }
 
@@ -139,5 +178,15 @@ export const getters = {
   },
   get: state => id => {
     return state.items.find(item => item.id === id)
+  },
+  hasSelected(state) {
+    return state.selected.hasOwnProperty('id')
+  },
+  selectedId(state) {
+    if (!state.selected.hasOwnProperty('id')) {
+      throw new Error('There is no selected group.')
+    }
+
+    return state.selected.id
   }
 }
diff --git a/web-frontend/utils/error.js b/web-frontend/utils/error.js
index 0ed9dcd44..5eebe08c6 100644
--- a/web-frontend/utils/error.js
+++ b/web-frontend/utils/error.js
@@ -18,8 +18,8 @@ export function notify404(dispatch, error, title, message) {
  * Adds a notification error if the response error is equal to the provided
  * error code.
  */
-export function notifyError(dispatch, error, error_code, title, message) {
-  if (error.responseError === error_code) {
+export function notifyError(dispatch, error, errorCode, title, message) {
+  if (error.responseError === errorCode) {
     dispatch(
       'notification/error',
       {