Repository: jhipster/generator-jhipster (ts)

Issue:
Frontend Enums should be global
##### **Overview of the feature request**
If I have this:
```
enum CommonFieldValues{
    A, B, C, D
}

entity Something {
    commonFieldValue CommonFieldValues
}

entity OtherEntity {
    commonFieldValue CommonFieldValues
}
```

Then the enum `CommonFieldValues` will be created twice, once in the class in their respective model.

As it is an enum used in different classes with the exact same values, this should be handled the same way is being done in the backend.

##### **Motivation for or Use Case**
There are times when I need to compare the same enum from 2 classes and it doesn't work as they are treated as different enums.

Plus, this will reduce the amount of code :)
##### **Related issues or PR**

None

-   [x] Checking this box is mandatory (this is just to show you read everything)

Pull request description:
feature(enums): make frontend enums global
Close #9920 

-   Please make sure the below checklist is followed for Pull Requests.

-   [ ] [Travis tests](https://travis-ci.org/jhipster/generator-jhipster/pull_requests) are green
-   [ ] Tests are added where necessary
-   [ ] Documentation is added/updated where necessary
-   [ ] Coding Rules & Commit Guidelines as per our [CONTRIBUTING.md document](https://github.com/jhipster/generator-jhipster/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/entity-client/files.js b/generators/entity-client/files.js
index 5c71041c08..d12e351907 100644
--- a/generators/entity-client/files.js
+++ b/generators/entity-client/files.js
@@ -16,6 +16,8 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+const _ = require('lodash');
+const utils = require('../utils');
 const constants = require('../generator-constants');
 
 /* Constants use throughout */
@@ -224,6 +226,26 @@ module.exports = {
     reactFiles
 };
 
+function addEnumerationFiles(generator, templateDir, clientFolder) {
+    generator.fields.forEach(field => {
+        if (field.fieldIsEnum === true) {
+            const enumFileName = _.kebabCase(field.fieldType);
+            const enumInfo = utils.buildEnumInfo(field, generator.angularAppName, generator.packageName, generator.clientRootFolder);
+            if (!generator.skipClient) {
+                generator.template(
+                    `${generator.fetchFromInstalledJHipster(
+                        `entity-client/templates/${templateDir}`
+                    )}/${clientFolder}entities/enumerations/enum.model.ts.ejs`,
+                    `${clientFolder}shared/model/enumerations/${enumFileName}.model.ts`,
+                    generator,
+                    {},
+                    enumInfo
+                );
+            }
+        }
+    });
+}
+
 function writeFiles() {
     return {
         writeClientFiles() {
@@ -247,6 +269,8 @@ function writeFiles() {
                     this.clientFramework,
                     this.microserviceName
                 );
+
+                addEnumerationFiles(this, CLIENT_NG2_TEMPLATES_DIR, ANGULAR_DIR);
             } else if (this.clientFramework === 'react') {
                 // write client side files for react
                 this.writeFilesToDisk(
@@ -264,6 +288,8 @@ function writeFiles() {
                     this.entityUrl,
                     this.clientFramework
                 );
+
+                addEnumerationFiles(this, CLIENT_REACT_TEMPLATES_DIR, REACT_DIR);
             }
             this.addEntityToMenu(this.entityStateName, this.enableTranslation, this.clientFramework, this.entityTranslationKeyMenu);
         }
diff --git a/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity.model.ts.ejs b/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity.model.ts.ejs
index 300b8aeb84..2aedfac8de 100644
--- a/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity.model.ts.ejs
+++ b/generators/entity-client/templates/angular/src/main/webapp/app/entities/entity.model.ts.ejs
@@ -20,6 +20,7 @@
 const variablesWithTypes = generateEntityClientFields(pkType, fields, relationships, dto);
 const typeImports = generateEntityClientImports(relationships, dto);
 const defaultVariablesValues = generateEntityClientFieldDefaultValues(fields);
+const enumImports = generateEntityClientEnumImports(fields);
 %>
 <%_ if (fieldsContainInstant || fieldsContainZonedDateTime || fieldsContainLocalDate) { _%>
 import { Moment } from 'moment';
@@ -27,20 +28,10 @@ import { Moment } from 'moment';
 <%_ typeImports.forEach( (importedPath, importedType) => { _%>
 import { <%- importedType %> } from '<%- importedPath %>';
 <%_ }); _%>
+<%_ enumImports.forEach( (importedPath, importedType) => { _%>
+import { <%- importedType %> } from '<%- importedPath %>';
+<%_ }); _%>
 
-<%_ const enumsAlreadyDeclared = [];
-fields.forEach(field => {
-    if (field.fieldIsEnum && !enumsAlreadyDeclared.includes(field.fieldType)) {
-        enumsAlreadyDeclared.push(field.fieldType); _%>
-export const enum <%= field.fieldType %> {<%
-        const enums = field.fieldValues.split(',');
-        for (let i = 0; i < enums.length; i++) { %>
-    <%= enums[i] %> = '<%= enums[i] %>'<%if (i < enums.length - 1) { %>,<% }
-        } %>
-}
-
-<%_ }
-}); _%>
 export interface I<%= entityAngularName %> {
     <%_ variablesWithTypes.forEach(variablesWithType => { _%>
     <%- variablesWithType %>;
diff --git a/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs b/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
new file mode 100644
index 0000000000..52156ecebc
--- /dev/null
+++ b/generators/entity-client/templates/angular/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
@@ -0,0 +1,23 @@
+<%#
+ Copyright 2013-2019 the original author or authors from the JHipster project.
+
+ This file is part of the JHipster project, see https://www.jhipster.tech/
+ for more information.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-%>
+export const enum <%= enumName %> {<%
+  for (let i = 0; i < enums.length; i++) { %>
+  <%= enums[i] %> = '<%= enums[i] %>'<%if (i < enums.length - 1) { %>,<% }
+  } %>
+}
diff --git a/generators/entity-client/templates/react/src/main/webapp/app/entities/entity.model.ts.ejs b/generators/entity-client/templates/react/src/main/webapp/app/entities/entity.model.ts.ejs
index d58b2bad88..28a4cf4f5e 100644
--- a/generators/entity-client/templates/react/src/main/webapp/app/entities/entity.model.ts.ejs
+++ b/generators/entity-client/templates/react/src/main/webapp/app/entities/entity.model.ts.ejs
@@ -20,6 +20,7 @@
 const variablesWithTypes = generateEntityClientFields(pkType, fields, relationships, dto);
 const typeImports = generateEntityClientImports(relationships, dto);
 const defaultVariablesValues = generateEntityClientFieldDefaultValues(fields, 'react');
+const enumImports = generateEntityClientEnumImports(fields);
 %>
 <%_ if (fieldsContainInstant || fieldsContainZonedDateTime || fieldsContainLocalDate) { _%>
 import { Moment } from 'moment';
@@ -27,20 +28,10 @@ import { Moment } from 'moment';
 <%_ typeImports.forEach( (importedPath, importedType) => { _%>
 import { <%- importedType %> } from '<%- importedPath %>';
 <%_ }); _%>
+<%_ enumImports.forEach( (importedPath, importedType) => { _%>
+import { <%- importedType %> } from '<%- importedPath %>';
+<%_ }); _%>
 
-<%_ const enumsAlreadyDeclared = [];
-fields.forEach(field => {
-    if (field.fieldIsEnum && !enumsAlreadyDeclared.includes(field.fieldType)) {
-        enumsAlreadyDeclared.push(field.fieldType); _%>
-export const enum <%= field.fieldType %> {<%
-        const enums = field.fieldValues.split(',');
-        for (let i = 0; i < enums.length; i++) { %>
-    <%= enums[i] %> = '<%= enums[i] %>'<%if (i < enums.length - 1) { %>,<% }
-        } %>
-}
-
-<%_ }
-}); _%>
 export interface I<%= entityReactName %> {
     <%_ variablesWithTypes.forEach(variablesWithType => { _%>
     <%- variablesWithType %>;
diff --git a/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs b/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
new file mode 100644
index 0000000000..52156ecebc
--- /dev/null
+++ b/generators/entity-client/templates/react/src/main/webapp/app/entities/enumerations/enum.model.ts.ejs
@@ -0,0 +1,23 @@
+<%#
+ Copyright 2013-2019 the original author or authors from the JHipster project.
+
+ This file is part of the JHipster project, see https://www.jhipster.tech/
+ for more information.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+      http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-%>
+export const enum <%= enumName %> {<%
+  for (let i = 0; i < enums.length; i++) { %>
+  <%= enums[i] %> = '<%= enums[i] %>'<%if (i < enums.length - 1) { %>,<% }
+  } %>
+}
diff --git a/generators/generator-base-private.js b/generators/generator-base-private.js
index fc3c10e144..5b5a7aaa22 100644
--- a/generators/generator-base-private.js
+++ b/generators/generator-base-private.js
@@ -1146,6 +1146,28 @@ module.exports = class extends Generator {
         return typeImports;
     }
 
+    /**
+     * Generate Entity Client Enum Imports
+     *
+     * @param {Array|Object} fields - array of the entity fields
+     * @param {string} clientFramework the client framework, 'angularX' or 'react'.
+     * @returns typeImports: Map
+     */
+    generateEntityClientEnumImports(fields, clientFramework = this.clientFramework) {
+        const typeImports = new Map();
+        const uniqueEnums = {};
+        fields.forEach(field => {
+            const fileName = _.kebabCase(field.fieldType);
+            if (field.fieldIsEnum && (!uniqueEnums[field.fieldType] || (uniqueEnums[field.fieldType] && field.fieldValues.length !== 0))) {
+                const importType = `${field.fieldType}`;
+                const importPath = `app/shared/model/enumerations/${fileName}.model`;
+                uniqueEnums[field.fieldType] = field.fieldType;
+                typeImports.set(importType, importPath);
+            }
+        });
+        return typeImports;
+    }
+
     /**
      * Get DB type from DB value
      * @param {string} db - db


Test patch:
diff --git a/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs b/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs
index 88ae661c35..d515d70569 100644
--- a/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs
+++ b/generators/entity-client/templates/angular/src/test/javascript/spec/app/entities/entity-management.service.spec.ts.ejs
@@ -19,6 +19,7 @@
 <%_
 const tsKeyId = generateTestEntityId(pkType, prodDatabaseType);
 const variablesWithTypes = generateEntityClientFields(pkType, fields, relationships, dto);
+const enumImports = generateEntityClientEnumImports(fields);
 _%>
 /* tslint:disable max-line-length */
 import { TestBed, getTestBed } from '@angular/core/testing';
@@ -31,14 +32,10 @@ import * as moment from 'moment';
 import { <%_ if (fieldsContainLocalDate) { _%>DATE_FORMAT,<%_ } if (fieldsContainInstant || fieldsContainZonedDateTime) {_%> DATE_TIME_FORMAT<%_ } _%> } from 'app/shared/constants/input.constants';
 <%_ } _%>
 import { <%= entityAngularName %>Service } from 'app/entities/<%= entityFolderName %>/<%= entityFileName %>.service';
-import { I<%= entityAngularName %>, <%= entityAngularName %>, <%_ const enumsAlreadyDeclared = [];
-fields.forEach(field => {
-if (field.fieldIsEnum && !enumsAlreadyDeclared.includes(field.fieldType)) {
-    enumsAlreadyDeclared.push(field.fieldType); _%>
-<%=field.fieldType%>,
-<%_ }
-}) _%>
-} from 'app/shared/model/<%= entityModelFileName %>.model';
+import { I<%= entityAngularName %>, <%= entityAngularName %> } from 'app/shared/model/<%= entityModelFileName %>.model';
+<%_ enumImports.forEach( (importedPath, importedType) => { _%>
+import { <%- importedType %> } from '<%- importedPath %>';
+<%_ }); _%>
 
 describe('Service Tests', () => {