├── frontend ├── src │ └── README └── styles │ ├── README │ └── shared-styles.css ├── bin ├── frontend │ ├── src │ │ └── README │ └── styles │ │ ├── README │ │ ├── shared-styles.css │ │ └── vaadin-text-field-styles.css ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── jabbour │ │ │ │ └── ems │ │ │ │ ├── MainView.class │ │ │ │ ├── Application.class │ │ │ │ └── GreetService.class │ │ └── resources │ │ │ ├── META-INF │ │ │ └── resources │ │ │ │ └── icons │ │ │ │ └── icon.png │ │ │ ├── application.properties │ │ │ └── banner.txt │ └── test │ │ └── java │ │ └── com │ │ └── jabbour │ │ └── ems │ │ ├── MainViewIT.class │ │ └── AbstractViewTest.class ├── .gitignore ├── LICENSE.md ├── README.md ├── package.json └── pom.xml ├── images ├── my-ems-project.png ├── 2020-08-21 22_18_57-.png ├── EditForm_responsivedesign.png └── Showcase_of_tableview and edit form.png ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src ├── main │ ├── resources │ │ ├── META-INF │ │ │ └── resources │ │ │ │ └── icons │ │ │ │ └── icon.png │ │ ├── application.properties │ │ └── banner.txt │ └── java │ │ └── com │ │ └── jabbour │ │ └── ems │ │ ├── backend │ │ ├── repository │ │ │ ├── DepartmentRepository.java │ │ │ └── EmployeeRepository.java │ │ ├── service │ │ │ ├── DepartmentService.java │ │ │ └── EmployeeService.java │ │ └── entity │ │ │ ├── Department.java │ │ │ ├── AbstractEntity.java │ │ │ └── Employee.java │ │ ├── Application.java │ │ ├── security │ │ ├── CustomRequestCache.java │ │ ├── ConfigureUIServiceInitListener.java │ │ ├── SecurityUtils.java │ │ └── SecurityConfiguration.java │ │ └── ui │ │ ├── MainLayout.java │ │ └── view │ │ ├── login │ │ └── LoginView.java │ │ └── list │ │ ├── EmployeeForm.java │ │ └── ListView.java └── test │ └── java │ └── com │ └── jabbour │ └── ems │ ├── MainViewIT.java │ └── AbstractViewTest.java ├── .gitignore ├── settings.gradle ├── heroku-settings.xml ├── LICENSE ├── webpack.config.js ├── gradlew.bat ├── README.md ├── package.json ├── gradlew └── pom.xml /frontend/src/README: -------------------------------------------------------------------------------- 1 | Place your Vaadin Designer or hand written templates in this folder. 2 | -------------------------------------------------------------------------------- /bin/frontend/src/README: -------------------------------------------------------------------------------- 1 | Place your Vaadin Designer or hand written templates in this folder. 2 | -------------------------------------------------------------------------------- /images/my-ems-project.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/images/my-ems-project.png -------------------------------------------------------------------------------- /images/2020-08-21 22_18_57-.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/images/2020-08-21 22_18_57-.png -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /images/EditForm_responsivedesign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/images/EditForm_responsivedesign.png -------------------------------------------------------------------------------- /images/Showcase_of_tableview and edit form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/images/Showcase_of_tableview and edit form.png -------------------------------------------------------------------------------- /bin/src/main/java/com/jabbour/ems/MainView.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/bin/src/main/java/com/jabbour/ems/MainView.class -------------------------------------------------------------------------------- /bin/src/main/java/com/jabbour/ems/Application.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/bin/src/main/java/com/jabbour/ems/Application.class -------------------------------------------------------------------------------- /bin/src/main/java/com/jabbour/ems/GreetService.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/bin/src/main/java/com/jabbour/ems/GreetService.class -------------------------------------------------------------------------------- /bin/src/test/java/com/jabbour/ems/MainViewIT.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/bin/src/test/java/com/jabbour/ems/MainViewIT.class -------------------------------------------------------------------------------- /src/main/resources/META-INF/resources/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/src/main/resources/META-INF/resources/icons/icon.png -------------------------------------------------------------------------------- /bin/src/main/resources/META-INF/resources/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/bin/src/main/resources/META-INF/resources/icons/icon.png -------------------------------------------------------------------------------- /bin/src/test/java/com/jabbour/ems/AbstractViewTest.class: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChrisJabb21/Employee-Management-System/HEAD/bin/src/test/java/com/jabbour/ems/AbstractViewTest.class -------------------------------------------------------------------------------- /frontend/styles/README: -------------------------------------------------------------------------------- 1 | Place your optional style modules in this folder. 2 | 3 | See https://vaadin.com/docs/flow/theme/theming-overview.html for getting started on theming Vaadin Flow applications. 4 | -------------------------------------------------------------------------------- /bin/frontend/styles/README: -------------------------------------------------------------------------------- 1 | Place your optional style modules in this folder. 2 | 3 | See https://vaadin.com/docs/flow/theme/theming-overview.html for getting started on theming Vaadin Flow applications. 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.6-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/repository/DepartmentRepository.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.repository; 2 | 3 | import org.springframework.data.jpa.repository.JpaRepository; 4 | 5 | import com.jabbour.ems.backend.entity.Department; 6 | 7 | public interface DepartmentRepository extends JpaRepository{ 8 | 9 | } 10 | -------------------------------------------------------------------------------- /bin/frontend/styles/shared-styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | CSS styling examples for the Vaadin app. 3 | Visit https://vaadin.com/docs/flow/theme/theming-overview.html and 4 | https://vaadin.com/themes/lumo for more information. 5 | */ 6 | 7 | /* Example: CSS class name to center align the content . */ 8 | .centered-content { 9 | margin: 0 auto; 10 | max-width: 250px; 11 | } 12 | 13 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .idea/ 3 | .settings 4 | .project 5 | .classpath 6 | 7 | *.iml 8 | .DS_Store 9 | 10 | # The following files are generated/updated by vaadin-maven-plugin 11 | node_modules/ 12 | 13 | # Browser drivers for local integration tests 14 | drivers/ 15 | # Error screenshots generated by TestBench for failed integration tests 16 | error-screenshots/ 17 | webpack.generated.js 18 | -------------------------------------------------------------------------------- /bin/.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | .idea/ 3 | .settings 4 | .project 5 | .classpath 6 | 7 | *.iml 8 | .DS_Store 9 | 10 | # The following files are generated/updated by vaadin-maven-plugin 11 | node_modules/ 12 | 13 | # Browser drivers for local integration tests 14 | drivers/ 15 | # Error screenshots generated by TestBench for failed integration tests 16 | error-screenshots/ 17 | webpack.generated.js 18 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * 6 | * Detailed information about configuring a multi-project build in Gradle can be found 7 | * in the user manual at https://docs.gradle.org/6.6/userguide/multi_project_builds.html 8 | */ 9 | 10 | rootProject.name = 'my-ems-project' 11 | -------------------------------------------------------------------------------- /bin/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT:8080} 2 | # Ensure application is run in Vaadin 14/npm mode 3 | vaadin.compatibilityMode = false 4 | logging.level.org.atmosphere = warn 5 | 6 | # To improve the performance during development. 7 | # For more information https://vaadin.com/docs/v14/flow/spring/tutorial-spring-configuration.html#special-configuration-parameters 8 | # vaadin.whitelisted-packages= org/vaadin/example 9 | -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=${PORT:8080} 2 | # Ensure application is run in Vaadin 14/npm mode 3 | vaadin.compatibilityMode = false 4 | logging.level.org.atmosphere = warn 5 | 6 | # To improve the performance during development. 7 | # For more information https://vaadin.com/docs/v14/flow/spring/tutorial-spring-configuration.html#special-configuration-parameters 8 | # vaadin.whitelisted-packages= org/vaadin/example 9 | -------------------------------------------------------------------------------- /bin/frontend/styles/vaadin-text-field-styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | CSS styling examples for the Vaadin app. 3 | Visit https://vaadin.com/docs/flow/theme/theming-overview.html and 4 | https://vaadin.com/themes/lumo for more information. 5 | */ 6 | 7 | /* Example: the style is applied only to the textfields which has the `bordered` theme attribute. */ 8 | :host([theme~="bordered"]) [part="input-field"] { 9 | box-shadow: inset 0 0 0 1px var(--lumo-contrast-30pct); 10 | background-color: var(--lumo-base-color); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | __ __ _____ __ __ ____ ____ _ _ 2 | | \/ | _ _ | ____|| \/ |/ ___| | _ \ _ __ ___ (_) ___ ___ | |_ 3 | | |\/| || | | | | _| | |\/| |\___ \ | |_) || '__| / _ \ | | / _ \ / __|| __| 4 | | | | || |_| | | |___ | | | | ___) | | __/ | | | (_) | | || __/| (__ | |_ 5 | |_| |_| \__, | |_____||_| |_||____/ |_| |_| \___/ _/ | \___| \___| \__| 6 | |___/ |__/ 7 | -------------------------------------------------------------------------------- /bin/src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | __ __ _____ __ __ ____ ____ _ _ 2 | | \/ | _ _ | ____|| \/ |/ ___| | _ \ _ __ ___ (_) ___ ___ | |_ 3 | | |\/| || | | | | _| | |\/| |\___ \ | |_) || '__| / _ \ | | / _ \ / __|| __| 4 | | | | || |_| | | |___ | | | | ___) | | __/ | | | (_) | | || __/| (__ | |_ 5 | |_| |_| \__, | |_____||_| |_||____/ |_| |_| \___/ _/ | \___| \___| \__| 6 | |___/ |__/ 7 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/Application.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | import org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration; 6 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 7 | 8 | /** 9 | * The entry point of the Spring Boot application. 10 | */ 11 | @SpringBootApplication(exclude = ErrorMvcAutoConfiguration.class) 12 | public class Application extends SpringBootServletInitializer { 13 | 14 | public static void main(String[] args) { 15 | SpringApplication.run(Application.class, args); 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/repository/EmployeeRepository.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.repository; 2 | import java.util.List; 3 | 4 | import org.springframework.data.jpa.repository.JpaRepository; 5 | import org.springframework.data.jpa.repository.Query; 6 | import org.springframework.data.repository.query.Param; 7 | 8 | import com.jabbour.ems.backend.entity.Employee; 9 | 10 | public interface EmployeeRepository extends JpaRepository { 11 | 12 | @Query("select e from Employee e " 13 | + "where lower(e.firstName) like lower(concat('%', :searchTerm, '%'))" 14 | + "or lower(e.lastName) like lower(concat('%', :searchTerm, '%'))") 15 | List search(@Param("searchTerm")String stringTerm); 16 | 17 | } -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/service/DepartmentService.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.service; 2 | 3 | import java.util.List; 4 | 5 | import org.springframework.stereotype.Service; 6 | 7 | import com.jabbour.ems.backend.entity.Department; 8 | import com.jabbour.ems.backend.repository.DepartmentRepository; 9 | 10 | @Service 11 | public class DepartmentService { 12 | private DepartmentRepository departmentRepository; 13 | 14 | public DepartmentService(DepartmentRepository departmentRepository) { 15 | this.departmentRepository = departmentRepository; 16 | } 17 | 18 | /** 19 | * 20 | * @return all instances of type department 21 | */ 22 | public List findAll() { 23 | return departmentRepository.findAll(); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/security/CustomRequestCache.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.security; 2 | 3 | import javax.servlet.http.HttpServletRequest; 4 | import javax.servlet.http.HttpServletResponse; 5 | 6 | import org.springframework.security.web.savedrequest.HttpSessionRequestCache; 7 | 8 | /** 9 | * Class that defining a cache 10 | * to keep track of unauthenticated requests. 11 | * @author chris 12 | */ 13 | class CustomRequestCache extends HttpSessionRequestCache { 14 | @Override 15 | public void saveRequest(HttpServletRequest request, HttpServletResponse response) 16 | { 17 | //if not an internal request, log/save request to cache. 18 | if(!SecurityUtils.isFrameworkInternalRequest(request)) 19 | { 20 | super.saveRequest(request, response); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/entity/Department.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.entity; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | import javax.persistence.Entity; 7 | import javax.persistence.FetchType; 8 | import javax.persistence.OneToMany; 9 | 10 | @Entity 11 | public class Department extends AbstractEntity { 12 | private String deptName; 13 | 14 | @OneToMany(mappedBy = "department", fetch = FetchType.EAGER) 15 | private List employees = new LinkedList<>(); 16 | 17 | public Department(){} 18 | 19 | public Department(String deptName) { 20 | setName(deptName); 21 | } 22 | public String getName() { 23 | return deptName; 24 | } 25 | public void setName(String departmentName) { 26 | this.deptName = departmentName; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /heroku-settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 8 | 9 | 12 | 13 | 14 | production 15 | npm 16 | 17 | -------------------------------------------------------------------------------- /frontend/styles/shared-styles.css: -------------------------------------------------------------------------------- 1 | /* 2 | CSS styling for the Vaadin app. 3 | Visit https://vaadin.com/docs/flow/theme/theming-overview.html and 4 | https://vaadin.com/themes/lumo for more information. 5 | */ 6 | 7 | 8 | /* Main layout */ 9 | a[highlight]{ 10 | font-weight: bold; 11 | text-decoration: underline; 12 | } 13 | .header { 14 | padding: 0 var(--lumo-space-m); 15 | } 16 | 17 | .header h1.logo { 18 | font-size: 1em; 19 | margin: var(--lumo-space-m); 20 | } 21 | 22 | 23 | /* List view */ 24 | .list-view .content { 25 | display: flex; 26 | } 27 | 28 | .list-view .employee-grid { 29 | flex: 2; 30 | } 31 | 32 | .list-view .employee-form { 33 | flex: 1; 34 | padding: var(--lumo-space-m); 35 | } 36 | 37 | @media all and (max-width: 1100px) { 38 | .list-view.editing .toolbar, 39 | .list-view.editing .employee-grid { 40 | display: none; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Christopher Jabbour 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /bin/LICENSE.md: -------------------------------------------------------------------------------- 1 | This is free and unencumbered software released into the public domain. 2 | 3 | Anyone is free to copy, modify, publish, use, compile, sell, or 4 | distribute this software, either in source code form or as a compiled 5 | binary, for any purpose, commercial or non-commercial, and by any 6 | means. 7 | 8 | In jurisdictions that recognize copyright laws, the author or authors 9 | of this software dedicate any and all copyright interest in the 10 | software to the public domain. We make this dedication for the benefit 11 | of the public at large and to the detriment of our heirs and 12 | successors. We intend this dedication to be an overt act of 13 | relinquishment in perpetuity of all present and future rights to this 14 | software under copyright law. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 20 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 21 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 22 | OTHER DEALINGS IN THE SOFTWARE. 23 | 24 | For more information, please refer to 25 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file has been autogenerated as it didn't exist or was made for an older incompatible version. 3 | * This file can be used for manual configuration will not be modified if the flowDefaults constant exists. 4 | */ 5 | const merge = require('webpack-merge'); 6 | const flowDefaults = require('./webpack.generated.js'); 7 | 8 | module.exports = merge(flowDefaults, { 9 | 10 | }); 11 | 12 | /** 13 | * This file can be used to configure the flow plugin defaults. 14 | * 15 | * // Add a custom plugin 16 | * flowDefaults.plugins.push(new MyPlugin()); 17 | * 18 | * // Update the rules to also transpile `.mjs` files 19 | * if (!flowDefaults.module.rules[0].test) { 20 | * throw "Unexpected structure in generated webpack config"; 21 | * } 22 | * flowDefaults.module.rules[0].test = /\.m?js$/ 23 | * 24 | * // Include a custom JS in the entry point in addition to generated-flow-imports.js 25 | * if (typeof flowDefaults.entry.index != "string") { 26 | * throw "Unexpected structure in generated webpack config"; 27 | * } 28 | * flowDefaults.entry.index = [flowDefaults.entry.index, "myCustomFile.js"]; 29 | * 30 | * or add new configuration in the merge block. 31 | * 32 | * module.exports = merge(flowDefaults, { 33 | * mode: 'development', 34 | * devtool: 'inline-source-map' 35 | * }); 36 | * 37 | */ 38 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/security/ConfigureUIServiceInitListener.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.security; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | import com.jabbour.ems.ui.view.login.LoginView; 6 | import com.vaadin.flow.component.UI; 7 | import com.vaadin.flow.router.BeforeEnterEvent; 8 | import com.vaadin.flow.server.ServiceInitEvent; 9 | import com.vaadin.flow.server.VaadinServiceInitListener; 10 | 11 | @Component 12 | public class ConfigureUIServiceInitListener implements VaadinServiceInitListener { 13 | 14 | @Override 15 | /*** 16 | * method that runs when a Vaadin Service is initialized 17 | * 18 | * overriden for authenicate method implementation 19 | * 20 | * listen for the initialization of the UI 21 | * (internal root component) and 22 | * add a listener before each view transition. 23 | */ 24 | public void serviceInit(ServiceInitEvent event) { 25 | event.getSource().addUIInitListener(uiEvent -> { 26 | final UI ui = uiEvent.getUI(); 27 | ui.addBeforeEnterListener(this::authenticateNavigation); 28 | }); 29 | } 30 | 31 | // listener 32 | /** 33 | * Reroute all requests to the login if the user is not logged in 34 | * @param event 35 | */ 36 | public void authenticateNavigation(BeforeEnterEvent event) { 37 | if(!LoginView.class.equals(event.getNavigationTarget()) 38 | && !SecurityUtils.isUserLoggedIn()) { 39 | event.rerouteTo(LoginView.class); 40 | } 41 | } 42 | 43 | 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/entity/AbstractEntity.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.entity; 2 | 3 | import javax.persistence.GeneratedValue; 4 | import javax.persistence.GenerationType; 5 | import javax.persistence.Id; 6 | import javax.persistence.MappedSuperclass; 7 | 8 | /*** 9 | * The common superclass 10 | * that the entities will inherit and 11 | * not generate a table 12 | * 13 | * it define how objects ids are generated 14 | * and how object equality is determined. 15 | * @author chris jabbour 16 | * 17 | *more on abstract classes 18 | *https://docs.oracle.com/javase/tutorial/java/IandI/abstract.html 19 | */ 20 | @MappedSuperclass 21 | public abstract class AbstractEntity { 22 | 23 | @Id 24 | @GeneratedValue(strategy= GenerationType.SEQUENCE) 25 | private Long id; 26 | public Long getId() { 27 | return id; 28 | } 29 | public boolean isPersisted() { 30 | return id != null; 31 | } 32 | @Override 33 | public int hashCode() { 34 | if(getId() != null) { 35 | return getId().hashCode(); 36 | } 37 | return super.hashCode(); 38 | } 39 | @Override 40 | /**Check if object passed equals the object 41 | * that invokes the equals method 42 | * @param obj parameter of the superclass Object. 43 | * @return a boolean value true or false 44 | * based on if the both objects are equal to each other 45 | */ 46 | public boolean equals(Object obj) { 47 | if(this == obj) { 48 | return true; 49 | } 50 | if (obj == null) { 51 | return false; 52 | } 53 | if(getClass() != obj.getClass()) { 54 | return false; 55 | } 56 | AbstractEntity other = (AbstractEntity) obj; 57 | if(getId() == null || other.getId() == null) 58 | { 59 | return false; 60 | } 61 | return getId().equals(other.getId()); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/security/SecurityUtils.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.security; 2 | 3 | import java.util.stream.Stream; 4 | import javax.servlet.http.HttpServletRequest; 5 | 6 | import org.springframework.security.authentication.AnonymousAuthenticationToken; 7 | import org.springframework.security.core.Authentication; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | 10 | import com.vaadin.flow.server.HandlerHelper; 11 | import com.vaadin.flow.shared.ApplicationConstants; 12 | /*** 13 | * Helper class 14 | * for Utility methods and static operations 15 | * using the Spring Security module 16 | * @author chris 17 | */ 18 | public final class SecurityUtils { 19 | 20 | //Private constructor prevents instantation of utility class directly 21 | //for serving singleton classes 22 | private SecurityUtils(){} 23 | /*** 24 | * determine if http request is a internal framework request to and from Vaadin. 25 | * @param request => {@link HttpServletRequest} client-side http request 26 | * @return true if request is an internal framework request, False if it is not 27 | */ 28 | static boolean isFrameworkInternalRequest(HttpServletRequest request) 29 | { 30 | final String parameterValue = request.getParameter(ApplicationConstants.REQUEST_TYPE_PARAMETER); 31 | return parameterValue != null 32 | && Stream.of(HandlerHelper.RequestType.values()).anyMatch(r -> r.getIdentifier().equals(parameterValue)); 33 | } 34 | /** 35 | * Check if the current user is logged in. 36 | * @return true if current user is logged in, otherwise false if not logged in. 37 | */ 38 | static boolean isUserLoggedIn() { 39 | Authentication authenication = SecurityContextHolder.getContext().getAuthentication(); 40 | return authenication != null 41 | && !(authenication instanceof AnonymousAuthenticationToken) 42 | && authenication.isAuthenticated(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/entity/Employee.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.entity; 2 | 3 | import javax.persistence.Entity; 4 | import javax.persistence.EnumType; 5 | import javax.persistence.Enumerated; 6 | import javax.persistence.JoinColumn; 7 | import javax.persistence.ManyToOne; 8 | import javax.persistence.OneToOne; 9 | import javax.validation.constraints.Email; 10 | import javax.validation.constraints.NotEmpty; 11 | import javax.validation.constraints.NotNull; 12 | 13 | @Entity 14 | public class Employee extends AbstractEntity implements Cloneable { 15 | 16 | public enum Status { 17 | FullTime, OnLeave, Contract 18 | } 19 | 20 | @NotNull 21 | @NotEmpty 22 | private String firstName = ""; 23 | 24 | @NotNull 25 | @NotEmpty 26 | private String lastName = ""; 27 | 28 | 29 | @ManyToOne 30 | @JoinColumn(name = "dept_id") 31 | private Department department; 32 | 33 | @Email 34 | @NotNull 35 | @NotEmpty 36 | private String email = ""; 37 | 38 | @Enumerated(EnumType.STRING) 39 | @NotNull 40 | private Employee.Status status; 41 | 42 | public Employee.Status getStatus() { 43 | return status; 44 | } 45 | public void setStatus(Employee.Status status) { 46 | this.status = status; 47 | } 48 | public String getFirstName() { 49 | return firstName; 50 | } 51 | public void setFirstName(String firstName) { 52 | this.firstName = firstName; 53 | } 54 | public String getLastName() { 55 | return lastName; 56 | } 57 | public void setLastName(String lastName) { 58 | this.lastName = lastName; 59 | } 60 | public Department getDepartment() { 61 | return department; 62 | } 63 | public void setDepartment(Department department) { 64 | this.department = department; 65 | } 66 | public String getEmail() { 67 | return email; 68 | } 69 | public void setEmail(String email) { 70 | this.email = email; 71 | } 72 | 73 | public String nameToString() { return firstName + " " + lastName; } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/ui/MainLayout.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.ui; 2 | 3 | import com.jabbour.ems.ui.view.list.ListView; 4 | import com.vaadin.flow.component.UI; 5 | import com.vaadin.flow.component.applayout.AppLayout; 6 | import com.vaadin.flow.component.applayout.DrawerToggle; 7 | import com.vaadin.flow.component.button.Button; 8 | import com.vaadin.flow.component.dependency.CssImport; 9 | import com.vaadin.flow.component.html.Anchor; 10 | import com.vaadin.flow.component.html.H1; 11 | import com.vaadin.flow.component.orderedlayout.FlexComponent; 12 | import com.vaadin.flow.component.orderedlayout.HorizontalLayout; 13 | import com.vaadin.flow.component.orderedlayout.VerticalLayout; 14 | import com.vaadin.flow.dom.ThemeList; 15 | import com.vaadin.flow.router.HighlightConditions; 16 | import com.vaadin.flow.router.RouterLink; 17 | import com.vaadin.flow.theme.lumo.Lumo; 18 | 19 | @SuppressWarnings("serial") 20 | @CssImport("./styles/shared-styles.css") 21 | public class MainLayout extends AppLayout { 22 | public MainLayout() { 23 | createHeader(); 24 | createDrawer(); 25 | } 26 | 27 | 28 | private void createHeader() { 29 | 30 | H1 logo = new H1("Slick EMS"); 31 | logo.addClassName("logo"); 32 | 33 | Anchor logout = new Anchor("logout", "Log out"); 34 | Button toggleButton = new Button("Toggle ☀️/🌙", click -> { 35 | ThemeList themeList = UI.getCurrent().getElement().getThemeList(); // 36 | if (themeList.contains(Lumo.DARK)) { // 37 | themeList.remove(Lumo.DARK); 38 | } else { 39 | themeList.add(Lumo.DARK); 40 | } 41 | }); 42 | 43 | HorizontalLayout header = new HorizontalLayout(new DrawerToggle(), logo,toggleButton, logout); 44 | header.expand(logo); 45 | header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); 46 | header.setWidth("100%"); 47 | header.addClassName("header"); 48 | addToNavbar(header); 49 | 50 | } 51 | 52 | private void createDrawer() { 53 | RouterLink listLink = new RouterLink("List", ListView.class); 54 | listLink.setHighlightCondition(HighlightConditions.sameLocation()); 55 | addToDrawer(new VerticalLayout(listLink)); 56 | } 57 | 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/ui/view/login/LoginView.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.ui.view.login; 2 | 3 | import com.vaadin.flow.component.Component; 4 | import com.vaadin.flow.component.button.ButtonVariant; 5 | import com.vaadin.flow.component.html.Div; 6 | import com.vaadin.flow.component.html.Footer; 7 | import com.vaadin.flow.component.html.H1; 8 | import com.vaadin.flow.component.html.NativeButton; 9 | import com.vaadin.flow.component.html.Paragraph; 10 | import com.vaadin.flow.component.login.LoginForm; 11 | import com.vaadin.flow.component.notification.Notification; 12 | import com.vaadin.flow.component.orderedlayout.VerticalLayout; 13 | import com.vaadin.flow.router.BeforeEnterEvent; 14 | import com.vaadin.flow.router.BeforeEnterObserver; 15 | import com.vaadin.flow.router.PageTitle; 16 | import com.vaadin.flow.router.Route; 17 | import com.vaadin.flow.theme.lumo.Lumo; 18 | 19 | @SuppressWarnings("serial") 20 | @Route("login") 21 | @PageTitle("Login | Slick EMS") 22 | public class LoginView extends VerticalLayout implements BeforeEnterObserver { 23 | 24 | private LoginForm login = new LoginForm(); 25 | 26 | 27 | public LoginView() { 28 | 29 | 30 | 31 | addClassName("login-view"); 32 | NativeButton button = new NativeButton("Click here!"); 33 | button.addClassName("lumo-button"); 34 | button.getElement().setAttribute("aria-label", "Click me"); 35 | 36 | Paragraph footer = new Paragraph("Created by Chris Jabbour🧿"); 37 | 38 | Notification notification = new Notification( 39 | "username is user and password is password for the Demo showcase", 3000); 40 | 41 | 42 | button.addClickListener(event -> notification.open()); 43 | 44 | setSizeFull(); 45 | setAlignItems(Alignment.CENTER); 46 | setJustifyContentMode(JustifyContentMode.CENTER); 47 | 48 | 49 | login.setAction("login"); 50 | add(new H1("Slick EMS"), login, button); 51 | add(footer); 52 | } 53 | 54 | 55 | @Override 56 | public void beforeEnter(BeforeEnterEvent beforeEnterEvent) { 57 | 58 | if(beforeEnterEvent.getLocation() 59 | .getQueryParameters() 60 | .getParameters() 61 | .containsKey("error")) { 62 | login.setError(true); 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/com/jabbour/ems/MainViewIT.java: -------------------------------------------------------------------------------- 1 | /* 2 | * package com.jabbour.ems; 3 | * 4 | * import com.vaadin.flow.component.textfield.testbench.TextFieldElement; import 5 | * org.junit.Assert; import org.junit.Test; import org.openqa.selenium.Keys; 6 | * import org.openqa.selenium.WebElement; 7 | * 8 | * import com.vaadin.flow.component.button.testbench.ButtonElement; import 9 | * com.vaadin.flow.component.notification.testbench.NotificationElement; import 10 | * com.vaadin.flow.theme.lumo.Lumo; 11 | * 12 | * public class MainViewIT extends AbstractViewTest { 13 | * 14 | * @Test public void clickingButtonShowsNotification() { 15 | * Assert.assertFalse($(NotificationElement.class).exists()); 16 | * $(ButtonElement.class).first().click(); 17 | * Assert.assertTrue($(NotificationElement.class).waitForFirst().isOpen()); } 18 | * 19 | * @Test public void clickingButtonTwiceShowsTwoNotifications() { 20 | * Assert.assertFalse($(NotificationElement.class).exists()); ButtonElement 21 | * button = $(ButtonElement.class).first(); button.click(); button.click(); 22 | * Assert.assertEquals(2, $(NotificationElement.class).all().size()); } 23 | * 24 | * @Test public void buttonIsUsingLumoTheme() { WebElement element = 25 | * $(ButtonElement.class).first(); assertThemePresentOnElement(element, 26 | * Lumo.class); } 27 | * 28 | * @Test public void 29 | * testClickButtonShowsHelloAnonymousUserNotificationWhenUserNameIsEmpty() { 30 | * ButtonElement button = $(ButtonElement.class).first(); button.click(); 31 | * Assert.assertTrue($(NotificationElement.class).exists()); NotificationElement 32 | * notification = $(NotificationElement.class).first(); 33 | * Assert.assertEquals("Hello anonymous user", notification.getText()); } 34 | * 35 | * @Test public void 36 | * testClickButtonShowsHelloUserNotificationWhenUserIsNotEmpty() { 37 | * TextFieldElement textField = $(TextFieldElement.class).first(); 38 | * textField.setValue("Vaadiner"); ButtonElement button = 39 | * $(ButtonElement.class).first(); button.click(); 40 | * Assert.assertTrue($(NotificationElement.class).exists()); NotificationElement 41 | * notification = $(NotificationElement.class).first(); 42 | * Assert.assertEquals("Hello Vaadiner", notification.getText()); } 43 | * 44 | * @Test public void testEnterShowsHelloUserNotificationWhenUserIsNotEmpty() { 45 | * TextFieldElement textField = $(TextFieldElement.class).first(); 46 | * textField.setValue("Vaadiner"); textField.sendKeys(Keys.ENTER); 47 | * Assert.assertTrue($(NotificationElement.class).exists()); NotificationElement 48 | * notification = $(NotificationElement.class).first(); 49 | * Assert.assertEquals("Hello Vaadiner", notification.getText()); } } 50 | */ -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/security/SecurityConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.security; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.builders.WebSecurity; 7 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 8 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 9 | import org.springframework.security.core.userdetails.User; 10 | import org.springframework.security.core.userdetails.UserDetails; 11 | import org.springframework.security.core.userdetails.UserDetailsService; 12 | import org.springframework.security.provisioning.InMemoryUserDetailsManager; 13 | /*** 14 | * Class for Spring Security Configuration and turning on Spring Security for the application. 15 | * more about Vaadin security features on {@linkplain https://vaadin.com/security } 16 | * 17 | * NOTE: it is not good practice to configure users 18 | * directly in code for applications in production. 19 | * 20 | * You can configure Spring Security to use an authentication provider 21 | * further reading: https://dzone.com/articles/spring-security-authentication 22 | * @author chris 23 | * 24 | * */ 25 | @EnableWebSecurity 26 | @Configuration 27 | public class SecurityConfiguration extends WebSecurityConfigurerAdapter { 28 | private static final String LOGIN_PROCESSING_URL = "/login"; 29 | private static final String LOGIN_FAILURE_URL = "/login?error"; 30 | private static final String LOGIN_URL = "/login"; 31 | private static final String LOGIN_SUCCESS_URL = "/login"; 32 | 33 | /*** 34 | * Block unauthenticated requests to all pages besides login page 35 | */ 36 | protected void configure(HttpSecurity http) throws Exception { 37 | //example of method chaining 38 | http.csrf().disable() // Disable CSRF protection from Spring Security as Vaadin already has it built in. 39 | .requestCache().requestCache(new CustomRequestCache()) 40 | .and().authorizeRequests() 41 | .requestMatchers(SecurityUtils::isFrameworkInternalRequest).permitAll() 42 | 43 | .anyRequest().authenticated() 44 | 45 | .and().formLogin() //Enable form-based login and permits even unauth to it 46 | .loginPage(LOGIN_URL).permitAll() 47 | .loginProcessingUrl(LOGIN_PROCESSING_URL) 48 | .failureUrl(LOGIN_FAILURE_URL) 49 | .and().logout().logoutSuccessUrl(LOGIN_SUCCESS_URL); 50 | } 51 | 52 | @Bean 53 | @Override 54 | /** 55 | * Configure test users for application. for testing and demonstration purposes. 56 | */ 57 | public UserDetailsService userDetailsService() { 58 | UserDetails user = 59 | User.withUsername("user") 60 | .password("{noop}password") 61 | .roles("USER") 62 | .build(); 63 | 64 | return new InMemoryUserDetailsManager(user); 65 | } 66 | 67 | @Override 68 | public void configure(WebSecurity web) { 69 | web.ignoring().antMatchers( 70 | //client side JS 71 | "/VAADIN/**", 72 | "/favicon.ico", 73 | "/robots.txt", 74 | "/manifest.webmanifest", 75 | "/sw.js", 76 | "/offline.html", 77 | "/icons/**", 78 | "/images/**", 79 | "/styles/**", 80 | "/h2-console/**"); 81 | } 82 | 83 | 84 | } 85 | 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Project Overview 2 | 3 | A Spring boot web application application that lets you add, delete, edit and update employees in different departments for enterprise needs and contact info. 4 | 5 | Technologies used: Java programming language, Spring Boot, H2 in-memory database to test mock data, Spring Security. 6 | 7 | 8 | Front-end UI and Routing implementation: Vaadin UI framework 9 | 10 | ## Showcase 11 | 12 |

13 | 14 | test demo version 1 15 |

16 | 17 | ## Testing: Running the Application 18 | 19 | There are two ways to run the application : using `mvn spring-boot:run` or by running the `Application` class directly from your IDE. 20 | 21 | You can use any IDE of your preference,but we suggest Eclipse or Intellij IDEA. 22 | Below are the configuration details to start the project using a `spring-boot:run` command. Both Eclipse and Intellij IDEA are covered. 23 | 24 | #### Eclipse / STS 25 | - Right click on a project folder and select `Run As` --> `Maven build..` . After that a configuration window is opened. 26 | - In the window set the value of the **Goals** field to `spring-boot:run` 27 | - You can optionally select `Skip tests` checkbox 28 | - All the other settings can be left to default 29 | 30 | Once configurations are set clicking `Run` will start the application 31 | 32 | #### Intellij IDEA 33 | - On the right side of the window, select Maven --> Plugins--> `spring-boot` --> `spring-boot:run` goal 34 | - Optionally, you can disable tests by clicking on a `Skip Tests mode` blue button. 35 | 36 | Clicking on the green run button will start the application. 37 | 38 | After the application has started, you can view your it at http://localhost:8080/ in your browser. 39 | 40 | 41 | If you want to run the application locally in the production mode, use `spring-boot:run -Pproduction` command instead. 42 | 43 | ## Project overview 44 | A Java Web App with Vaadin follows the Maven's [standard directory layout structure](https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html): 45 | - Under the `src/main/java` are located Application sources 46 | - Under the `src/test` are located test files 47 | - `src/main/resources` contains configuration files and static resources 48 | - The `frontend` directory in the root folder contains client-side dependencies and resource files 49 | - All CSS styles used by the application are located under the root directory `frontend/styles` 50 | - Templates would be stored under the `frontend/src` 51 | 52 | ## More Information and Next Steps, Vaadin Documentation 53 | - Vaadin Basics [https://vaadin.com/docs](https://vaadin.com/docs) 54 | - More components at [https://vaadin.com/components](https://vaadin.com/components) and [https://vaadin.com/directory](https://vaadin.com/directory) 55 | - Experiment with Vaadin and other examples at [https://vaadin.com/start](https://vaadin.com/start) 56 | - Using Vaadin and Spring [https://vaadin.com/docs/v14/flow/spring/tutorial-spring-basic.html](https://vaadin.com/docs/v14/flow/spring/tutorial-spring-basic.html) article 57 | - Join discussion and ask a question at [https://vaadin.com/forum](https://vaadin.com/forum) 58 | 59 | ## Notes 60 | If you run application from a command line, remember to prepend a `mvn` to the command. 61 | -------------------------------------------------------------------------------- /bin/README.md: -------------------------------------------------------------------------------- 1 | # Project Base for Vaadin and Spring Boot 2 | 3 | This is an example project that can be used as a starting point to create your own Vaadin application with Spring Boot. 4 | It contains all the necessary configuration and some placeholder files to get you started. 5 | 6 | 7 | ## Running the Application 8 | There are two ways to run the application : using `mvn spring-boot:run` or by running the `Application` class directly from your IDE. 9 | 10 | You can use any IDE of your preference,but we suggest Eclipse or Intellij IDEA. 11 | Below are the configuration details to start the project using a `spring-boot:run` command. Both Eclipse and Intellij IDEA are covered. 12 | 13 | #### Eclipse 14 | - Right click on a project folder and select `Run As` --> `Maven build..` . After that a configuration window is opened. 15 | - In the window set the value of the **Goals** field to `spring-boot:run` 16 | - You can optionally select `Skip tests` checkbox 17 | - All the other settings can be left to default 18 | 19 | Once configurations are set clicking `Run` will start the application 20 | 21 | #### Intellij IDEA 22 | - On the right side of the window, select Maven --> Plugins--> `spring-boot` --> `spring-boot:run` goal 23 | - Optionally, you can disable tests by clicking on a `Skip Tests mode` blue button. 24 | 25 | Clicking on the green run button will start the application. 26 | 27 | After the application has started, you can view your it at http://localhost:8080/ in your browser. 28 | 29 | 30 | If you want to run the application locally in the production mode, use `spring-boot:run -Pproduction` command instead. 31 | ### Running Integration Tests 32 | 33 | Integration tests are implemented using [Vaadin TestBench](https://vaadin.com/testbench). The tests take a few minutes to run and are therefore included in a separate Maven profile. We recommend running tests with a production build to minimize the chance of development time toolchains affecting test stability. To run the tests using Google Chrome, execute 34 | 35 | `mvn verify -Pit,production` 36 | 37 | and make sure you have a valid TestBench license installed. 38 | 39 | Profile `it` adds the following parameters to run integration tests: 40 | ```sh 41 | -Dwebdriver.chrome.driver=path_to_driver 42 | -Dcom.vaadin.testbench.Parameters.runLocally=chrome 43 | ``` 44 | 45 | If you would like to run a separate test make sure you have added these parameters to VM Options of JUnit run configuration 46 | 47 | ## Project overview 48 | 49 | Project follow the Maven's [standard directory layout structure](https://maven.apache.org/guides/introduction/introduction-to-the-standard-directory-layout.html): 50 | - Under the `srs/main/java` are located Application sources 51 | - `Application.java` is a runnable Java application class and a starting point 52 | - `GreetService.java` is a Spring service class 53 | - `MainView.java` is a default view and entry point of the application 54 | - Under the `srs/test` are located test files 55 | - `src/main/resources` contains configuration files and static resources 56 | - The `frontend` directory in the root folder contains client-side dependencies and resource files 57 | - All CSS styles used by the application are located under the root directory `frontend/styles` 58 | - Templates would be stored under the `frontend/src` 59 | 60 | 61 | ## More Information and Next Steps 62 | 63 | - Vaadin Basics [https://vaadin.com/docs](https://vaadin.com/docs) 64 | - More components at [https://vaadin.com/components](https://vaadin.com/components) and [https://vaadin.com/directory](https://vaadin.com/directory) 65 | - Download this and other examples at [https://vaadin.com/start](https://vaadin.com/start) 66 | - Using Vaadin and Spring [https://vaadin.com/docs/v14/flow/spring/tutorial-spring-basic.html](https://vaadin.com/docs/v14/flow/spring/tutorial-spring-basic.html) article 67 | - Join discussion and ask a question at [https://vaadin.com/forum](https://vaadin.com/forum) 68 | 69 | 70 | ## Notes 71 | 72 | If you run application from a command line, remember to prepend a `mvn` to the command. 73 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/ui/view/list/EmployeeForm.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.ui.view.list; 2 | 3 | import java.util.List; 4 | 5 | import com.jabbour.ems.backend.entity.Department; 6 | import com.jabbour.ems.backend.entity.Employee; 7 | import com.vaadin.flow.component.Component; 8 | import com.vaadin.flow.component.ComponentEvent; 9 | import com.vaadin.flow.component.ComponentEventListener; 10 | import com.vaadin.flow.component.Key; 11 | import com.vaadin.flow.component.button.Button; 12 | import com.vaadin.flow.component.button.ButtonVariant; 13 | import com.vaadin.flow.component.combobox.ComboBox; 14 | import com.vaadin.flow.component.formlayout.FormLayout; 15 | import com.vaadin.flow.component.orderedlayout.HorizontalLayout; 16 | import com.vaadin.flow.component.textfield.EmailField; 17 | import com.vaadin.flow.component.textfield.TextField; 18 | import com.vaadin.flow.data.binder.BeanValidationBinder; 19 | import com.vaadin.flow.data.binder.Binder; 20 | import com.vaadin.flow.data.binder.ValidationException; 21 | import com.vaadin.flow.shared.Registration; 22 | 23 | /** 24 | * Employee form component that extends FormLayout Vaadin for form display 25 | * 26 | * @author chris 27 | * 28 | */ 29 | public class EmployeeForm extends FormLayout { 30 | 31 | private Employee employee; 32 | 33 | TextField firstName = new TextField("First name"); 34 | TextField lastName = new TextField("Last name"); 35 | EmailField email = new EmailField("Email"); 36 | ComboBox status = new ComboBox<>("Status"); 37 | ComboBox department = new ComboBox<>("Department"); 38 | 39 | Button save = new Button("Save"); 40 | Button delete = new Button("Delete"); 41 | Button close = new Button("Cancel"); 42 | 43 | Binder binder = new BeanValidationBinder<>(Employee.class); 44 | 45 | // Constructor 46 | public EmployeeForm(List departments) { 47 | addClassName("employee-form"); 48 | binder.bindInstanceFields(this); 49 | department.setItems(departments); 50 | department.setItemLabelGenerator(Department::getName); 51 | status.setItems(Employee.Status.values()); 52 | add(firstName, lastName, email, department, status, createButtonsLayout()); 53 | } 54 | 55 | public void setEmployee(Employee employee) { 56 | this.employee = employee; 57 | binder.readBean(employee); 58 | } 59 | 60 | private Component createButtonsLayout() { 61 | save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); 62 | delete.addThemeVariants(ButtonVariant.LUMO_ERROR); 63 | close.addThemeVariants(ButtonVariant.LUMO_TERTIARY); 64 | save.addClickShortcut(Key.ENTER); 65 | close.addClickShortcut(Key.ESCAPE); 66 | 67 | save.addClickListener(event -> validateAndSave()); 68 | delete.addClickListener(event -> fireEvent(new DeleteEvent(this, employee))); 69 | close.addClickListener(event -> fireEvent(new CloseEvent(this))); 70 | 71 | binder.addStatusChangeListener(e -> save.setEnabled(binder.isValid())); 72 | return new HorizontalLayout(save, delete, close); 73 | } 74 | 75 | private void validateAndSave() { 76 | try { 77 | binder.writeBean(employee); 78 | fireEvent(new SaveEvent(this, employee)); 79 | } catch (ValidationException e) { 80 | e.printStackTrace(); 81 | } 82 | } 83 | 84 | // Events 85 | /** 86 | * Superclass for all events and handles the employee object that is edited or 87 | * deleted. 88 | * 89 | * @author chris 90 | */ 91 | public static abstract class EmployeeFormEvent extends ComponentEvent { 92 | private Employee employee; 93 | 94 | protected EmployeeFormEvent(EmployeeForm source, Employee employee) { 95 | super(source, false); 96 | this.employee = employee; 97 | } 98 | 99 | public Employee getEmployee() { 100 | return employee; 101 | } 102 | } 103 | 104 | public static class SaveEvent extends EmployeeFormEvent { 105 | SaveEvent(EmployeeForm source, Employee employee) { 106 | super(source, employee); 107 | } 108 | } 109 | 110 | public static class DeleteEvent extends EmployeeFormEvent { 111 | DeleteEvent(EmployeeForm source, Employee employee) { 112 | super(source, employee); 113 | } 114 | } 115 | 116 | public static class CloseEvent extends EmployeeFormEvent { 117 | CloseEvent(EmployeeForm source) { 118 | super(source, null); 119 | } 120 | } 121 | 122 | public > Registration addListener(Class eventType, 123 | ComponentEventListener listener) { 124 | return getEventBus().addListener(eventType, listener); 125 | } 126 | } -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/backend/service/EmployeeService.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.backend.service; 2 | 3 | import java.util.List; 4 | import java.util.Random; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | import java.util.stream.Collectors; 8 | import java.util.stream.Stream; 9 | 10 | import javax.annotation.PostConstruct; 11 | 12 | import org.springframework.stereotype.Service; 13 | 14 | import com.jabbour.ems.backend.entity.Department; 15 | import com.jabbour.ems.backend.entity.Employee; 16 | import com.jabbour.ems.backend.repository.DepartmentRepository; 17 | import com.jabbour.ems.backend.repository.EmployeeRepository; 18 | 19 | /*** 20 | * A service class for employee 21 | * to handle business logic and data access. 22 | * It lets you do CRUD operations 23 | * on a database depending on which DB you choose to incorporate. 24 | * 25 | * @author Chris 26 | */ 27 | 28 | @Service 29 | public class EmployeeService { 30 | //members 31 | private static final Logger LOGGER = Logger.getLogger(EmployeeService.class.getName()); 32 | private EmployeeRepository employeeRepository; 33 | private DepartmentRepository departmentRepository; 34 | 35 | //Constructor 36 | public EmployeeService(EmployeeRepository employeeRepository, 37 | DepartmentRepository departmentRepository) 38 | { 39 | this.employeeRepository = employeeRepository; 40 | this.departmentRepository = departmentRepository; 41 | } 42 | 43 | /*** 44 | * Find and return all instances as specified in jparepository interface. 45 | * @return All entity instances of the type employee. 46 | */ 47 | public List findAll() { 48 | return employeeRepository.findAll(); 49 | } 50 | 51 | public List findAll(String stringFilter) 52 | { 53 | if (stringFilter == null || stringFilter.isEmpty()) 54 | { 55 | return employeeRepository.findAll(); 56 | } 57 | else { 58 | return employeeRepository.search(stringFilter); 59 | } 60 | 61 | } 62 | /*** 63 | * return number of instances in empRepository 64 | * @return the number of entities available 65 | */ 66 | public long count() { 67 | return employeeRepository.count(); 68 | } 69 | /** 70 | * Delete an employee from the repository 71 | * @param emp employee entity to delete. 72 | */ 73 | public void delete(Employee emp) { 74 | employeeRepository.delete(emp); 75 | } 76 | /*** 77 | * Save an employee to your repository 78 | * @param emp the saved entity and must not be null. 79 | */ 80 | public void save(Employee emp) { 81 | //make sure employee is not null, return log message to console. 82 | if(emp == null) { 83 | LOGGER.log(Level.SEVERE, 84 | "Employee is null. is your form connected to the application?"); 85 | return; 86 | } 87 | employeeRepository.save(emp); 88 | } 89 | 90 | //tells Spring to run this method after Constructing EmployeeService. 91 | @PostConstruct 92 | public void populateTestData() { 93 | if(departmentRepository.count() == 0) { 94 | departmentRepository.saveAll( 95 | Stream.of("HR","Marketing","Finance","Dev","IT","Cybersecurity","Executive","R&D") 96 | .map(Department::new) 97 | .collect(Collectors.toList())); 98 | } 99 | 100 | 101 | if(employeeRepository.count()==0) { 102 | Random r = new Random(0); 103 | List departments = departmentRepository.findAll(); 104 | employeeRepository.saveAll( 105 | Stream.of("Rich Bowers", "Mark Rubio","Andrew Stich", "Andy Roo", 106 | "Steven McDonald","Christopher Jabbour", 107 | "Lucifer Morningstar", 108 | "Daniel Birmingham", "Matthew Knudsvig", 109 | "Luke Bell", "Stephen Jabbour", "Victor Jabbour", 110 | "Makar Tchekalenkov", "Daniel Lopez", "Raymond Moorhead", 111 | "Daniel Larrea","Jabid Methun", "Joshua Anderson","Elizabeth Grauvogel", 112 | "Charles Dunn", "Keith Prince") 113 | .map(name -> { 114 | String[] split = name.split(" "); 115 | Employee emp= new Employee(); 116 | emp.setFirstName(split[0]); 117 | emp.setLastName(split[1]); 118 | emp.setDepartment(departments.get(r.nextInt(departments.size()))); 119 | emp.setStatus(Employee.Status.values()[r.nextInt(Employee.Status.values().length)]); 120 | String email = (emp.getFirstName() + "." + emp.getLastName()+ "@" + emp.getDepartment().getName().replaceAll("[\\s-]", "") + ".com").toLowerCase(); 121 | emp.setEmail(email); 122 | return emp; 123 | }).collect(Collectors.toList())); 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/test/java/com/jabbour/ems/AbstractViewTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * package com.jabbour.ems; 3 | * 4 | * import com.vaadin.flow.theme.AbstractTheme; import 5 | * com.vaadin.testbench.ScreenshotOnFailureRule; import 6 | * com.vaadin.testbench.TestBench; import 7 | * com.vaadin.testbench.parallel.ParallelTest; import 8 | * io.github.bonigarcia.wdm.WebDriverManager; import org.junit.Assert; import 9 | * org.junit.Before; import org.junit.BeforeClass; import org.junit.Rule; import 10 | * org.openqa.selenium.By; import org.openqa.selenium.WebElement; import 11 | * org.openqa.selenium.chrome.ChromeDriver; 12 | *//** 13 | * Base class for TestBench IntegrationTests on chrome. 14 | *

15 | * The tests use Chrome driver (see pom.xml for integration-tests profile) to 16 | * run integration tests on a headless Chrome. If a property {@code test.use 17 | * .hub} is set to true, {@code AbstractViewTest} will assume that the TestBench 18 | * test is running in a CI environment. In order to keep the this class light, 19 | * it makes certain assumptions about the CI environment (such as available 20 | * environment variables). It is not advisable to use this class as a base class 21 | * for you own TestBench tests. 22 | *

23 | * To learn more about TestBench, visit Vaadin 25 | * TestBench. 26 | */ 27 | /* 28 | * public abstract class AbstractViewTest extends ParallelTest { private static 29 | * final int SERVER_PORT = 8080; 30 | * 31 | * private final String route; private final By rootSelector; 32 | * 33 | * @Rule public ScreenshotOnFailureRule rule = new ScreenshotOnFailureRule(this, 34 | * false); 35 | * 36 | * public AbstractViewTest() { this("", By.tagName("body")); } 37 | * 38 | * protected AbstractViewTest(String route, By rootSelector) { this.route = 39 | * route; this.rootSelector = rootSelector; } 40 | * 41 | * @BeforeClass public static void setupClass() { 42 | * WebDriverManager.chromedriver().setup(); } 43 | * 44 | * @Before public void setup() throws Exception { if (isUsingHub()) { 45 | * super.setup(); } else { setDriver(TestBench.createDriver(new 46 | * ChromeDriver())); } getDriver().get(getURL(route)); } 47 | * 48 | *//** 49 | * Convenience method for getting the root element of the view based on the 50 | * selector passed to the constructor. 51 | * 52 | * @return the root element 53 | */ 54 | /* 55 | * protected WebElement getRootElement() { return findElement(rootSelector); } 56 | * 57 | *//** 58 | * Asserts that the given {@code element} is rendered using a theme identified 59 | * by {@code themeClass}. If the theme is not found, JUnit assert will fail the 60 | * test case. 61 | * 62 | * @param element web element to check for the theme 63 | * @param themeClass theme class (such as {@code Lumo.class} 64 | */ 65 | /* 66 | * protected void assertThemePresentOnElement( WebElement element, Class themeClass) { String themeName = 68 | * themeClass.getSimpleName().toLowerCase(); Boolean hasStyle = (Boolean) 69 | * executeScript("" + "var styles = Array.from(arguments[0]._template.content" + 70 | * ".querySelectorAll('style'))" + 71 | * ".filter(style => style.textContent.indexOf('" + themeName + "') > -1);" + 72 | * "return styles.length > 0;", element); 73 | * 74 | * Assert.assertTrue("Element '" + element.getTagName() + "' should have" + 75 | * " had theme '" + themeClass.getSimpleName() + "'.", hasStyle); } 76 | * 77 | *//** 78 | * Property set to true when running on a test hub. 79 | */ 80 | /* 81 | * private static final String USE_HUB_PROPERTY = "test.use.hub"; 82 | * 83 | *//** 84 | * Returns deployment host name concatenated with route. 85 | * 86 | * @return URL to route 87 | */ 88 | /* 89 | * private static String getURL(String route) { return 90 | * String.format("http://%s:%d/%s", getDeploymentHostname(), SERVER_PORT, 91 | * route); } 92 | * 93 | *//** 94 | * Returns whether we are using a test hub. This means that the starter is 95 | * running tests in Vaadin's CI environment, and uses TestBench to connect to 96 | * the testing hub. 97 | * 98 | * @return whether we are using a test hub 99 | */ 100 | /* 101 | * private static boolean isUsingHub() { return Boolean.TRUE.toString().equals( 102 | * System.getProperty(USE_HUB_PROPERTY)); } 103 | * 104 | *//** 105 | * If running on CI, get the host name from environment variable HOSTNAME 106 | * 107 | * @return the host name 108 | *//* 109 | * private static String getDeploymentHostname() { return isUsingHub() ? 110 | * System.getenv("HOSTNAME") : "localhost"; } } 111 | */ -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "no-name", 3 | "license": "UNLICENSED", 4 | "dependencies": { 5 | "@polymer/polymer": "3.2.0", 6 | "@webcomponents/webcomponentsjs": "^2.2.10", 7 | "@vaadin/vaadin-crud": "1.2.1", 8 | "@vaadin/vaadin-icons": "4.3.1", 9 | "@vaadin/vaadin-grid": "5.6.6", 10 | "@vaadin/vaadin-split-layout": "4.2.0", 11 | "@vaadin/vaadin-combo-box": "5.2.0", 12 | "@vaadin/vaadin-cookie-consent": "1.1.2", 13 | "@vaadin/vaadin-core-shrinkwrap": "14.3.0", 14 | "@vaadin/vaadin-upload": "4.3.0", 15 | "@vaadin/vaadin-dialog": "2.4.2", 16 | "@vaadin/vaadin-select": "2.2.0", 17 | "@vaadin/vaadin-app-layout": "2.1.0", 18 | "@vaadin/vaadin-item": "2.2.0", 19 | "@vaadin/vaadin-board": "2.1.1", 20 | "@vaadin/vaadin-notification": "1.5.0", 21 | "@vaadin/vaadin-charts": "6.3.0", 22 | "@vaadin/vaadin-grid-pro": "2.1.1", 23 | "@vaadin/vaadin-progress-bar": "1.2.0", 24 | "@vaadin/vaadin-shrinkwrap": "14.3.0", 25 | "@vaadin/vaadin-date-time-picker": "1.2.0", 26 | "@vaadin/vaadin-ordered-layout": "1.3.0", 27 | "@vaadin/vaadin-login": "1.1.0", 28 | "@vaadin/vaadin-button": "2.3.0", 29 | "@vaadin/vaadin-date-picker": "4.2.0", 30 | "@vaadin/vaadin-text-field": "2.6.2", 31 | "@vaadin/vaadin-menu-bar": "1.1.0", 32 | "@vaadin/vaadin-custom-field": "1.1.0", 33 | "@vaadin/vaadin-form-layout": "2.2.0", 34 | "@polymer/iron-list": "3.1.0", 35 | "@vaadin/vaadin-accordion": "1.1.0", 36 | "@vaadin/vaadin-confirm-dialog": "1.2.0", 37 | "@vaadin/vaadin-list-box": "1.3.0", 38 | "@vaadin/vaadin-checkbox": "2.3.0", 39 | "@vaadin/vaadin-details": "1.1.0", 40 | "@polymer/iron-icon": "3.0.1", 41 | "@vaadin/vaadin-time-picker": "2.2.0", 42 | "@vaadin/vaadin-context-menu": "4.4.0", 43 | "@vaadin/vaadin-tabs": "3.1.0", 44 | "@vaadin/vaadin-radio-button": "1.3.0", 45 | "@vaadin/vaadin-lumo-styles": "1.6.0", 46 | "@vaadin/vaadin-material-styles": "1.3.2", 47 | "@vaadin/vaadin-rich-text-editor": "1.2.0", 48 | "lit-element": "^2.2.1" 49 | }, 50 | "devDependencies": { 51 | "webpack": "4.42.0", 52 | "webpack-cli": "3.3.11", 53 | "webpack-dev-server": "3.10.3", 54 | "webpack-babel-multi-target-plugin": "2.3.3", 55 | "copy-webpack-plugin": "5.1.1", 56 | "webpack-merge": "4.2.2", 57 | "raw-loader": "3.1.0", 58 | "compression-webpack-plugin": "3.1.0", 59 | "terser": "4.6.7", 60 | "chokidar": "^3.4.0" 61 | }, 62 | "vaadin": { 63 | "dependencies": { 64 | "@polymer/polymer": "3.2.0", 65 | "@webcomponents/webcomponentsjs": "^2.2.10", 66 | "@vaadin/vaadin-crud": "1.2.1", 67 | "@vaadin/vaadin-icons": "4.3.1", 68 | "@vaadin/vaadin-grid": "5.6.6", 69 | "@vaadin/vaadin-split-layout": "4.2.0", 70 | "@vaadin/vaadin-combo-box": "5.2.0", 71 | "@vaadin/vaadin-cookie-consent": "1.1.2", 72 | "@vaadin/vaadin-core-shrinkwrap": "14.3.0", 73 | "@vaadin/vaadin-upload": "4.3.0", 74 | "@vaadin/vaadin-dialog": "2.4.2", 75 | "@vaadin/vaadin-select": "2.2.0", 76 | "@vaadin/vaadin-app-layout": "2.1.0", 77 | "@vaadin/vaadin-item": "2.2.0", 78 | "@vaadin/vaadin-board": "2.1.1", 79 | "@vaadin/vaadin-notification": "1.5.0", 80 | "@vaadin/vaadin-charts": "6.3.0", 81 | "@vaadin/vaadin-grid-pro": "2.1.1", 82 | "@vaadin/vaadin-progress-bar": "1.2.0", 83 | "@vaadin/vaadin-shrinkwrap": "14.3.0", 84 | "@vaadin/vaadin-date-time-picker": "1.2.0", 85 | "@vaadin/vaadin-ordered-layout": "1.3.0", 86 | "@vaadin/vaadin-login": "1.1.0", 87 | "@vaadin/vaadin-button": "2.3.0", 88 | "@vaadin/vaadin-date-picker": "4.2.0", 89 | "@vaadin/vaadin-text-field": "2.6.2", 90 | "@vaadin/vaadin-menu-bar": "1.1.0", 91 | "@vaadin/vaadin-custom-field": "1.1.0", 92 | "@vaadin/vaadin-form-layout": "2.2.0", 93 | "@polymer/iron-list": "3.1.0", 94 | "@vaadin/vaadin-accordion": "1.1.0", 95 | "@vaadin/vaadin-confirm-dialog": "1.2.0", 96 | "@vaadin/vaadin-list-box": "1.3.0", 97 | "@vaadin/vaadin-checkbox": "2.3.0", 98 | "@vaadin/vaadin-details": "1.1.0", 99 | "@polymer/iron-icon": "3.0.1", 100 | "@vaadin/vaadin-time-picker": "2.2.0", 101 | "@vaadin/vaadin-context-menu": "4.4.0", 102 | "@vaadin/vaadin-tabs": "3.1.0", 103 | "@vaadin/vaadin-radio-button": "1.3.0", 104 | "@vaadin/vaadin-lumo-styles": "1.6.0", 105 | "@vaadin/vaadin-material-styles": "1.3.2", 106 | "@vaadin/vaadin-rich-text-editor": "1.2.0", 107 | "lit-element": "^2.2.1" 108 | }, 109 | "devDependencies": { 110 | "webpack-babel-multi-target-plugin": "2.3.3", 111 | "copy-webpack-plugin": "5.1.1", 112 | "compression-webpack-plugin": "3.1.0", 113 | "raw-loader": "3.1.0", 114 | "webpack-cli": "3.3.11", 115 | "webpack": "4.42.0", 116 | "terser": "4.6.7", 117 | "webpack-merge": "4.2.2", 118 | "webpack-dev-server": "3.10.3", 119 | "chokidar": "^3.4.0" 120 | }, 121 | "hash": "7f1db8716ed4e10dc92bab2e55b34ca30608326186a717575db9447840a76bc1" 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /bin/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "no-name", 3 | "license": "UNLICENSED", 4 | "dependencies": { 5 | "@polymer/polymer": "3.2.0", 6 | "@webcomponents/webcomponentsjs": "^2.2.10", 7 | "@vaadin/vaadin-crud": "1.2.1", 8 | "@vaadin/vaadin-icons": "4.3.1", 9 | "@vaadin/vaadin-grid": "5.6.6", 10 | "@vaadin/vaadin-split-layout": "4.2.0", 11 | "@vaadin/vaadin-combo-box": "5.2.0", 12 | "@vaadin/vaadin-cookie-consent": "1.1.2", 13 | "@vaadin/vaadin-core-shrinkwrap": "14.3.0", 14 | "@vaadin/vaadin-upload": "4.3.0", 15 | "@vaadin/vaadin-dialog": "2.4.2", 16 | "@vaadin/vaadin-select": "2.2.0", 17 | "@vaadin/vaadin-app-layout": "2.1.0", 18 | "@vaadin/vaadin-item": "2.2.0", 19 | "@vaadin/vaadin-board": "2.1.1", 20 | "@vaadin/vaadin-notification": "1.5.0", 21 | "@vaadin/vaadin-charts": "6.3.0", 22 | "@vaadin/vaadin-grid-pro": "2.1.1", 23 | "@vaadin/vaadin-progress-bar": "1.2.0", 24 | "@vaadin/vaadin-shrinkwrap": "14.3.0", 25 | "@vaadin/vaadin-date-time-picker": "1.2.0", 26 | "@vaadin/vaadin-ordered-layout": "1.3.0", 27 | "@vaadin/vaadin-login": "1.1.0", 28 | "@vaadin/vaadin-button": "2.3.0", 29 | "@vaadin/vaadin-date-picker": "4.2.0", 30 | "@vaadin/vaadin-text-field": "2.6.2", 31 | "@vaadin/vaadin-menu-bar": "1.1.0", 32 | "@vaadin/vaadin-custom-field": "1.1.0", 33 | "@vaadin/vaadin-form-layout": "2.2.0", 34 | "@polymer/iron-list": "3.1.0", 35 | "@vaadin/vaadin-accordion": "1.1.0", 36 | "@vaadin/vaadin-confirm-dialog": "1.2.0", 37 | "@vaadin/vaadin-list-box": "1.3.0", 38 | "@vaadin/vaadin-checkbox": "2.3.0", 39 | "@vaadin/vaadin-details": "1.1.0", 40 | "@polymer/iron-icon": "3.0.1", 41 | "@vaadin/vaadin-time-picker": "2.2.0", 42 | "@vaadin/vaadin-context-menu": "4.4.0", 43 | "@vaadin/vaadin-tabs": "3.1.0", 44 | "@vaadin/vaadin-radio-button": "1.3.0", 45 | "@vaadin/vaadin-lumo-styles": "1.6.0", 46 | "@vaadin/vaadin-material-styles": "1.3.2", 47 | "@vaadin/vaadin-rich-text-editor": "1.2.0", 48 | "lit-element": "^2.2.1" 49 | }, 50 | "devDependencies": { 51 | "webpack": "4.42.0", 52 | "webpack-cli": "3.3.11", 53 | "webpack-dev-server": "3.10.3", 54 | "webpack-babel-multi-target-plugin": "2.3.3", 55 | "copy-webpack-plugin": "5.1.1", 56 | "webpack-merge": "4.2.2", 57 | "raw-loader": "3.1.0", 58 | "compression-webpack-plugin": "3.1.0", 59 | "terser": "4.6.7", 60 | "chokidar": "^3.4.0" 61 | }, 62 | "vaadin": { 63 | "dependencies": { 64 | "@polymer/polymer": "3.2.0", 65 | "@webcomponents/webcomponentsjs": "^2.2.10", 66 | "@vaadin/vaadin-crud": "1.2.1", 67 | "@vaadin/vaadin-icons": "4.3.1", 68 | "@vaadin/vaadin-grid": "5.6.6", 69 | "@vaadin/vaadin-split-layout": "4.2.0", 70 | "@vaadin/vaadin-combo-box": "5.2.0", 71 | "@vaadin/vaadin-cookie-consent": "1.1.2", 72 | "@vaadin/vaadin-core-shrinkwrap": "14.3.0", 73 | "@vaadin/vaadin-upload": "4.3.0", 74 | "@vaadin/vaadin-dialog": "2.4.2", 75 | "@vaadin/vaadin-select": "2.2.0", 76 | "@vaadin/vaadin-app-layout": "2.1.0", 77 | "@vaadin/vaadin-item": "2.2.0", 78 | "@vaadin/vaadin-board": "2.1.1", 79 | "@vaadin/vaadin-notification": "1.5.0", 80 | "@vaadin/vaadin-charts": "6.3.0", 81 | "@vaadin/vaadin-grid-pro": "2.1.1", 82 | "@vaadin/vaadin-progress-bar": "1.2.0", 83 | "@vaadin/vaadin-shrinkwrap": "14.3.0", 84 | "@vaadin/vaadin-date-time-picker": "1.2.0", 85 | "@vaadin/vaadin-ordered-layout": "1.3.0", 86 | "@vaadin/vaadin-login": "1.1.0", 87 | "@vaadin/vaadin-button": "2.3.0", 88 | "@vaadin/vaadin-date-picker": "4.2.0", 89 | "@vaadin/vaadin-text-field": "2.6.2", 90 | "@vaadin/vaadin-menu-bar": "1.1.0", 91 | "@vaadin/vaadin-custom-field": "1.1.0", 92 | "@vaadin/vaadin-form-layout": "2.2.0", 93 | "@polymer/iron-list": "3.1.0", 94 | "@vaadin/vaadin-accordion": "1.1.0", 95 | "@vaadin/vaadin-confirm-dialog": "1.2.0", 96 | "@vaadin/vaadin-list-box": "1.3.0", 97 | "@vaadin/vaadin-checkbox": "2.3.0", 98 | "@vaadin/vaadin-details": "1.1.0", 99 | "@polymer/iron-icon": "3.0.1", 100 | "@vaadin/vaadin-time-picker": "2.2.0", 101 | "@vaadin/vaadin-context-menu": "4.4.0", 102 | "@vaadin/vaadin-tabs": "3.1.0", 103 | "@vaadin/vaadin-radio-button": "1.3.0", 104 | "@vaadin/vaadin-lumo-styles": "1.6.0", 105 | "@vaadin/vaadin-material-styles": "1.3.2", 106 | "@vaadin/vaadin-rich-text-editor": "1.2.0", 107 | "lit-element": "^2.2.1" 108 | }, 109 | "devDependencies": { 110 | "webpack-babel-multi-target-plugin": "2.3.3", 111 | "copy-webpack-plugin": "5.1.1", 112 | "compression-webpack-plugin": "3.1.0", 113 | "raw-loader": "3.1.0", 114 | "webpack-cli": "3.3.11", 115 | "webpack": "4.42.0", 116 | "terser": "4.6.7", 117 | "webpack-merge": "4.2.2", 118 | "webpack-dev-server": "3.10.3", 119 | "chokidar": "^3.4.0" 120 | }, 121 | "hash": "7f1db8716ed4e10dc92bab2e55b34ca30608326186a717575db9447840a76bc1" 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/jabbour/ems/ui/view/list/ListView.java: -------------------------------------------------------------------------------- 1 | package com.jabbour.ems.ui.view.list; 2 | 3 | import com.jabbour.ems.backend.entity.Department; 4 | import com.jabbour.ems.backend.entity.Employee; 5 | import com.jabbour.ems.backend.service.DepartmentService; 6 | import com.jabbour.ems.backend.service.EmployeeService; 7 | import com.jabbour.ems.ui.MainLayout; 8 | import com.vaadin.flow.component.button.Button; 9 | import com.vaadin.flow.component.grid.Grid; 10 | import com.vaadin.flow.component.html.Div; 11 | import com.vaadin.flow.component.orderedlayout.HorizontalLayout; 12 | import com.vaadin.flow.component.orderedlayout.VerticalLayout; 13 | import com.vaadin.flow.component.textfield.TextField; 14 | import com.vaadin.flow.data.value.ValueChangeMode; 15 | import com.vaadin.flow.router.PageTitle; 16 | import com.vaadin.flow.router.Route; 17 | 18 | @Route(value="", layout = MainLayout.class) 19 | @PageTitle("Employees | Slick EMS") 20 | /** 21 | * Construct a Vaadin view to populate grid table with data. Build the 22 | * initial UI state for the user accessing the application. 23 | */ 24 | public class ListView extends VerticalLayout { 25 | private EmployeeService employeeService; 26 | private Grid grid = new Grid<>(Employee.class); 27 | private TextField filterText = new TextField(); 28 | private EmployeeForm empForm; 29 | 30 | /** 31 | * component for main view 32 | * 33 | * @param employeeService 34 | * @param departmentService 35 | */ 36 | public ListView(EmployeeService employeeService, DepartmentService departmentService) { 37 | this.employeeService = employeeService; 38 | addClassName("list-view"); // declare a CSS class name for styling support. 39 | setSizeFull(); // Set height and Width to 100% 40 | 41 | configureGrid(); 42 | getToolbar(); 43 | 44 | empForm = new EmployeeForm(departmentService.findAll()); 45 | empForm.addListener(EmployeeForm.SaveEvent.class, this::saveEmp); 46 | empForm.addListener(EmployeeForm.DeleteEvent.class, this::deleteEmp); 47 | empForm.addListener(EmployeeForm.CloseEvent.class, e -> closeEditor()); 48 | 49 | //DivContent 50 | Div content = new Div( grid, empForm);// place grid and form child components into a div element. 51 | content.addClassName("content"); 52 | content.setSizeFull(); 53 | add(getToolbar(), content);// add the filtertextfield and to the main view layout. 54 | updateList(); 55 | closeEditor(); 56 | } 57 | 58 | /*** 59 | * Event Listener method for saving an employee to its repository. 60 | * 61 | * @param event 62 | */ 63 | private void saveEmp(EmployeeForm.SaveEvent event) { 64 | employeeService.save(event.getEmployee()); 65 | updateList(); 66 | closeEditor(); 67 | } 68 | 69 | /*** 70 | * Event Listener method for deleting an employee to its repository. 71 | * 72 | * @param event 73 | */ 74 | private void deleteEmp(EmployeeForm.DeleteEvent event) { 75 | employeeService.delete(event.getEmployee()); 76 | updateList(); 77 | closeEditor(); 78 | } 79 | 80 | /*** 81 | * Method for hiding the employee form editor 82 | */ 83 | private void closeEditor() { 84 | empForm.setEmployee(null); 85 | empForm.setVisible(false); 86 | removeClassName("editing"); 87 | } 88 | 89 | /*** 90 | * Method that hides and shows the form and based on the up the employee 91 | * selected on the grid using the addvaluechange listener 92 | * 93 | * @param employee -> the employee to modify or if nothing is selected, null. 94 | */ 95 | public void editEmployee(Employee employee) { 96 | if (employee == null) { 97 | closeEditor(); 98 | } else { 99 | empForm.setEmployee(employee); 100 | empForm.setVisible(true); 101 | addClassName("editing"); 102 | } 103 | } 104 | 105 | /** 106 | * Method for configuring the grid object and populating the table 107 | */ 108 | private void configureGrid() { 109 | grid.addClassName("employee-grid"); 110 | grid.setSizeFull(); 111 | grid.removeColumnByKey("department"); 112 | grid.setColumns("firstName", "lastName", "email", "status"); 113 | grid.addColumn(emp -> { 114 | Department department = emp.getDepartment(); 115 | return department == null ? "-" : department.getName(); 116 | }).setHeader("Department"); 117 | 118 | grid.getColumns().forEach(col -> col.setAutoWidth(true)); 119 | grid.asSingleSelect().addValueChangeListener(event -> editEmployee(event.getValue())); 120 | 121 | } 122 | 123 | /** 124 | * method for setting up text filter functionality. 125 | */ 126 | private HorizontalLayout getToolbar() { 127 | filterText.setPlaceholder("Filter by name..."); 128 | filterText.setClearButtonVisible(true); 129 | filterText.setValueChangeMode(ValueChangeMode.LAZY); 130 | filterText.addValueChangeListener(e -> updateList()); 131 | Button addEmployeeButton = new Button("Add Employee"); 132 | addEmployeeButton.addClickListener(click -> addEmployee()); 133 | HorizontalLayout toolbar = new HorizontalLayout(filterText, addEmployeeButton); 134 | toolbar.addClassName("toolbar"); 135 | return toolbar; 136 | 137 | // TODO add a button/grid to add a department 138 | } 139 | 140 | /*** 141 | * Method to add a new employee 142 | */ 143 | void addEmployee() { 144 | grid.asSingleSelect().clear(); 145 | editEmployee(new Employee()); 146 | } 147 | 148 | /** 149 | * Return all employees from the service after an event is triggered and pass 150 | * them into the grid. filter text will look for first or last name of employee 151 | */ 152 | private void updateList() { 153 | grid.setItems(employeeService.findAll(filterText.getValue())); 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /bin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.jabbour.ems 4 | my-ems-project 5 | My EMS Project 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | 1.8 11 | 1.8 12 | UTF-8 13 | UTF-8 14 | 15 | 19.0.6 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-parent 22 | 2.2.0.RELEASE 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | central 31 | https://repo.maven.apache.org/maven2 32 | 33 | false 34 | 35 | 36 | 37 | 38 | Vaadin Directory 39 | https://maven.vaadin.com/vaadin-addons 40 | 41 | false 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | central 50 | https://repo.maven.apache.org/maven2 51 | 52 | false 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | com.vaadin 61 | vaadin-bom 62 | ${vaadin.version} 63 | pom 64 | import 65 | 66 | 67 | 68 | 69 | 70 | 71 | com.vaadin 72 | 73 | vaadin 74 | 75 | 76 | 77 | com.vaadin.webjar 78 | * 79 | 80 | 81 | org.webjars.bowergithub.insites 82 | * 83 | 84 | 85 | org.webjars.bowergithub.polymer 86 | * 87 | 88 | 89 | org.webjars.bowergithub.polymerelements 90 | * 91 | 92 | 93 | org.webjars.bowergithub.vaadin 94 | * 95 | 96 | 97 | org.webjars.bowergithub.webcomponents 98 | * 99 | 100 | 101 | 102 | 103 | com.vaadin 104 | vaadin-spring-boot-starter 105 | 106 | 107 | 108 | com.vaadin 109 | vaadin-core 110 | 111 | 112 | 113 | 114 | org.springframework.boot 115 | spring-boot-devtools 116 | true 117 | 118 | 119 | com.vaadin 120 | vaadin-testbench 121 | test 122 | 123 | 124 | io.github.bonigarcia 125 | webdrivermanager 126 | 3.8.1 127 | test 128 | 129 | 130 | 131 | 132 | spring-boot:run 133 | 134 | 135 | org.springframework.boot 136 | spring-boot-maven-plugin 137 | 139 | 140 | 500 141 | 240 142 | 143 | 144 | 145 | 150 | 151 | com.vaadin 152 | vaadin-maven-plugin 153 | ${vaadin.version} 154 | 155 | 156 | 157 | prepare-frontend 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | production 169 | 170 | true 171 | 172 | 173 | 174 | 175 | com.vaadin 176 | flow-server-production-mode 177 | 178 | 179 | 180 | 181 | 182 | 183 | org.springframework.boot 184 | spring-boot-maven-plugin 185 | 186 | -Dvaadin.productionMode 187 | 188 | 189 | 190 | com.vaadin 191 | vaadin-maven-plugin 192 | ${vaadin.version} 193 | 194 | 195 | 196 | build-frontend 197 | 198 | compile 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | it 208 | 209 | 210 | 211 | org.springframework.boot 212 | spring-boot-maven-plugin 213 | 214 | 215 | start-spring-boot 216 | pre-integration-test 217 | 218 | start 219 | 220 | 221 | 222 | stop-spring-boot 223 | post-integration-test 224 | 225 | stop 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | org.apache.maven.plugins 234 | maven-failsafe-plugin 235 | 236 | 237 | 238 | integration-test 239 | verify 240 | 241 | 242 | 243 | 244 | false 245 | true 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4.0.0 3 | com.jabbour.ems 4 | my-ems-project 5 | My EMS Project 6 | 1.0-SNAPSHOT 7 | jar 8 | 9 | 10 | 1.8 11 | 1.8 12 | UTF-8 13 | UTF-8 14 | 15 | 19.0.6 16 | 17 | 18 | 19 | 20 | org.springframework.boot 21 | spring-boot-starter-parent 22 | 2.2.0.RELEASE 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | central 31 | https://repo.maven.apache.org/maven2 32 | 33 | false 34 | 35 | 36 | 37 | 38 | Vaadin Directory 39 | https://maven.vaadin.com/vaadin-addons 40 | 41 | false 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | central 50 | https://repo.maven.apache.org/maven2 51 | 52 | false 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | com.vaadin 61 | vaadin-bom 62 | ${vaadin.version} 63 | pom 64 | import 65 | 66 | 67 | 68 | 69 | 70 | 71 | com.vaadin 72 | 73 | vaadin 74 | 75 | 76 | 77 | com.vaadin.webjar 78 | * 79 | 80 | 81 | org.webjars.bowergithub.insites 82 | * 83 | 84 | 85 | org.webjars.bowergithub.polymer 86 | * 87 | 88 | 89 | org.webjars.bowergithub.polymerelements 90 | * 91 | 92 | 93 | org.webjars.bowergithub.vaadin 94 | * 95 | 96 | 97 | org.webjars.bowergithub.webcomponents 98 | * 99 | 100 | 101 | 102 | 103 | com.vaadin 104 | vaadin-spring-boot-starter 105 | 106 | 107 | 108 | com.vaadin 109 | vaadin-core 110 | 111 | 112 | 113 | 114 | org.springframework.boot 115 | spring-boot-devtools 116 | true 117 | 118 | 119 | org.springframework.security 120 | spring-security-web 121 | 122 | 123 | org.springframework.security 124 | spring-security-config 125 | 126 | 131 | 132 | io.github.bonigarcia 133 | webdrivermanager 134 | 3.8.1 135 | test 136 | 137 | 138 | org.springframework.boot 139 | spring-boot-starter-data-jpa 140 | 141 | 142 | com.h2database 143 | h2 144 | runtime 145 | 146 | 147 | 148 | org.springframework.boot 149 | spring-boot-starter-actuator 150 | 151 | 152 | 153 | 154 | spring-boot:run 155 | 156 | 157 | org.springframework.boot 158 | spring-boot-maven-plugin 159 | 161 | 162 | 500 163 | 240 164 | 165 | 166 | 167 | 172 | 173 | com.vaadin 174 | vaadin-maven-plugin 175 | ${vaadin.version} 176 | 177 | 178 | 179 | prepare-frontend 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | production 191 | 192 | true 193 | 194 | 195 | 196 | 197 | com.vaadin 198 | flow-server-production-mode 199 | 200 | 201 | 202 | 203 | 204 | 205 | org.springframework.boot 206 | spring-boot-maven-plugin 207 | 208 | -Dvaadin.productionMode 209 | 210 | 211 | 212 | com.vaadin 213 | vaadin-maven-plugin 214 | ${vaadin.version} 215 | 216 | 217 | 218 | build-frontend 219 | 220 | compile 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | it 230 | 231 | 232 | 233 | org.springframework.boot 234 | spring-boot-maven-plugin 235 | 236 | 237 | start-spring-boot 238 | pre-integration-test 239 | 240 | start 241 | 242 | 243 | 244 | stop-spring-boot 245 | post-integration-test 246 | 247 | stop 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | org.apache.maven.plugins 256 | maven-failsafe-plugin 257 | 258 | 259 | 260 | integration-test 261 | verify 262 | 263 | 264 | 265 | 266 | false 267 | true 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | --------------------------------------------------------------------------------