Repository: jhipster/jhipster-control-center
Pull request #56 (2020-05-09 17:14:43)

Sidebar menu
![Capture d’écran de 2020-05-09 18-49-06](https://user-images.githubusercontent.com/13909948/81479955-d5b45800-9226-11ea-9917-4f6eb30335e2.png)

fix https://github.com/jhipster/jhipster-control-center/issues/48

I add the sidebar and fix other components on the page to adapt the responsive design with the sidebar and add space to the content.
I change the theme to spacelab that corresponds more to JHipster theme.
I add a font-family for JHipster title and make logo in relief :heart_eyes
I delete the hipter image on the home page and put it in the sidebar so we can see this image mark every time :blush:




Patch:
diff --git a/package-lock.json b/package-lock.json
index 4c96702..7f9ccbc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2935,13 +2935,13 @@
       "integrity": "sha512-tbx5cHubwE6e2ZG7nqM3g/FZ5PQEDMWmMGNrCUBVRPHXTJaH7CBDdsLeu3eCh3B1tzAxTnAbtmrzvWEvT2NNEA=="
     },
     "bootstrap-vue": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.2.2.tgz",
-      "integrity": "sha512-jFw9WcPX2MLCMaMNdakXke2Fwtz5o7EeByMJGhzGjiL1TNBr/nz2PolCAFW475+o2t5Tz3MZT152o6zLQXcElw==",
+      "version": "2.13.0",
+      "resolved": "https://registry.npmjs.org/bootstrap-vue/-/bootstrap-vue-2.13.0.tgz",
+      "integrity": "sha512-V/q2tgIY+tDxeBGOwqdPC4jk8CWmpwpSVY/1OVGxg4Jg1Jxcs6gQn7chhpI9KuBrO8diG/Nd6JRtnYwyE3b7Qw==",
       "requires": {
         "@nuxt/opencollective": "^0.3.0",
         "bootstrap": ">=4.4.1 <5.0.0",
-        "popper.js": "^1.16.0",
+        "popper.js": "^1.16.1",
         "portal-vue": "^2.1.7",
         "vue-functional-data-merge": "^3.1.0"
       }
diff --git a/package.json b/package.json
index 0b2f410..de1ac24 100644
--- a/package.json
+++ b/package.json
@@ -13,7 +13,7 @@
     "@fortawesome/vue-fontawesome": "0.1.9",
     "axios": "0.19.0",
     "bootstrap": "4.4.1",
-    "bootstrap-vue": "2.2.2",
+    "bootstrap-vue": "2.13.0",
     "bootswatch": "4.4.1",
     "date-fns": "2.9.0",
     "swagger-ui-dist": "3.24.3",
diff --git a/pom.xml b/pom.xml
index 7a67a9c..5f4eea4 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,13 +10,6 @@
     <name>Jhipster Control Center</name>
 
     <repositories>
-        <repository>
-            <id>springfox-snapshots</id>
-            <url>https://oss.jfrog.org/artifactory/oss-snapshot-local/</url>
-            <snapshots>
-                <enabled>true</enabled>
-            </snapshots>
-        </repository>
         <!-- jhipster-needle-maven-repository -->
     </repositories>
 
@@ -79,8 +72,6 @@
         <jacoco.itReportFile>${jacoco.itReportFolder}/integrationTest.exec</jacoco.itReportFile>
         <junit.utReportFolder>${project.testresult.directory}/test</junit.utReportFolder>
         <junit.itReportFolder>${project.testresult.directory}/integrationTest</junit.itReportFolder>
-        <!-- Swagger -->
-        <springfox.version>3.0.0-SNAPSHOT</springfox.version>
         <!-- jhipster-needle-maven-property -->
     </properties>
 
@@ -160,21 +151,6 @@
             <artifactId>h2</artifactId>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-swagger2</artifactId>
-            <version>${springfox.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-swagger-ui</artifactId>
-            <version>${springfox.version}</version>
-        </dependency>
-        <dependency>
-            <groupId>io.springfox</groupId>
-            <artifactId>springfox-spring-webflux</artifactId>
-            <version>${springfox.version}</version>
-        </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
diff --git a/src/main/docker/central-server-config/README.md b/src/main/docker/central-server-config/README.md
deleted file mode 100644
index d3d905f..0000000
--- a/src/main/docker/central-server-config/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Central configuration sources details
-
-The JHipster-Registry will use the following directories as its configuration source :
-
-- localhost-config : when running the registry in docker with the jhipster-registry.yml docker-compose file
-- docker-config : when running the registry and the app both in docker with the app.yml docker-compose file
-
-For more info, refer to https://www.jhipster.tech/jhipster-registry/#spring-cloud-config
-
-When running the consul.yml or app.yml docker-compose files, files located in `central-server-config/`
-will get automatically loaded in Consul's K/V store. Adding or editing files will trigger a reloading.
-
-For more info, refer to https://www.jhipster.tech/consul/
diff --git a/src/main/docker/central-server-config/application.yml b/src/main/docker/central-server-config/application.yml
deleted file mode 100644
index e2398f0..0000000
--- a/src/main/docker/central-server-config/application.yml
+++ /dev/null
@@ -1,9 +0,0 @@
-configserver:
-  name: Docker Consul Service
-  status: Connected to Consul Server running in Docker
-
-jhipster:
-  security:
-    authentication:
-      jwt:
-        secret: my-secret-key-which-should-be-changed-in-production-and-be-base64-encoded
diff --git a/src/main/docker/central-server-config/docker-config/application.yml b/src/main/docker/central-server-config/docker-config/application.yml
deleted file mode 100644
index d276ea6..0000000
--- a/src/main/docker/central-server-config/docker-config/application.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-# Common configuration shared between all applications
-configserver:
-  name: Docker JHipster Registry
-  status: Connected to the JHipster Registry running in Docker
-
-jhipster:
-  security:
-    authentication:
-      jwt:
-        secret: my-secret-key-which-should-be-changed-in-production-and-be-base64-encoded
-
-eureka:
-  client:
-    service-url:
-      defaultZone: http://admin:${jhipster.registry.password}@jhipster-registry:8761/eureka/
diff --git a/src/main/docker/central-server-config/localhost-config/application.yml b/src/main/docker/central-server-config/localhost-config/application.yml
deleted file mode 100644
index 811f6e2..0000000
--- a/src/main/docker/central-server-config/localhost-config/application.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-# Common configuration shared between all applications
-configserver:
-  name: Docker JHipster Registry
-  status: Connected to the JHipster Registry running in Docker
-
-jhipster:
-  security:
-    authentication:
-      jwt:
-        secret: my-secret-key-which-should-be-changed-in-production-and-be-base64-encoded
-
-eureka:
-  client:
-    service-url:
-      defaultZone: http://admin:${jhipster.registry.password}@localhost:8761/eureka/
diff --git a/src/main/docker/consul.yml b/src/main/docker/consul.yml
deleted file mode 100644
index 73329af..0000000
--- a/src/main/docker/consul.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-version: '2'
-services:
-  consul:
-    image: consul:1.7.1
-    ports:
-      - 8300:8300
-      - 8500:8500
-      - 8600:8600
-    command: consul agent -dev -ui -client 0.0.0.0
-
-  consul-config-loader:
-    image: jhipster/consul-config-loader:v0.3.0
-    volumes:
-      - ./central-server-config:/config
-    environment:
-      - INIT_SLEEP_SECONDS=5
-      - CONSUL_URL=consul
-      - CONSUL_PORT=8500
-      # Uncomment to load configuration into Consul from a Git repository
-      # as configured in central-server-config/git2consul.json
-      # Also set SPRING_CLOUD_CONSUL_CONFIG_FORMAT=files on your apps
-      # - CONFIG_MODE=git
diff --git a/src/main/docker/jhipster-registry.yml b/src/main/docker/jhipster-registry.yml
deleted file mode 100644
index d28da1e..0000000
--- a/src/main/docker/jhipster-registry.yml
+++ /dev/null
@@ -1,22 +0,0 @@
-version: '2'
-services:
-  jhipster-registry:
-    image: jhipster/jhipster-registry:v6.1.2
-    volumes:
-      - ./central-server-config:/central-config
-    # When run with the "dev" Spring profile, the JHipster Registry will
-    # read the config from the local filesystem (central-server-config directory)
-    # When run with the "prod" Spring profile, it will read the configuration from a Git repository
-    # See https://www.jhipster.tech/jhipster-registry/#spring-cloud-config
-    environment:
-      - _JAVA_OPTIONS=-Xmx512m -Xms256m
-      - SPRING_PROFILES_ACTIVE=dev,swagger
-      - SPRING_SECURITY_USER_PASSWORD=admin
-      - JHIPSTER_REGISTRY_PASSWORD=admin
-      - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=native
-      - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_LOCATIONS=file:./central-config/localhost-config/
-      # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_TYPE=git
-      # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_URI=https://github.com/jhipster/jhipster-registry/
-      # - SPRING_CLOUD_CONFIG_SERVER_COMPOSITE_0_SEARCH_PATHS=central-config
-    ports:
-      - 8761:8761
diff --git a/src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java b/src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java
index 82d5480..9f85fcb 100644
--- a/src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java
+++ b/src/main/java/tech/jhipster/controlcenter/config/SecurityConfiguration.java
@@ -22,6 +22,7 @@ import org.springframework.security.web.server.util.matcher.NegatedServerWebExch
 import org.springframework.security.web.server.util.matcher.OrServerWebExchangeMatcher;
 import org.springframework.util.StringUtils;
 import org.zalando.problem.spring.webflux.advice.security.SecurityProblemSupport;
+import tech.jhipster.controlcenter.security.AuthoritiesConstants;
 import tech.jhipster.controlcenter.security.jwt.JWTFilter;
 import tech.jhipster.controlcenter.security.jwt.TokenProvider;
 import tech.jhipster.controlcenter.web.filter.SpaWebFilter;
@@ -73,7 +74,7 @@ public class SecurityConfiguration {
                 .authenticationEntryPoint(problemSupport)
         .and()
             .headers()
-                .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self' data:")
+                .contentSecurityPolicy("default-src 'self'; frame-src 'self' data:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https://storage.googleapis.com; style-src 'self' https://fonts.googleapis.com 'unsafe-inline'; img-src 'self' data:; font-src 'self' https://fonts.gstatic.com data:")
             .and()
                 .referrerPolicy(ReferrerPolicyServerHttpHeadersWriter.ReferrerPolicy.STRICT_ORIGIN_WHEN_CROSS_ORIGIN)
             .and()
@@ -87,8 +88,6 @@ public class SecurityConfiguration {
             .pathMatchers("/api/auth-info").permitAll()
             .pathMatchers("/api/authenticate").permitAll()
             .pathMatchers("/api/**").permitAll()
-            .pathMatchers("/swagger-resources/**").permitAll()
-            .pathMatchers("/v2/api-docs").permitAll()
             .pathMatchers("/gateway/**").permitAll()
             .pathMatchers("/management/health").permitAll()
             .pathMatchers("/management/info").permitAll()
diff --git a/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java b/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java
deleted file mode 100644
index 0d34a65..0000000
--- a/src/main/java/tech/jhipster/controlcenter/config/apidoc/SwaggerConfiguration.java
+++ /dev/null
@@ -1,66 +0,0 @@
-package tech.jhipster.controlcenter.config.apidoc;
-
-import io.github.jhipster.config.JHipsterConstants;
-import java.util.ArrayList;
-import java.util.List;
-import org.springframework.cloud.gateway.route.Route;
-import org.springframework.cloud.gateway.route.RouteLocator;
-import org.springframework.context.annotation.Bean;
-import org.springframework.context.annotation.Configuration;
-import org.springframework.context.annotation.Primary;
-import org.springframework.context.annotation.Profile;
-import org.springframework.stereotype.Component;
-import springfox.documentation.builders.PathSelectors;
-import springfox.documentation.builders.RequestHandlerSelectors;
-import springfox.documentation.spi.DocumentationType;
-import springfox.documentation.spring.web.plugins.Docket;
-import springfox.documentation.swagger.web.SwaggerResource;
-import springfox.documentation.swagger.web.SwaggerResourcesProvider;
-import springfox.documentation.swagger2.annotations.EnableSwagger2WebFlux;
-
-/**
- * Retrieves all registered microservices Swagger resources.
- */
-@Component
-@Primary
-@Profile(JHipsterConstants.SPRING_PROFILE_SWAGGER)
-@Configuration
-@EnableSwagger2WebFlux
-public class SwaggerConfiguration implements SwaggerResourcesProvider {
-    private final RouteLocator routeLocator;
-
-    public SwaggerConfiguration(RouteLocator routeLocator) {
-        this.routeLocator = routeLocator;
-    }
-
-    @Bean
-    public Docket api() {
-        return new Docket(DocumentationType.SWAGGER_2).select().apis(RequestHandlerSelectors.any()).paths(PathSelectors.any()).build();
-    }
-
-    @Override
-    public List<SwaggerResource> get() {
-        List<SwaggerResource> resources = new ArrayList<>();
-        resources.add(swaggerResource("jhipster-control-center", "/v2/api-docs"));
-        //Add the registered microservices swagger docs as additional swagger resources
-        List<Route> routes = routeLocator.getRoutes().collectList().block();
-        routes.forEach(
-            route -> {
-                String predicate = route.getPredicate().toString();
-                if (!predicate.contains("consul")) {
-                    String path = predicate.substring(predicate.indexOf("[") + 1, predicate.indexOf("]"));
-                    resources.add(swaggerResource(route.getId(), path.replace("**", "v2/api-docs")));
-                }
-            }
-        );
-        return resources;
-    }
-
-    private SwaggerResource swaggerResource(String name, String location) {
-        SwaggerResource swaggerResource = new SwaggerResource();
-        swaggerResource.setName(name);
-        swaggerResource.setLocation(location);
-        swaggerResource.setSwaggerVersion("2.0");
-        return swaggerResource;
-    }
-}
diff --git a/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java b/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java
deleted file mode 100644
index 874c1e3..0000000
--- a/src/main/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactory.java
+++ /dev/null
@@ -1,40 +0,0 @@
-package tech.jhipster.controlcenter.security.jwt;
-
-import static tech.jhipster.controlcenter.security.jwt.JWTFilter.AUTHORIZATION_HEADER;
-
-import org.springframework.cloud.gateway.filter.GatewayFilter;
-import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
-import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.stereotype.Component;
-import org.springframework.util.StringUtils;
-import tech.jhipster.controlcenter.security.jwt.TokenProvider;
-
-@Component
-public class JWTRelayGatewayFilterFactory extends AbstractGatewayFilterFactory<Object> {
-    private final TokenProvider tokenProvider;
-
-    public JWTRelayGatewayFilterFactory(TokenProvider tokenProvider) {
-        this.tokenProvider = tokenProvider;
-    }
-
-    @Override
-    public GatewayFilter apply(Object config) {
-        return (exchange, chain) -> {
-            String token = this.extractJWTToken(exchange.getRequest());
-            if (StringUtils.hasText(token) && this.tokenProvider.validateToken(token)) {
-                ServerHttpRequest request = exchange.getRequest().mutate().header(AUTHORIZATION_HEADER, "Bearer " + token).build();
-
-                return chain.filter(exchange.mutate().request(request).build());
-            }
-            return chain.filter(exchange);
-        };
-    }
-
-    private String extractJWTToken(ServerHttpRequest request) {
-        String bearerToken = request.getHeaders().getFirst(AUTHORIZATION_HEADER);
-        if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
-            return bearerToken.substring(7);
-        }
-        throw new IllegalArgumentException("Invalid token in Authorization header");
-    }
-}
diff --git a/src/main/resources/config/application-dev.yml b/src/main/resources/config/application-dev.yml
index 8610664..b8b4334 100644
--- a/src/main/resources/config/application-dev.yml
+++ b/src/main/resources/config/application-dev.yml
@@ -67,11 +67,10 @@ jhipster:
     authentication:
       jwt:
         # This token must be encoded using Base64 and be at least 256 bits long (you can type `openssl rand -base64 64` on your command line to generate a 512 bits one)
-        # base64-secret: MDc2YTJkMGVmMTc3ZjQ3MjgwNjVkMDRhYmE1MThmZmNlZGMyMjgzNjgxMTMyZGY3MzlhOTA1Mjk1NzUwODgyOTZhODM1ZTYyZmI5YmJjZWVkNjVhZWM5NDgzMzU2OWZkYzUzNjVkYTNiMTIxZTlhMWRkYjJhMTg2ZWJmMzgxMjQ=
+        base64-secret: MDc2YTJkMGVmMTc3ZjQ3MjgwNjVkMDRhYmE1MThmZmNlZGMyMjgzNjgxMTMyZGY3MzlhOTA1Mjk1NzUwODgyOTZhODM1ZTYyZmI5YmJjZWVkNjVhZWM5NDgzMzU2OWZkYzUzNjVkYTNiMTIxZTlhMWRkYjJhMTg2ZWJmMzgxMjQ=
         # Token is valid 24 hours
         token-validity-in-seconds: 86400
         token-validity-in-seconds-for-remember-me: 2592000
-        secret: my-secret-key-which-should-be-changed-in-production-and-be-base64-encoded
   mail: # specific JHipster mail property, for standard properties see MailProperties
     base-url: http://127.0.0.1:8080
   metrics:
diff --git a/src/main/resources/config/application.yml b/src/main/resources/config/application.yml
index 36511e1..2f43664 100644
--- a/src/main/resources/config/application.yml
+++ b/src/main/resources/config/application.yml
@@ -101,9 +101,6 @@ spring:
     loadbalancer:
       ribbon:
         enabled: false
-    gateway:
-      default-filters:
-        - JWTRelay
 
 server:
   servlet:
diff --git a/src/main/webapp/app/app.component.ts b/src/main/webapp/app/app.component.ts
index 3600c02..7bfc586 100644
--- a/src/main/webapp/app/app.component.ts
+++ b/src/main/webapp/app/app.component.ts
@@ -4,13 +4,14 @@ import Ribbon from '@/core/ribbon/ribbon.vue';
 import JhiFooter from '@/core/jhi-footer/jhi-footer.vue';
 import JhiNavbar from '@/core/jhi-navbar/jhi-navbar.vue';
 import LoginForm from '@/account/login-form/login-form.vue';
+import JhiSidebar from '@/core/jhi-sidebar/jhi-sidebar.vue';
 
 @Component({
   components: {
     ribbon: Ribbon,
     'jhi-navbar': JhiNavbar,
+    'jhi-sidebar': JhiSidebar,
     'login-form': LoginForm,
-
     'jhi-footer': JhiFooter
   }
 })
diff --git a/src/main/webapp/app/app.vue b/src/main/webapp/app/app.vue
index 4230971..16ef98d 100644
--- a/src/main/webapp/app/app.vue
+++ b/src/main/webapp/app/app.vue
@@ -1,20 +1,24 @@
 <template>
   <div id="app">
     <ribbon></ribbon>
-    <div id="app-header">
+    <div id="app-header" class="py-5">
       <jhi-navbar></jhi-navbar>
     </div>
-    <div class="container-fluid">
-      <div class="card jh-card">
-        <router-view></router-view>
-      </div>
-      <b-modal id="login-page" hide-footer lazy>
-        <span slot="modal-title" id="login-title">Sign in</span>
-        <login-form></login-form>
-      </b-modal>
-
-      <jhi-footer></jhi-footer>
-    </div>
+    <b-container fluid>
+      <b-row>
+        <jhi-sidebar></jhi-sidebar>
+        <div class="card jh-card">
+          <router-view></router-view>
+        </div>
+        <b-modal id="login-page" hide-footer lazy>
+          <span slot="modal-title" id="login-title">Sign in</span>
+          <login-form></login-form>
+        </b-modal>
+      </b-row>
+      <b-row>
+        <jhi-footer></jhi-footer>
+      </b-row>
+    </b-container>
   </div>
 </template>
 
diff --git a/src/main/webapp/app/core/home/home.vue b/src/main/webapp/app/core/home/home.vue
index 0cbe0e4..d485248 100644
--- a/src/main/webapp/app/core/home/home.vue
+++ b/src/main/webapp/app/core/home/home.vue
@@ -1,39 +1,35 @@
 <template>
-    <div class="home row">
-        <div class="col-md-3">
-            <span class="hipster img-fluid rounded"></span>
-        </div>
-        <div class="col-md-9">
-            <h1 class="display-4">Welcome, Java Hipster!</h1>
-            <p class="lead">This is your homepage</p>
+    <div>
+        <h1 class="display-4">Welcome, Java Hipster!</h1>
+        <p class="lead">This is your homepage</p>
 
-            <div>
-                <div class="alert alert-success" v-if="authenticated">
-                    <span v-if="username">You are logged in as user "{{username}}"</span>
-                </div>
+        <div>
+            <div class="alert alert-success" v-if="authenticated">
+                <span v-if="username">You are logged in as user "{{ username }}"</span>
+            </div>
 
-                <div class="alert alert-warning" v-if="!authenticated">
-                    <span>If you want to </span>
-                    <a class="alert-link" v-on:click="openLogin()">sign in</a><span >, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").</span>
-                </div>
+            <div class="alert alert-warning" v-if="!authenticated">
+                <span>If you want to </span>
+                <a class="alert-link" v-on:click="openLogin()">sign in</a><span >, you can try the default accounts:<br/>- Administrator (login="admin" and password="admin") <br/>- User (login="user" and password="user").</span>
             </div>
+        </div>
 
-            <p>
-                If you have any question on JHipster:
-            </p>
+        <p>
+            If you have any question on JHipster:
+        </p>
 
-            <ul>
-                <li><a href="https://www.jhipster.tech/" target="_blank" rel="noopener">JHipster homepage</a></li>
-                <li><a href="http://stackoverflow.com/tags/jhipster/info" target="_blank" rel="noopener">JHipster on Stack Overflow</a></li>
-                <li><a href="https://github.com/jhipster/generator-jhipster/issues?state=open" target="_blank" rel="noopener">JHipster bug tracker</a></li>
-                <li><a href="https://gitter.im/jhipster/generator-jhipster" target="_blank" rel="noopener">JHipster public chat room</a></li>
-                <li><a href="https://twitter.com/java_hipster" target="_blank" rel="noopener">follow @java_hipster on Twitter</a></li>
-            </ul>
+        <ul>
+            <li><a href="https://www.jhipster.tech/" target="_blank" rel="noopener">JHipster homepage</a></li>
+            <li><a href="http://stackoverflow.com/tags/jhipster/info" target="_blank" rel="noopener">JHipster on Stack Overflow</a></li>
+            <li><a href="https://github.com/jhipster/generator-jhipster/issues?state=open" target="_blank" rel="noopener">JHipster bug tracker</a></li>
+            <li><a href="https://gitter.im/jhipster/generator-jhipster" target="_blank" rel="noopener">JHipster public chat room</a></li>
+            <li><a href="https://twitter.com/java_hipster" target="_blank" rel="noopener">follow @java_hipster on Twitter</a></li>
+        </ul>
 
-            <p>
-                <span>If you like JHipster, don't forget to give us a star on</span> <a href="https://github.com/jhipster/generator-jhipster" target="_blank" rel="noopener">GitHub</a>!
-            </p>
-        </div>
+        <p>
+            <span>If you like JHipster, don't forget to give us a star on</span>
+            <a href="https://github.com/jhipster/generator-jhipster" target="_blank" rel="noopener">GitHub</a>!
+        </p>
     </div>
 </template>
 
diff --git a/src/main/webapp/app/core/jhi-navbar/jhi-navbar.vue b/src/main/webapp/app/core/jhi-navbar/jhi-navbar.vue
index aa0d6eb..207267d 100644
--- a/src/main/webapp/app/core/jhi-navbar/jhi-navbar.vue
+++ b/src/main/webapp/app/core/jhi-navbar/jhi-navbar.vue
@@ -1,149 +1,191 @@
 <template>
-    <b-navbar toggleable="md" type="dark" class="bg-dark">
-        <div class="jh-logo-container float-left">
-            <b-navbar-toggle right class="jh-navbar-toggler d-lg-none float-right" href="javascript:void(0);"  data-toggle="collapse" target="header-tabs" aria-expanded="false" aria-label="Toggle navigation">
-                <font-awesome-icon icon="bars" />
-            </b-navbar-toggle>
-            <b-navbar-brand class="logo float-left" b-link to="/">
-                <span class="logo-img"></span>
-                <span class="navbar-title">jhipsterControlCenter</span> <span class="navbar-version">{{version}}</span>
-            </b-navbar-brand>
+  <b-navbar toggleable="md" type="dark" class="bg-primary">
+    <b-row>
+      <div class="jh-logo-container" my-auto>
+        <div class="icon-sidebar" id="sidebar-icon" exact v-if="hasAnyAuthority('ROLE_ADMIN')">
+          <div v-b-toggle.sidebar-footer>
+            <b-button variant="outline-primary">
+              <font-awesome-icon class="fa-lg" icon="ellipsis-v" />
+              <font-awesome-icon class="fa-lg" icon="ellipsis-v" />
+            </b-button>
+          </div>
         </div>
-        <b-collapse is-nav id="header-tabs">
-            <b-navbar-nav class="ml-auto">
-                <b-nav-item to="/" exact>
-                    <span>
-                        <font-awesome-icon icon="home" />
-                        <span>Home</span>
-                    </span>
-                </b-nav-item>
-                <b-nav-item-dropdown
-                    id="applications-menu"
-                    v-if="hasAnyAuthority('ROLE_ADMIN')"
-                    :class="{'router-link-active': subIsActive('/applications')}"
-                    active-class="active"
-                    class="pointer">
-                    <span slot="button-content" class="navbar-dropdown-menu">
-                        <font-awesome-icon icon="sitemap" />
-                        <span>Applications</span>
-                    </span>
-                    <b-dropdown-item to="/applications/instances">
-                        <font-awesome-icon icon="cube" />
-                        <span>Instances</span>
-                    </b-dropdown-item>
-                </b-nav-item-dropdown>
-                <b-nav-item-dropdown
-                    id="entity-menu"
-                    v-if="authenticated"
-                    active-class="active" class="pointer">
-                    <span slot="button-content" class="navbar-dropdown-menu">
-                        <font-awesome-icon icon="th-list" />
-                        <span>Entities</span>
-                    </span>
-                    <!-- jhipster-needle-add-entity-to-menu - JHipster will add entities to the menu here -->
-                </b-nav-item-dropdown>
-                <b-nav-item-dropdown
-                    id="admin-menu"
-                    v-if="hasAnyAuthority('ROLE_ADMIN')"
-                    :class="{'router-link-active': subIsActive('/admin')}"
-                    active-class="active"
-                    class="pointer">
-                    <span slot="button-content" class="navbar-dropdown-menu">
-                        <font-awesome-icon icon="user-plus" />
-                        <span>Administration</span>
-                    </span>
-                    <b-dropdown-item  to="/admin/jhi-metrics">
-                        <font-awesome-icon icon="tachometer-alt" />
-                        <span>Metrics</span>
-                    </b-dropdown-item>
-                    <b-dropdown-item to="/admin/jhi-health">
-                        <font-awesome-icon icon="heart" />
-                        <span>Health</span>
-                    </b-dropdown-item>
-                    <b-dropdown-item  to="/admin/jhi-configuration">
-                        <font-awesome-icon icon="list" />
-                        <span>Configuration</span>
-                    </b-dropdown-item>
-                    <b-dropdown-item  to="/admin/logs">
-                        <font-awesome-icon icon="tasks" />
-                        <span>Logs</span>
-                    </b-dropdown-item>
-                    <b-dropdown-item v-if="swaggerEnabled"  to="/admin/docs">
-                        <font-awesome-icon icon="book" />
-                        <span>API</span>
-                    </b-dropdown-item>
-                </b-nav-item-dropdown>
-                <b-nav-item-dropdown
-                    right
-                    href="javascript:void(0);"
-                    id="account-menu"
-                    :class="{'router-link-active': subIsActive('/account')}"
-                    active-class="active"
-                    class="pointer">
-                    <span slot="button-content" class="navbar-dropdown-menu">
-                        <font-awesome-icon icon="user" />
-                        <span>
-                            Account
-                        </span>
-                    </span>
-                    <b-dropdown-item v-if="authenticated"  v-on:click="logout()" id="logout">
-                        <font-awesome-icon icon="sign-out-alt" />
-                        <span>Sign out</span>
-                    </b-dropdown-item>
-                    <b-dropdown-item v-if="!authenticated"  v-on:click="openLogin()" id="login">
-                        <font-awesome-icon icon="sign-in-alt" />
-                        <span>Sign in</span>
-                    </b-dropdown-item>
-                </b-nav-item-dropdown>
-            </b-navbar-nav>
-        </b-collapse>
-    </b-navbar>
+        <b-navbar-brand class="logo" b-link to="/">
+          <span class="logo-img"></span>
+          <span class="navbar-title"><span class="jhipster-title">JHipster</span> Control Center</span>
+          <span class="navbar-version">{{ version }}</span>
+        </b-navbar-brand>
+        <b-navbar-toggle
+          class="header-tabs"
+          right
+          href="javascript:void(0);"
+          target="header-tabs"
+          aria-expanded="false"
+          aria-label="Toggle navigation"
+        >
+          <font-awesome-icon icon="bars" />
+        </b-navbar-toggle>
+      </div>
+    </b-row>
+    <b-collapse is-nav id="header-tabs">
+      <b-navbar-nav class="ml-auto">
+        <b-nav-item to="/" exact>
+          <span>
+            <font-awesome-icon icon="home" />
+            <span>Home</span>
+          </span>
+        </b-nav-item>
+        <b-nav-item-dropdown right id="entity-menu" v-if="authenticated" active-class="active" class="pointer">
+          <span slot="button-content" class="navbar-dropdown-menu">
+            <font-awesome-icon icon="th-list" />
+            <span>Entities</span>
+          </span>
+          <!-- jhipster-needle-add-entity-to-menu - JHipster will add entities to the menu here -->
+        </b-nav-item-dropdown>
+        <b-nav-item-dropdown
+          right
+          id="applications-menu"
+          v-if="hasAnyAuthority('ROLE_ADMIN')"
+          :class="{ 'router-link-active': subIsActive('/applications') }"
+          active-class="active"
+          class="pointer"
+        >
+          <span slot="button-content" class="navbar-dropdown-menu">
+            <font-awesome-icon icon="cogs" />
+            <span>Administration</span>
+          </span>
+          <b-dropdown-item to="/admin/jhi-metrics">
+            <font-awesome-icon icon="tachometer-alt" />
+            <span id="collapse-1">Metrics</span>
+          </b-dropdown-item>
+          <b-dropdown-item to="/admin/jhi-health">
+            <font-awesome-icon icon="heart" />
+            <span>Health</span>
+          </b-dropdown-item>
+          <b-dropdown-item to="/admin/jhi-configuration">
+            <font-awesome-icon icon="list" />
+            <span>Configuration</span>
+          </b-dropdown-item>
+          <b-dropdown-item to="/admin/logs">
+            <font-awesome-icon icon="tasks" />
+            <span>Logs</span>
+          </b-dropdown-item>
+          <b-dropdown-item v-if="swaggerEnabled" to="/admin/docs">
+            <font-awesome-icon icon="book" />
+            <span>API</span>
+          </b-dropdown-item>
+        </b-nav-item-dropdown>
+        <b-nav-item-dropdown
+          right
+          href="javascript:void(0);"
+          id="account-menu"
+          :class="{ 'router-link-active': subIsActive('/account') }"
+          active-class="active"
+          class="pointer"
+        >
+          <span slot="button-content" class="navbar-dropdown-menu">
+            <font-awesome-icon icon="user" />
+            <span>
+              Account
+            </span>
+          </span>
+          <b-dropdown-item v-if="authenticated" v-on:click="logout()" id="logout">
+            <font-awesome-icon icon="sign-out-alt" />
+            <span>Sign out</span>
+          </b-dropdown-item>
+          <b-dropdown-item v-if="!authenticated" v-on:click="openLogin()" id="login">
+            <font-awesome-icon icon="sign-in-alt" />
+            <span>Sign in</span>
+          </b-dropdown-item>
+        </b-nav-item-dropdown>
+      </b-navbar-nav>
+    </b-collapse>
+  </b-navbar>
 </template>
 
-<script lang="ts" src="./jhi-navbar.component.ts">
-</script>
+<script lang="ts" src="./jhi-navbar.component.ts"></script>
 
 <!-- Add "scoped" attribute to limit CSS to this component only -->
 <style scoped>
 /* ==========================================================================
-    Navbar
-    ========================================================================== */
-.navbar-version {
-  font-size: 10px;
-}
+Navbar
+========================================================================== */
 
 /* jhcc-custom */
 .navbar {
+  min-height: 4rem;
   padding: 0.5rem !important;
+  z-index: 1000;
+  position: fixed;
+  top: 0;
+  right: 0;
+  left: 0;
+  justify-content: start;
+}
+
+.navbar-dropdown-menu {
+  right: 0;
+}
+
+/* ==========================================================================
+    Title
+    ========================================================================== */
+
+.navbar-title {
+  display: inline-block;
+  vertical-align: middle;
+  font-family: Pacifico, cursive;
+  font-weight: lighter;
 }
 
+.jhipster-title {
+  font-size: larger;
+}
+
+.navbar-version {
+  font-size: 10px;
+  vertical-align: sub;
+}
+
+/* ==========================================================================
+    @media screen
+    ========================================================================== */
+
 @media screen and (min-width: 768px) {
-  .jh-navbar-toggler {
+  navbar-toggle {
     display: none;
   }
 }
 
 @media screen and (min-width: 768px) and (max-width: 1150px) {
-  span span{
-    display:none;
+  span span {
+    display: none;
   }
 }
-
-@media screen and (max-width: 767px) {
+@media screen and (max-width: 768px) {
   .jh-logo-container {
     width: 100%;
   }
 }
 
-.navbar-title {
-  display: inline-block;
-  vertical-align: middle;
+/* ==========================================================================
+    button & icon
+    ========================================================================== */
+
+.navbar-dark button {
+  color: white;
 }
-/* waiting for bootstrap fix bug on nav-item-dropdown a:active
-https://github.com/bootstrap-vue/bootstrap-vue/issues/2219
-*/
-nav li.router-link-active .navbar-dropdown-menu {
-  cursor: pointer;
+
+.header-tabs {
+  position: absolute;
+  right: 1rem;
+  line-height: 1.5;
+  margin-top: 10px;
+}
+
+.icon-sidebar {
+  display: inline-block;
+  margin-left: 10px;
 }
 
 /* ==========================================================================
@@ -162,9 +204,9 @@ nav li.router-link-active .navbar-dropdown-menu {
 
 .logo-img {
   height: 100%;
-  background: url("../../../content/images/logo-jhipster.png") no-repeat center
-    center;
+  background: url('../../../content/images/logo-jhipster.png') no-repeat center center;
   background-size: contain;
   width: 100%;
+  filter: drop-shadow(0 0 0.05rem white);
 }
 </style>
diff --git a/src/main/webapp/app/core/jhi-sidebar/jhi-sidebar.component.ts b/src/main/webapp/app/core/jhi-sidebar/jhi-sidebar.component.ts
new file mode 100644
index 0000000..fd3d420
--- /dev/null
+++ b/src/main/webapp/app/core/jhi-sidebar/jhi-sidebar.component.ts
@@ -0,0 +1,18 @@
+import { Component, Inject, Vue } from 'vue-property-decorator';
+import AccountService from '@/account/account.service';
+
+@Component
+export default class VueSidebarMenu extends Vue {
+  @Inject('accountService')
+  private accountService: () => AccountService;
+
+  created() {}
+
+  public hasAnyAuthority(authorities: any): boolean {
+    return this.accountService().hasAnyAuthority(authorities);
+  }
+
+  public get swaggerEnabled(): boolean {
+    return this.$store.getters.activeProfiles.indexOf('swagger') > -1;
+  }
+}
diff --git a/src/main/webapp/app/core/jhi-sidebar/jhi-sidebar.vue b/src/main/webapp/app/core/jhi-sidebar/jhi-sidebar.vue
new file mode 100644
index 0000000..6ab97d5
--- /dev/null
+++ b/src/main/webapp/app/core/jhi-sidebar/jhi-sidebar.vue
@@ -0,0 +1,65 @@
+<template>
+  <div class="sidebar" v-if="hasAnyAuthority('ROLE_ADMIN')">
+    <b-sidebar bg-variant="dark" text-variant="light" id="sidebar-footer" no-close-on-route-change no-close-on-esc visible>
+      <template v-slot:footer="{ hide }">
+        <div class="d-flex text-light align-items-center">
+          <b-button block variant="primary" @click="hide">
+            <font-awesome-icon icon="angle-double-left" />
+          </b-button>
+        </div>
+      </template>
+      <div class="px-3">
+        <b-nav vertical text-variant="light" aria-label="Sidebar" no-header>
+          <b-nav-item to="/applications/instances">
+            <font-awesome-icon icon="cube" />
+            <span>Instances</span>
+          </b-nav-item>
+          <b-nav-item none disabled>
+            <font-awesome-icon icon="chart-line" />
+            <span>DashBoard</span>
+          </b-nav-item>
+          <b-nav-item to="/admin/jhi-metrics">
+            <font-awesome-icon icon="tachometer-alt" />
+            <span>Metrics</span>
+          </b-nav-item>
+          <b-nav-item to="/admin/jhi-health">
+            <font-awesome-icon icon="heart" />
+            <span>Health</span>
+          </b-nav-item>
+          <b-nav-item to="/admin/jhi-configuration">
+            <font-awesome-icon icon="list" />
+            <span>Configuration</span>
+          </b-nav-item>
+          <b-nav-item to="/admin/logs">
+            <font-awesome-icon icon="tasks" />
+            <span>Logs</span>
+          </b-nav-item>
+          <b-nav-item v-if="swaggerEnabled" to="/admin/docs">
+            <font-awesome-icon icon="book" />
+            <span>API</span>
+          </b-nav-item>
+        </b-nav>
+        <span class="fixed-bottom hipster img-fluid"></span>
+      </div>
+    </b-sidebar>
+  </div>
+</template>
+
+<script lang="ts" src="./jhi-sidebar.component.ts"></script>
+
+<!-- Add "scoped" attribute to limit CSS to this component only -->
+<style scoped>
+@media screen and (max-width: 1150px) {
+  span {
+    display: none;
+  }
+  .hipster {
+    display: none !important;
+  }
+}
+
+.btn-block {
+  border-radius: 0;
+}
+
+</style>
diff --git a/src/main/webapp/app/shared/config/config-bootstrap-vue.ts b/src/main/webapp/app/shared/config/config-bootstrap-vue.ts
index 7e59b23..10e1b8c 100644
--- a/src/main/webapp/app/shared/config/config-bootstrap-vue.ts
+++ b/src/main/webapp/app/shared/config/config-bootstrap-vue.ts
@@ -3,10 +3,12 @@ import {
   BFormInput,
   BFormCheckbox,
   BFormGroup,
+  BImg,
   BProgress,
   BProgressBar,
   BPagination,
   BButton,
+  BNav,
   BNavbar,
   BNavbarNav,
   BNavbarBrand,
@@ -20,7 +22,14 @@ import {
   BLink,
   BAlert,
   BModal,
-  VBModal
+  VBModal,
+  BSidebar,
+  VBToggle,
+  BFormSelect,
+  BCard,
+  BContainer,
+  BRow,
+  BCol
 } from 'bootstrap-vue';
 
 export function initBootstrapVue(vue) {
@@ -47,4 +56,13 @@ export function initBootstrapVue(vue) {
   vue.component('b-nav-item-dropdown', BNavItemDropdown);
   vue.component('b-modal', BModal);
   vue.directive('b-modal', VBModal);
+  vue.directive('bToggle', VBToggle);
+  vue.component('b-sidebar', BSidebar);
+  vue.component('b-img', BImg);
+  vue.component('b-nav', BNav);
+  vue.component('b-select', BFormSelect);
+  vue.component('b-card', BCard);
+  vue.component('b-container', BContainer);
+  vue.component('b-row', BRow);
+  vue.component('b-col', BCol);
 }
diff --git a/src/main/webapp/app/shared/config/config.ts b/src/main/webapp/app/shared/config/config.ts
index 9d6085b..563639e 100644
--- a/src/main/webapp/app/shared/config/config.ts
+++ b/src/main/webapp/app/shared/config/config.ts
@@ -38,6 +38,10 @@ import { faUser } from '@fortawesome/free-solid-svg-icons/faUser';
 import { faWrench } from '@fortawesome/free-solid-svg-icons/faWrench';
 import { faSitemap } from '@fortawesome/free-solid-svg-icons/faSitemap';
 import { faCube } from '@fortawesome/free-solid-svg-icons/faCube';
+import { faChartLine } from '@fortawesome/free-solid-svg-icons/faChartLine';
+import { faAngleDoubleLeft } from '@fortawesome/free-solid-svg-icons/faAngleDoubleLeft';
+import { faCogs } from '@fortawesome/free-solid-svg-icons/faCogs';
+import { faEllipsisV } from '@fortawesome/free-solid-svg-icons/faEllipsisV';
 
 import VueCookie from 'vue-cookie';
 import Vuelidate from 'vuelidate';
@@ -59,12 +63,14 @@ export function initVueApp(vue) {
 export function initFortAwesome(vue) {
   library.add(
     faArrowLeft,
+    faAngleDoubleLeft,
     faAsterisk,
     faBan,
     faBars,
     faBell,
     faBook,
     faCloud,
+    faChartLine,
     faEye,
     faFlag,
     faHdd,
@@ -93,7 +99,9 @@ export function initFortAwesome(vue) {
     faUserPlus,
     faWrench,
     faSitemap,
-    faCube
+    faCube,
+    faCogs,
+    faEllipsisV
   );
 }
 
diff --git a/src/main/webapp/content/scss/global.scss b/src/main/webapp/content/scss/global.scss
index 349d7dc..57af202 100644
--- a/src/main/webapp/content/scss/global.scss
+++ b/src/main/webapp/content/scss/global.scss
@@ -49,9 +49,9 @@ Main page styles
 
 .hipster.img-fluid {
   display: inline-block;
-  width: 347px;
+  width: 250px;
   height: 497px;
-  background: url('../images/jhipster_family_member_3.svg') no-repeat center top;
+  background: url('../images/jhipster_family_member_3.svg') no-repeat center center;
   background-size: contain;
 }
 
@@ -84,11 +84,30 @@ Generic styles
 
 /* other generic styles */
 
+.app {
+  height: 100%;
+  position: relative;
+  padding-bottom: 0px;
+}
+
 .jh-card {
   padding: 1.5%;
-  margin-top: 20px;
   border: none;
   background: inherit;
+  margin-left: 200px;
+  width: 100%;
+}
+
+@media screen and (max-width: 767px) {
+  .jh-card {
+    margin-left: 20px;
+  }
+}
+
+@media screen and (min-width: 768px) and (max-width: 1150px) {
+  .jh-card {
+    margin-left: 90px;
+  }
 }
 
 .error {
@@ -118,15 +137,23 @@ Generic styles
   opacity: 1;
 }
 
-.footer {
-  border-top: 1px solid rgba(0, 0, 0, 0.125);
-}
-
 .hand,
 [jhisortby] {
   cursor: pointer;
 }
 
+/* ==========================================================================
+App footer
+========================================================================== */
+
+.footer {
+  border-top: 1px solid rgba(0, 0, 0, 0.125);
+  bottom: 0;
+  width: 99%;
+  height: 60px;
+  display: block;
+}
+
 /* ==========================================================================
 Custom alerts for notification
 ========================================================================== */
@@ -254,4 +281,55 @@ ui bootstrap tweaks
   white-space: nowrap;
 }
 
+@import url('https://fonts.googleapis.com/css2?family=Open+Sans&family=Pacifico&display=swap');
+
+/* ==========================================================================
+Sidebar
+========================================================================== */
+
+.b-sidebar {
+  padding-top: 70px;
+  width: auto;
+  .b-sidebar-body {
+    height: calc(100% - 70px);
+    overflow: hidden;
+  }
+}
+
+.b-sidebar-outer {
+  z-index: 900;
+}
+
+.b-sidebar .btn {
+  margin-left: auto;
+  margin-right: auto;
+}
+
+.b-sidebar {
+  .nav-link {
+    color: rgba(255, 255, 255, 0.75);
+    &:hover {
+      color: white;
+    }
+    &:focus {
+      color: white;
+    }
+  }
+  .disabled {
+    color: rgb(119, 119, 119);
+  }
+  .none {
+    cursor: none;
+  }
+}
+
+.b-sidebar-body .hipster.img-fluid {
+  display: inline-block;
+  width: 150px;
+  height: 497px;
+  background: url('../images/jhipster_family_member_3.svg') no-repeat center bottom;
+  background-size: contain;
+  margin-bottom: 30px;
+}
+
 /* jhipster-needle-scss-add-main JHipster will add new css style */
diff --git a/src/main/webapp/content/scss/vendor.scss b/src/main/webapp/content/scss/vendor.scss
index 801f17e..560f820 100644
--- a/src/main/webapp/content/scss/vendor.scss
+++ b/src/main/webapp/content/scss/vendor.scss
@@ -6,13 +6,9 @@ eg $input-color: red;
 ****************************/
 // Override Boostrap variables
 // Import Bootstrap source files from node_modules
-@import '~bootswatch/dist/pulse/variables';
+@import '~bootswatch/dist/spacelab/variables';
 @import '~bootstrap/scss/bootstrap';
 @import '~bootstrap-vue/src/index.scss';
-@import '~bootswatch/dist/pulse/bootswatch';
-/* Dirty fix on navbar active link with bootswatch theme */
-nav li.router-link-active .navbar-dropdown-menu {
-  color: $navbar-dark-active-color;
-}
+@import '~bootswatch/dist/spacelab/bootswatch';
 
 /* jhipster-needle-scss-add-vendor JHipster will add new css style */
diff --git a/src/main/webapp/swagger-ui/index.html b/src/main/webapp/swagger-ui/index.html
index 0b5ddce..beb02fb 100644
--- a/src/main/webapp/swagger-ui/index.html
+++ b/src/main/webapp/swagger-ui/index.html
@@ -26,6 +26,11 @@
                 urls.push({"name": resource.name, "url": resource.location});
             });
 
+            urls.sort(function (a, b) {
+                var x = a.name.toLowerCase(), y = b.name.toLowerCase();
+                return x < y ? -1 : x > y ? 1 : 0;
+            });
+
             // Build a system
             var ui = SwaggerUIBundle({
                 urls: urls,
diff --git a/webpack/webpack.prod.js b/webpack/webpack.prod.js
index 40e342f..d97fdbb 100644
--- a/webpack/webpack.prod.js
+++ b/webpack/webpack.prod.js
@@ -68,7 +68,6 @@ const webpackConfig = merge(baseWebpackConfig, {
     new HtmlWebpackPlugin({
       template: './src/main/webapp/index.html',
       chunks: ['vendors', 'main', 'global'],
-      // chunksSortMode: 'manual',
       inject: true,
       minify: {
         removeComments: true,
@@ -78,7 +77,7 @@ const webpackConfig = merge(baseWebpackConfig, {
         // https://github.com/kangax/html-minifier#options-quick-reference
       },
       // necessary to consistently work with multiple chunks via CommonsChunkPlugin
-      chunksSortMode: 'dependency'
+      chunksSortMode: 'manual'
     }),
     // keep module.id stable when vendor modules does not change
     new webpack.HashedModuleIdsPlugin()


Test patch:
diff --git a/src/test/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactoryTest.java b/src/test/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactoryTest.java
deleted file mode 100644
index e1529e6..0000000
--- a/src/test/java/tech/jhipster/controlcenter/security/jwt/JWTRelayGatewayFilterFactoryTest.java
+++ /dev/null
@@ -1,113 +0,0 @@
-package tech.jhipster.controlcenter.security.jwt;
-
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertThrows;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.when;
-
-import io.github.jhipster.config.JHipsterProperties;
-import io.jsonwebtoken.io.Decoders;
-import io.jsonwebtoken.security.Keys;
-import java.util.Collections;
-import org.junit.jupiter.api.Assertions;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-import org.mockito.ArgumentCaptor;
-import org.springframework.cloud.gateway.filter.GatewayFilter;
-import org.springframework.cloud.gateway.filter.GatewayFilterChain;
-import org.springframework.http.server.reactive.ServerHttpRequest;
-import org.springframework.mock.http.server.reactive.MockServerHttpRequest;
-import org.springframework.mock.web.server.MockServerWebExchange;
-import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
-import org.springframework.security.core.authority.SimpleGrantedAuthority;
-import org.springframework.test.util.ReflectionTestUtils;
-import org.springframework.web.server.ServerWebExchange;
-import reactor.core.publisher.Mono;
-import tech.jhipster.controlcenter.security.AuthoritiesConstants;
-import tech.jhipster.controlcenter.security.jwt.JWTFilter;
-import tech.jhipster.controlcenter.security.jwt.JWTRelayGatewayFilterFactory;
-import tech.jhipster.controlcenter.security.jwt.TokenProvider;
-
-public class JWTRelayGatewayFilterFactoryTest {
-    private final TokenProvider tokenProvider;
-    private final GatewayFilterChain filterChain = mock(GatewayFilterChain.class);
-    private final ArgumentCaptor<ServerWebExchange> captor = ArgumentCaptor.forClass(ServerWebExchange.class);
-    public static final String AUTHORIZATION_HEADER = "Authorization";
-
-    JWTRelayGatewayFilterFactoryTest() {
-        JHipsterProperties jHipsterProperties = new JHipsterProperties();
-        tokenProvider = new TokenProvider(jHipsterProperties);
-        ReflectionTestUtils.setField(
-            tokenProvider,
-            "key",
-            Keys.hmacShaKeyFor(
-                Decoders.BASE64.decode("fd54a45s65fds737b9aafcb3412e07ed99b267f33413274720ddbb7f6c5e64e9f14075f2d7ed041592f0b7657baf8")
-            )
-        );
-
-        ReflectionTestUtils.setField(tokenProvider, "tokenValidityInMilliseconds", 60000);
-    }
-
-    @BeforeEach
-    void setup() {
-        when(filterChain.filter(captor.capture())).thenReturn(Mono.empty());
-    }
-
-    @Test
-    void applyFilterTest() {
-        // create authentication
-        UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(
-            "test-user",
-            "test-password",
-            Collections.singletonList(new SimpleGrantedAuthority(AuthoritiesConstants.USER))
-        );
-
-        // dummy api url to filter
-        String sample_url = "/gateway/service-id/service-id:number/api/test";
-
-        // create request with jwt in header
-        String jwt = tokenProvider.createToken(authentication, false);
-        MockServerHttpRequest request = MockServerHttpRequest
-            .get(sample_url)
-            .header(JWTFilter.AUTHORIZATION_HEADER, "Bearer " + jwt)
-            .build();
-
-        // apply the filter to the request
-        ServerWebExchange exchange = MockServerWebExchange.from(request);
-        JWTRelayGatewayFilterFactory factory = new JWTRelayGatewayFilterFactory(tokenProvider);
-        Config config = new Config();
-        GatewayFilter filter = factory.apply(config);
-
-        filter.filter(exchange, filterChain);
-        // replace by the commented next line for reactive methods
-        // filter.filter(exchange, filterChain).block();
-
-        // intercept the request after filter
-        ServerHttpRequest requestAfterFilter = captor.getValue().getRequest();
-
-        assertEquals(request.getHeaders().getFirst(AUTHORIZATION_HEADER), requestAfterFilter.getHeaders().getFirst(AUTHORIZATION_HEADER));
-    }
-
-    @Test
-    void applyFilterWithWrongTokenTest() {
-        String sample_url = "/gateway/service-id/service-id:number/api/test";
-        String jwt = "wrong-token";
-        MockServerHttpRequest request = MockServerHttpRequest.get(sample_url).header(JWTFilter.AUTHORIZATION_HEADER, jwt).build();
-
-        Throwable exceptionThrown = assertThrows(
-            IllegalArgumentException.class,
-            () -> {
-                // apply the filter to the request
-                ServerWebExchange exchange = MockServerWebExchange.from(request);
-                JWTRelayGatewayFilterFactory factory = new JWTRelayGatewayFilterFactory(tokenProvider);
-                Config config = new Config();
-                GatewayFilter filter = factory.apply(config);
-                filter.filter(exchange, filterChain);
-            }
-        );
-
-        assertEquals(exceptionThrown.getMessage(), "Invalid token in Authorization header");
-    }
-
-    public static class Config {}
-}
diff --git a/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts b/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts
index 02189d1..c04e5ea 100644
--- a/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts
+++ b/src/test/javascript/spec/app/core/jhi-navbar/jhi-navbar.component.spec.ts
@@ -17,6 +17,7 @@ localVue.component('b-nav-item-dropdown', {});
 localVue.component('b-navbar-toggle', {});
 localVue.component('b-navbar-brand', {});
 localVue.component('b-navbar-nav', {});
+localVue.component('b-row', {});
 
 describe('JhiNavbar', () => {
   let jhiNavbar: JhiNavbarClass;
diff --git a/src/test/javascript/spec/app/core/jhi-sidebar/jhi-sidebar.component.spec.ts b/src/test/javascript/spec/app/core/jhi-sidebar/jhi-sidebar.component.spec.ts
new file mode 100644
index 0000000..259e402
--- /dev/null
+++ b/src/test/javascript/spec/app/core/jhi-sidebar/jhi-sidebar.component.spec.ts
@@ -0,0 +1,50 @@
+import { createLocalVue, shallowMount, Wrapper } from '@vue/test-utils';
+import JhiSidebar from '@/core/jhi-sidebar/jhi-sidebar.vue';
+import JhiSidebarClass from '@/core/jhi-sidebar/jhi-sidebar.component';
+import * as config from '@/shared/config/config';
+import router from '@/router';
+
+const localVue = createLocalVue();
+config.initVueApp(localVue);
+const store = config.initVueXStore(localVue);
+localVue.component('b-sidebar', {});
+localVue.component('b-nav', {});
+localVue.component('b-nav-item', {});
+
+describe('JhiSidebar', () => {
+  let jhiSidebar: JhiSidebarClass;
+  let wrapper: Wrapper<JhiSidebarClass>;
+  const accountService = { hasAnyAuthority: jest.fn() };
+
+  beforeEach(() => {
+    wrapper = shallowMount<JhiSidebarClass>(JhiSidebar, {
+      store,
+      router,
+      localVue,
+      provide: {
+        accountService: () => accountService
+      }
+    });
+    jhiSidebar = wrapper.vm;
+  });
+
+  it('should be a Vue instance', () => {
+    expect(wrapper.isVueInstance()).toBeTruthy();
+  });
+
+  it('should not have user data set', () => {
+    expect(jhiSidebar.swaggerEnabled).toBeFalsy();
+  });
+
+  it('should have profile info set after info retrieved', () => {
+    store.commit('setActiveProfiles', ['prod', 'swagger']);
+
+    expect(jhiSidebar.swaggerEnabled).toBeTruthy();
+  });
+
+  it('should use account service', () => {
+    jhiSidebar.hasAnyAuthority('auth');
+
+    expect(accountService.hasAnyAuthority).toHaveBeenCalled();
+  });
+});