Repository: jhipster/jhipster-vuejs (ts)

Issue:
Improve Vuex Store
##### **Overview of the feature request**

<!-- Explain the feature request -->
All state of our application is contained inside one big object in the `config.ts` file. However, as our application grows in scale, the store can get really bloated.

##### **Motivation for or Use Case**

<!-- Explain why this new feature is important for you -->
To help with that, Vuex allows us to divide our store into modules. Each module can contain its own state, mutations, actions, getters, and even nested modules

##### **What I propose**

I propose to create 3 modules:
- `alert-store.ts`
- `account-store.ts`
- `translation-store.ts`

This configuration could also be put outside the file `config.ts`

-   [x] Checking this box is mandatory (this is just to show you read everything)

<!-- Love JHipster? Please consider supporting our collective:
👉  https://opencollective.com/generator-jhipster/donate -->


Pull request:
improve vuex store
split config.ts store in three modules
see https://vuex.vuejs.org/guide/modules.html

fix #491

-   Please make sure the below checklist is followed for Pull Requests.

-   [ ] [Azure tests](https://dev.azure.com/jhipster/jhipster-vuejs/_build) are green
-   [x] Tests are added where necessary
-   [x] Documentation is added/updated where necessary
-   [x] Coding Rules & Commit Guidelines as per our [CONTRIBUTING.md document](https://github.com/jhipster/jhipster-vuejs/blob/master/CONTRIBUTING.md) are followed

<!--
Please also reference the issue number in a commit message to [automatically close the related Github issue](https://help.github.com/articles/closing-issues-via-commit-messages/)

Note: It is also possible to add `[skip ci]` to your commit message to skip Travis tests
-->


Patch:
diff --git a/generators/client/files.js b/generators/client/files.js
index 85b5fea..63d663a 100644
--- a/generators/client/files.js
+++ b/generators/client/files.js
@@ -132,6 +132,8 @@ const vueFiles = {
                 'shared/config/axios-interceptor.ts',
                 'shared/config/config.ts',
                 'shared/config/config-bootstrap-vue.ts',
+                'shared/config/store/account-store.ts',
+                'shared/config/store/alert-store.ts',
                 'router/index.ts'
             ]
         }
@@ -142,7 +144,8 @@ const vueFiles = {
             path: VUE_DIR,
             templates: [
                 'locale/translation.service.ts',
-                'shared/config/formatter.ts'
+                'shared/config/formatter.ts',
+                'shared/config/store/translation-store.ts',
             ]
         }
     ],
diff --git a/generators/client/templates/vue/src/main/webapp/app/shared/config/config.ts.ejs b/generators/client/templates/vue/src/main/webapp/app/shared/config/config.ts.ejs
index a1a2e8e..e13bd69 100644
--- a/generators/client/templates/vue/src/main/webapp/app/shared/config/config.ts.ejs
+++ b/generators/client/templates/vue/src/main/webapp/app/shared/config/config.ts.ejs
@@ -44,6 +44,11 @@ import Vuelidate from 'vuelidate';
 import Vue2Filters from 'vue2-filters';
 
 import * as filters from '@/shared/date/filters';
+import { accountStore } from '@/shared/config/store/account-store';
+import { alertStore } from '@/shared/config/store/alert-store';
+<%_ if (enableTranslation) { _%>
+import { translationStore } from '@/shared/config/store/translation-store';
+<%_ } _%>
 
 export function initVueApp(vue) {
   vue.use(VueCookie);
@@ -103,81 +108,12 @@ export function initI18N(vue) {
 export function initVueXStore(vue) {
   vue.use(Vuex);
   return new Vuex.Store({
-    state: {
-      dismissSecs: 0,
-      dismissCountDown: 0,
-      alertType: '',
-      alertMessage: {},
-      logon: false,
-      userIdentity: null,
-      authenticated: false,
-      ribbonOnProfiles: '',
-      activeProfiles: '',
+    modules: {
+      accountStore,
+      alertStore,
       <%_ if (enableTranslation) { _%>
-      currentLanguage: localStorage.getItem("currentLanguage") || '<%= nativeLanguage %>',
-      languages: {
-        // jhipster-needle-i18n-language-key-pipe - JHipster will add/remove languages in this object
-      }
+      translationStore,
       <%_ } _%>
     },
-    mutations: {
-      initAlert(state) {
-        state.dismissSecs = 0;
-        state.dismissCountDown = 0;
-        state.alertType = '';
-        state.alertMessage = {};
-      },
-      setAlertType(state, alertType) {
-        state.alertType = alertType;
-      },
-      setAlertMessage(state, alertMessage) {
-        state.dismissSecs = 5;
-        state.dismissCountDown = 5;
-        state.alertMessage = alertMessage;
-      },
-      countDownChanged(state, newCountDown) {
-        state.dismissCountDown = newCountDown;
-      },
-      <%_ if (enableTranslation) { _%>
-      currentLanguage(state, newLanguage) {
-        state.currentLanguage = newLanguage;
-        localStorage.setItem("currentLanguage", newLanguage);
-      },
-      <%_ } _%>
-      authenticate(state) {
-        state.logon = true;
-      },
-      authenticated(state, identity) {
-        state.userIdentity = identity;
-        state.authenticated = true;
-        state.logon = false;
-      },
-      logout(state) {
-        state.userIdentity = null;
-        state.authenticated = false;
-        state.logon = false;
-      },
-      setActiveProfiles(state, profile) {
-        state.activeProfiles = profile;
-      },
-      setRibbonOnProfiles(state, ribbon) {
-        state.ribbonOnProfiles = ribbon;
-      }
-    },
-    getters: {
-      dismissSecs: state => state.dismissSecs,
-      dismissCountDown: state => state.dismissCountDown,
-      alertType: state => state.alertType,
-      alertMessage: state => state.alertMessage,
-      <%_ if (enableTranslation) { _%>
-      currentLanguage: state => state.currentLanguage,
-      languages: state => state.languages,
-      <%_ } _%>
-      logon: state => state.logon,
-      account: state => state.userIdentity,
-      authenticated: state => state.authenticated,
-      activeProfiles: state => state.activeProfiles,
-      ribbonOnProfiles: state => state.ribbonOnProfiles
-    }
   });
 }
diff --git a/generators/client/templates/vue/src/main/webapp/app/shared/config/store/account-store.ts.ejs b/generators/client/templates/vue/src/main/webapp/app/shared/config/store/account-store.ts.ejs
new file mode 100644
index 0000000..85e89f0
--- /dev/null
+++ b/generators/client/templates/vue/src/main/webapp/app/shared/config/store/account-store.ts.ejs
@@ -0,0 +1,39 @@
+import { Module } from 'vuex';
+
+export const accountStore: Module<any, any> = {
+    state: {
+        logon: false,
+        userIdentity: null,
+        authenticated: false,
+        ribbonOnProfiles: '',
+        activeProfiles: '',
+    },
+    getters: {
+        logon: state => state.logon,
+        account: state => state.userIdentity,
+        authenticated: state => state.authenticated,
+        activeProfiles: state => state.activeProfiles,
+        ribbonOnProfiles: state => state.ribbonOnProfiles,
+    },
+    mutations: {
+        authenticate(state) {
+            state.logon = true;
+        },
+        authenticated(state, identity) {
+            state.userIdentity = identity;
+            state.authenticated = true;
+            state.logon = false;
+        },
+        logout(state) {
+            state.userIdentity = null;
+            state.authenticated = false;
+            state.logon = false;
+        },
+        setActiveProfiles(state, profile) {
+            state.activeProfiles = profile;
+        },
+        setRibbonOnProfiles(state, ribbon) {
+            state.ribbonOnProfiles = ribbon;
+        },
+    },
+};
diff --git a/generators/client/templates/vue/src/main/webapp/app/shared/config/store/alert-store.ts.ejs b/generators/client/templates/vue/src/main/webapp/app/shared/config/store/alert-store.ts.ejs
new file mode 100644
index 0000000..ad65abe
--- /dev/null
+++ b/generators/client/templates/vue/src/main/webapp/app/shared/config/store/alert-store.ts.ejs
@@ -0,0 +1,35 @@
+import { Module } from 'vuex';
+
+export const alertStore: Module<any, any> = {
+    state: {
+        dismissSecs: 0,
+        dismissCountDown: 0,
+        alertType: '',
+        alertMessage: {},
+    },
+    getters: {
+        dismissSecs: state => state.dismissSecs,
+        dismissCountDown: state => state.dismissCountDown,
+        alertType: state => state.alertType,
+        alertMessage: state => state.alertMessage,
+    },
+    mutations: {
+        initAlert(state) {
+            state.dismissSecs = 0;
+            state.dismissCountDown = 0;
+            state.alertType = '';
+            state.alertMessage = {};
+        },
+        setAlertType(state, alertType) {
+            state.alertType = alertType;
+        },
+        setAlertMessage(state, alertMessage) {
+            state.dismissSecs = 5;
+            state.dismissCountDown = 5;
+            state.alertMessage = alertMessage;
+        },
+        countDownChanged(state, newCountDown) {
+            state.dismissCountDown = newCountDown;
+        },
+    },
+};
diff --git a/generators/client/templates/vue/src/main/webapp/app/shared/config/store/translation-store.ts.ejs b/generators/client/templates/vue/src/main/webapp/app/shared/config/store/translation-store.ts.ejs
new file mode 100644
index 0000000..fc9a15a
--- /dev/null
+++ b/generators/client/templates/vue/src/main/webapp/app/shared/config/store/translation-store.ts.ejs
@@ -0,0 +1,20 @@
+import { Module } from 'vuex';
+
+export const translationStore: Module<any, any> = {
+    state: {
+        currentLanguage: localStorage.getItem("currentLanguage") || '<%= nativeLanguage %>',
+        languages: {
+            // jhipster-needle-i18n-language-key-pipe - JHipster will add/remove languages in this object
+        }
+    },
+    getters: {
+        currentLanguage: state => state.currentLanguage,
+        languages: state => state.languages,
+    },
+    mutations: {
+        currentLanguage(state, newLanguage) {
+            state.currentLanguage = newLanguage;
+            localStorage.setItem("currentLanguage", newLanguage);
+        },
+    },
+};
diff --git a/generators/languages/index.js b/generators/languages/index.js
index c9c95ce..26c34e8 100644
--- a/generators/languages/index.js
+++ b/generators/languages/index.js
@@ -45,7 +45,7 @@ module.exports = class extends LanguagesGenerator {
         const phaseFromJHipster = super._writing();
         const customPhaseSteps = {
             write() {
-                utils.updateLanguagesInConfig(this);
+                utils.updateLanguagesInTranslationStore(this);
                 utils.updateLanguagesInWebpack(this);
             }
         };
diff --git a/generators/utils.js b/generators/utils.js
index 80fcabe..1d2341e 100644
--- a/generators/utils.js
+++ b/generators/utils.js
@@ -25,7 +25,7 @@ const CLIENT_WEBPACK_DIR = constants.CLIENT_WEBPACK_DIR;
 const CLIENT_TEST_SRC_DIR = constants.CLIENT_TEST_SRC_DIR;
 
 module.exports = {
-    updateLanguagesInConfig,
+    updateLanguagesInTranslationStore,
     updateLanguagesInWebpack,
     replaceTranslation,
     addEntityToMenu,
@@ -40,8 +40,8 @@ module.exports = {
     addPageProtractorConf
 };
 
-function updateLanguagesInConfig(generator) {
-    const fullPath = `${CLIENT_MAIN_SRC_DIR}app/shared/config/config.ts`;
+function updateLanguagesInTranslationStore(generator) {
+    const fullPath = `${CLIENT_MAIN_SRC_DIR}app/shared/config/store/translation-store.ts`;
     try {
         let content = 'languages: {\n';
         if (generator.enableTranslation) {


Test patch:
diff --git a/test/generator.spec.js b/test/generator.spec.js
index 7a4cc9f..826e848 100644
--- a/test/generator.spec.js
+++ b/test/generator.spec.js
@@ -13,6 +13,7 @@ const CLIENT_WEBPACK_DIR = constants.CLIENT_WEBPACK_DIR;
 const expectedFiles = {
     i18n: [
         `${CLIENT_MAIN_SRC_DIR}app/locale/translation.service.ts`,
+        `${CLIENT_MAIN_SRC_DIR}app/shared/config/store/translation-store.ts`,
         `${CLIENT_MAIN_SRC_DIR}i18n/en/activate.json`,
         `${CLIENT_MAIN_SRC_DIR}i18n/en/audits.json`,
         `${CLIENT_MAIN_SRC_DIR}i18n/en/configuration.json`,
@@ -168,6 +169,8 @@ const expectedFiles = {
         `${CLIENT_MAIN_SRC_DIR}app/shared/date/filters.ts`,
         `${CLIENT_MAIN_SRC_DIR}app/shared/config/config.ts`,
         `${CLIENT_MAIN_SRC_DIR}app/shared/config/config-bootstrap-vue.ts`,
+        `${CLIENT_MAIN_SRC_DIR}app/shared/config/store/account-store.ts`,
+        `${CLIENT_MAIN_SRC_DIR}app/shared/config/store/alert-store.ts`,
         `${CLIENT_MAIN_SRC_DIR}app/shared/jhi-item-count.vue`,
         `${CLIENT_MAIN_SRC_DIR}app/shared/jhi-item-count.component.ts`,
         `${CLIENT_MAIN_SRC_DIR}app/shared/model/user.model.ts`,
diff --git a/test/languages.spec.js b/test/languages.spec.js
index 1a17bf4..ce23c90 100644
--- a/test/languages.spec.js
+++ b/test/languages.spec.js
@@ -1,5 +1,4 @@
 const path = require('path');
-const fse = require('fs-extra');
 const assert = require('yeoman-assert');
 const helpers = require('yeoman-test');
 const constants = require('generator-jhipster/generators/generator-constants');
@@ -10,33 +9,47 @@ const CLIENT_WEBPACK_DIR = constants.CLIENT_WEBPACK_DIR;
 
 describe('Subgenerator languages of Vue.js JHipster blueprint', () => {
     context('Creates default i18n files', () => {
+        before((done) => {
+            helpers
+                .run('generator-jhipster/generators/app')
+                .withOptions({
+                    'from-cli': true,
+                    skipInstall: true,
+                    blueprint: 'vuejs',
+                    skipChecks: true
+                })
+                .withGenerators([
+                    [
+                        require('../generators/client/index.js'), // eslint-disable-line global-require
+                        'jhipster-vuejs:client',
+                        path.join(__dirname, '../generators/client/index.js')
+                    ],
+                    [
+                        require('../generators/languages/index.js'), // eslint-disable-line global-require
+                        'jhipster-vuejs:languages',
+                        path.join(__dirname, '../generators/languages/index.js')
+                    ],
+                ])
+                .withPrompts({
+                    baseName: 'sampleMysql',
+                    packageName: 'com.mycompany.myapp',
+                    applicationType: 'monolith',
+                    databaseType: 'sql',
+                    devDatabaseType: 'h2Disk',
+                    prodDatabaseType: 'mysql',
+                    cacheProvider: 'ehcache',
+                    authenticationType: 'jwt',
+                    enableTranslation: true,
+                    nativeLanguage: 'en',
+                    languages: constants.LANGUAGES.map(el => el.value),
+                    buildTool: 'maven',
+                    clientFramework: 'Vue.js',
+                    clientTheme: 'none'
+                })
+                .on('end', done);
+        });
         constants.LANGUAGES.forEach((language) => {
             describe(`for ${language.name}`, () => {
-                before((done) => {
-                    helpers
-                        .run('generator-jhipster/generators/languages')
-                        .inTmpDir((dir) => {
-                            fse.copySync(path.join(__dirname, '../test/templates/vuejs-default'), dir);
-                        })
-                        .withOptions({
-                            'from-cli': true,
-                            skipInstall: true,
-                            blueprint: 'vuejs',
-                            skipChecks: true
-                        })
-                        .withGenerators([
-                            [
-                                require('../generators/languages/index.js'), // eslint-disable-line global-require
-                                'jhipster-vuejs:languages',
-                                path.join(__dirname, '../generators/languages/index.js')
-                            ]
-                        ])
-                        .withPrompts({
-                            languages: [language.value]
-                        })
-                        .on('end', done);
-                });
-
                 it('creates expected files', () => {
                     assert.file([
                         `${CLIENT_MAIN_SRC_DIR}i18n/${language.value}/activate.json`,
@@ -75,10 +88,11 @@ describe('Subgenerator languages of Vue.js JHipster blueprint', () => {
                         '"jhipster-needle-menu-add-admin-element": "JHipster will add additional menu entries here (do not translate!)"'
                     );
                 });
-                it('add language into config.ts, webpack.common.js', () => {
+                it('add language into translation-store.ts, webpack.common.js', () => {
+                    const langKey = language.value.includes('-') ? `'${language.value}'` : `${language.value}`;
                     assert.fileContent(
-                        `${CLIENT_MAIN_SRC_DIR}app/shared/config/config.ts`,
-                        `'${language.value}': { name:`
+                        `${CLIENT_MAIN_SRC_DIR}app/shared/config/store/translation-store.ts`,
+                        `${langKey}: { name:`,
                     );
                     assert.fileContent(
                         `${CLIENT_WEBPACK_DIR}webpack.common.js`,