// Application.java package com.aksenov.monitor; import com.aksenov.monitor.repositories.CityRepository; import com.vaadin.flow.component.page.AppShellConfigurator; import com.vaadin.flow.theme.Theme; import javax.sql.DataSource; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.sql.init.SqlDataSourceScriptDatabaseInitializer; import org.springframework.boot.autoconfigure.sql.init.SqlInitializationProperties; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; /** * The entry point of the Spring Boot application. * * Use the @PWA annotation make the application installable on phones, tablets * and some desktop browsers. * */ @SpringBootApplication @ComponentScan(basePackages = {"com.aksenov.monitor"}) @Theme(value = "monitor") public class Application implements AppShellConfigurator { public static void main(String[] args) { SpringApplication.run(Application.class, args); } @Bean SqlDataSourceScriptDatabaseInitializer dataSourceScriptDatabaseInitializer(DataSource dataSource, SqlInitializationProperties properties, CityRepository repository) { // This bean ensures the database is only initialized when empty return new SqlDataSourceScriptDatabaseInitializer(dataSource, properties) { @Override public boolean initializeDatabase() { if (repository.count() == 0L) { return super.initializeDatabase(); } return false; } }; } } // AbstractEntity.java package com.aksenov.monitor.data; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; import jakarta.persistence.MappedSuperclass; import jakarta.persistence.SequenceGenerator; import jakarta.persistence.Version; @MappedSuperclass public abstract class AbstractEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "idgenerator") // The initial value is to account for data.sql demo data ids @SequenceGenerator(name = "idgenerator", initialValue = 1000) private Long id; @Version private int version; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public int getVersion() { return version; } @Override public int hashCode() { if (getId() != null) { return getId().hashCode(); } return super.hashCode(); } @Override public boolean equals(Object obj) { if (!(obj instanceof AbstractEntity that)) { return false; // null or not an AbstractEntity class } if (getId() != null) { return getId().equals(that.getId()); } return super.equals(that); } } // City.java package com.aksenov.monitor.data; import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; @Entity @Data @Table(name = "City") public class City extends AbstractEntity { @Column(name = "name") private String name; public String getName() { return name; } public void setName(String name) { this.name = name; } } // Customer.java package com.aksenov.monitor.data; import jakarta.persistence.*; import jakarta.validation.constraints.Email; import lombok.Data; @Entity @Data @Table(name = "Customer") public class Customer extends AbstractEntity { private String name; @Email private String email; @ManyToOne(cascade = CascadeType.DETACH) @JoinColumn(name = "city_id", nullable = false) private City city; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public City getCity() { return city; } public void setCityID(City cityID) { this.city = cityID; } public String getCityName() { return city.getName(); } } // Manufacturer.java package com.aksenov.monitor.data; import jakarta.persistence.Entity; import jakarta.persistence.Table; import lombok.Data; @Entity @Data @Table(name = "Manufacturer") public class Manufacturer extends AbstractEntity { private String name; private String country; private String contactInfo; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } public String getContactInfo() { return contactInfo; } public void setContactInfo(String contactInfo) { this.contactInfo = contactInfo; } } // Monitor.java package com.aksenov.monitor.data; import jakarta.persistence.*; import lombok.Data; @Entity @Data @Table(name = "Monitor") public class Monitor extends AbstractEntity { @Column(name = "model") private String model; @ManyToOne(cascade = CascadeType.DETACH) @JoinColumn(name = "manufacturer_id", nullable = false) private Manufacturer manufacturer; private Integer screenSize; @Column(name = "resolution") private String resolution; private Integer refreshrRate; private Integer price; public String getModel() { return model; } public void setModel(String model) { this.model = model; } public Manufacturer getManufacturer() { return manufacturer; } public void setManufacturer(Manufacturer manufacturer) { this.manufacturer = manufacturer; } public Integer getScreenSize() { return screenSize; } public void setScreenSize(Integer screenSize) { this.screenSize = screenSize; } public String getResolution() { return resolution; } public void setResolution(String resolution) { this.resolution = resolution; } public Integer getRefreshrRate() { return refreshrRate; } public void setRefreshrRate(Integer refreshrRate) { this.refreshrRate = refreshrRate; } public Integer getPrice() { return price; } public void setPrice(Integer price) { this.price = price; } } // package-info.java @NonNullApi package com.aksenov.monitor.data; import org.springframework.lang.NonNullApi; // Sales.java package com.aksenov.monitor.data; import jakarta.persistence.*; import lombok.Data; import java.time.LocalDateTime; @Entity @Data @Table(name = "Sales") public class Sales extends AbstractEntity { @ManyToOne(cascade = CascadeType.DETACH) @JoinColumn(name = "monitor_id", nullable = false) private Monitor monitor; @ManyToOne(cascade = CascadeType.DETACH) @JoinColumn(name = "customer_id", nullable = false) private Customer customer; private LocalDateTime saleDate; private Integer quantity; private Integer totalPrice; public Monitor getMonitor() { return monitor; } public void setMonitor(Monitor monitor) { this.monitor = monitor; } public Customer getCustomer() { return customer; } public void setCustomer(Customer customer) { this.customer = customer; } public LocalDateTime getSaleDate() { return saleDate; } public void setSaleDate(LocalDateTime saleDate) { this.saleDate = saleDate; } public Integer getQuantity() { return quantity; } public void setQuantity(Integer quantity) { this.quantity = quantity; } public Integer getTotalPrice() { return totalPrice; } public void setTotalPrice(Integer totalPrice) { this.totalPrice = totalPrice; } } // CityRepository.java package com.aksenov.monitor.repositories; import com.aksenov.monitor.data.City; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.stereotype.Repository; import java.util.List; @Repository public interface CityRepository extends JpaRepository, JpaSpecificationExecutor { } // CustomerRepository.java package com.aksenov.monitor.repositories; import com.aksenov.monitor.data.Customer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface CustomerRepository extends JpaRepository, JpaSpecificationExecutor { } // ManufacturerRepository.java package com.aksenov.monitor.repositories; import com.aksenov.monitor.data.Manufacturer; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface ManufacturerRepository extends JpaRepository, JpaSpecificationExecutor { } // MonitorRepository.java package com.aksenov.monitor.repositories; import com.aksenov.monitor.data.Monitor; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; public interface MonitorRepository extends JpaRepository, JpaSpecificationExecutor { } // SalesRepository.java package com.aksenov.monitor.repositories; import com.aksenov.monitor.data.Sales; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import java.util.List; import java.util.Map; public interface SalesRepository extends JpaRepository, JpaSpecificationExecutor { @Query("SELECT s FROM Sales s WHERE FUNCTION('MONTH', s.saleDate) = :month AND FUNCTION('YEAR', s.saleDate) = :year") List findAllBySaleDateMonthAndYear(@Param("month") int month, @Param("year") int year); @Query("SELECT SUM(s.totalPrice) FROM Sales s WHERE FUNCTION('MONTH', s.saleDate) = :month AND FUNCTION('YEAR', s.saleDate) = :year") Double findTotalSalesByMonthAndYear(@Param("month") int month, @Param("year") int year); @Query("SELECT m.model, SUM(s.totalPrice) FROM Sales s JOIN s.monitor m GROUP BY m.model") List getSalesStatisticsByModel(); } // CityService.java package com.aksenov.monitor.services; import com.aksenov.monitor.data.City; import com.aksenov.monitor.repositories.CityRepository; import java.util.List; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @Service public class CityService { private final CityRepository repository; @Autowired public CityService(CityRepository repository) { this.repository = repository; } public Optional get(Long id) { return repository.findById(id); } public City save(City entity) { return repository.save(entity); } public void delete(Long id) { repository.deleteById(id); } public Page list(Pageable pageable) { return repository.findAll(pageable); } public Page list(Pageable pageable, Specification filter) { return repository.findAll(filter, pageable); } public int count() { return (int) repository.count(); } public List list() { return repository.findAll(); } } // CustomerService.java package com.aksenov.monitor.services; import com.aksenov.monitor.data.Customer; import com.aksenov.monitor.repositories.CustomerRepository; import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @Service public class CustomerService { private final CustomerRepository repository; public CustomerService(CustomerRepository repository) { this.repository = repository; } public Optional get(Long id) { return repository.findById(id); } public Customer save(Customer entity) { return repository.save(entity); } public void delete(Long id) { repository.deleteById(id); } public Page list(Pageable pageable) { return repository.findAll(pageable); } public Page list(Pageable pageable, Specification filter) { return repository.findAll(filter, pageable); } public int count() { return (int) repository.count(); } public List list() { return repository.findAll(); } } // ManufacturerService.java package com.aksenov.monitor.services; import com.aksenov.monitor.data.Manufacturer; import com.aksenov.monitor.repositories.ManufacturerRepository; import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @Service public class ManufacturerService { private final ManufacturerRepository repository; public ManufacturerService(ManufacturerRepository repository) { this.repository = repository; } public Optional get(Long id) { return repository.findById(id); } public Manufacturer save(Manufacturer entity) { return repository.save(entity); } public void delete(Long id) { repository.deleteById(id); } public Page list(Pageable pageable) { return repository.findAll(pageable); } public Page list(Pageable pageable, Specification filter) { return repository.findAll(filter, pageable); } public List list() { return repository.findAll(); } public int count() { return (int) repository.count(); } } // MonitorService.java package com.aksenov.monitor.services; import com.aksenov.monitor.data.Customer; import com.aksenov.monitor.data.Monitor; import com.aksenov.monitor.repositories.MonitorRepository; import java.util.List; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @Service public class MonitorService { private final MonitorRepository repository; public MonitorService(MonitorRepository repository) { this.repository = repository; } public Optional get(Long id) { return repository.findById(id); } public Monitor save(Monitor entity) { return repository.save(entity); } public void delete(Long id) { repository.deleteById(id); } public Page list(Pageable pageable) { return repository.findAll(pageable); } public Page list(Pageable pageable, Specification filter) { return repository.findAll(filter, pageable); } public int count() { return (int) repository.count(); } public List list() { return repository.findAll(); } } // package-info.java @NonNullApi package com.aksenov.monitor.services; import org.springframework.lang.NonNullApi; // SalesService.java package com.aksenov.monitor.services; import com.aksenov.monitor.data.Sales; import com.aksenov.monitor.repositories.SalesRepository; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; @Service public class SalesService { private final SalesRepository repository; public SalesService(SalesRepository repository) { this.repository = repository; } public Optional get(Long id) { return repository.findById(id); } public Sales save(Sales entity) { return repository.save(entity); } public void delete(Long id) { repository.deleteById(id); } public Page list(Pageable pageable) { return repository.findAll(pageable); } public Page list(Pageable pageable, Specification filter) { return repository.findAll(filter, pageable); } public int count() { return (int) repository.count(); } public List list() { return repository.findAll(); } public List findSalesByMonthAndYear(int month, int year) { return repository.findAllBySaleDateMonthAndYear(month, year); } public Double getTotalSalesForMonthAndYear(int month, int year) { return repository.findTotalSalesByMonthAndYear(month, year); } public Map getSalesDataByModel() { Map salesData = new HashMap<>(); List results = repository.getSalesStatisticsByModel(); for (Object[] result : results) { String model = (String) result[0]; Long total = (Long) result[1]; salesData.put(model, total); } return salesData; } } // CityView.java package com.aksenov.monitor.views.city; import com.aksenov.monitor.data.City; import com.aksenov.monitor.data.Customer; import com.aksenov.monitor.services.CityService; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.datepicker.DatePicker; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.grid.HeaderRow; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification.Position; import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.splitlayout.SplitLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.binder.BeanValidationBinder; import com.vaadin.flow.data.binder.ValidationException; import com.vaadin.flow.data.provider.ListDataProvider; import com.vaadin.flow.data.value.ValueChangeMode; import com.vaadin.flow.function.SerializablePredicate; import com.vaadin.flow.router.BeforeEnterEvent; import com.vaadin.flow.router.BeforeEnterObserver; import com.vaadin.flow.router.Menu; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import com.vaadin.flow.router.RouteAlias; import com.vaadin.flow.spring.data.VaadinSpringDataHelpers; import java.util.Arrays; import java.util.List; import java.util.Optional; import org.apache.commons.lang3.StringUtils; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.vaadin.lineawesome.LineAwesomeIconUrl; @PageTitle("City") @Route("/:cityID?/:action?(edit)") @Menu(order = 0, icon = LineAwesomeIconUrl.CITY_SOLID) @RouteAlias("") public class CityView extends Div implements BeforeEnterObserver { private final String CITY_ID = "cityID"; private final String CITY_EDIT_ROUTE_TEMPLATE = "/%s/edit"; private final Grid grid = new Grid<>(City.class, false); private TextField name; private final Button cancel = new Button("Cancel"); private final Button save = new Button("Save"); private final Button delete = new Button("Delete"); private final BeanValidationBinder binder; private City city; private List cities; private final CityService cityService; private ListDataProvider dataProvider; public CityView(CityService cityService) { this.cityService = cityService; addClassNames("city-view"); // Create UI SplitLayout splitLayout = new SplitLayout(); createGridLayout(splitLayout); createEditorLayout(splitLayout); add(splitLayout); // Configure Grid grid.addColumn("name").setAutoWidth(true); //grid.setItems(query -> cityService.list(VaadinSpringDataHelpers.toSpringPageRequest(query)).stream()); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); cities = cityService.list(); dataProvider = new ListDataProvider<>(cities); grid.setDataProvider(dataProvider); // when a row is selected or deselected, populate form grid.asSingleSelect().addValueChangeListener(event -> { if (event.getValue() != null) { UI.getCurrent().navigate(String.format(CITY_EDIT_ROUTE_TEMPLATE, event.getValue().getId())); } else { clearForm(); UI.getCurrent().navigate(CityView.class); } }); // Configure Form binder = new BeanValidationBinder<>(City.class); // Bind fields. This is where you'd define e.g. validation rules binder.bindInstanceFields(this); addFilterRow(); cancel.addClickListener(e -> { clearForm(); refreshGrid(); }); delete.addClickListener(e -> { try { if (this.city == null) { return; } binder.writeBean(this.city); cityService.delete(this.city.getId()); clearForm(); refreshGrid(); Notification.show("Data deleted"); UI.getCurrent().navigate(CityView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); save.addClickListener(e -> { try { if (this.city == null) { this.city = new City(); } if (name.isEmpty() ) { Notification.show("Please fill all required fields"); return; } binder.writeBean(this.city); cityService.save(this.city); clearForm(); refreshGrid(); Notification.show("Data updated"); UI.getCurrent().navigate(CityView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); } @Override public void beforeEnter(BeforeEnterEvent event) { Optional cityId = event.getRouteParameters().get(CITY_ID).map(Long::parseLong); if (cityId.isPresent()) { Optional cityFromBackend = cityService.get(cityId.get()); if (cityFromBackend.isPresent()) { populateForm(cityFromBackend.get()); } else { Notification.show(String.format("The requested city was not found, ID = %s", cityId.get()), 3000, Notification.Position.BOTTOM_START); // when a row is selected but the data is no longer available, // refresh grid refreshGrid(); event.forwardTo(CityView.class); } } } private void addFilterRow() { HeaderRow headerRow = grid.appendHeaderRow(); // Filter for name column TextField nameFilter = new TextField(); nameFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if (filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(city -> city.getName().toLowerCase().contains(filterValue.toLowerCase())); } // Clear other filters clearOtherFilters(nameFilter); clearForm(); refreshGrid(); }); nameFilter.setPlaceholder("Filter"); nameFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("name")).setComponent(nameFilter); } private void clearOtherFilters(TextField currentFilter) { if (currentFilter != nameFilter) { nameFilter.clear(); } } private void createEditorLayout(SplitLayout splitLayout) { Div editorLayoutDiv = new Div(); editorLayoutDiv.setClassName("editor-layout"); Div editorDiv = new Div(); editorDiv.setClassName("editor"); editorLayoutDiv.add(editorDiv); FormLayout formLayout = new FormLayout(); name = new TextField("Name"); formLayout.add(name); editorDiv.add(formLayout); createButtonLayout(editorLayoutDiv); splitLayout.addToSecondary(editorLayoutDiv); } private void createButtonLayout(Div editorLayoutDiv) { HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.setClassName("button-layout"); cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY); save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); delete.addThemeVariants(ButtonVariant.LUMO_PRIMARY); buttonLayout.add(save, cancel, delete); editorLayoutDiv.add(buttonLayout); } private void createGridLayout(SplitLayout splitLayout) { Div wrapper = new Div(); wrapper.setClassName("grid-wrapper"); splitLayout.addToPrimary(wrapper); wrapper.add(grid); } private void refreshGrid() { SerializablePredicate currentFilter = dataProvider.getFilter(); // 2. Обновить список cities = cityService.list(); // 3. Создать новый dataProvider и применить фильтр dataProvider = new ListDataProvider<>(cities); grid.setDataProvider(dataProvider); dataProvider.setFilter(currentFilter); // grid.select(null); dataProvider.refreshAll(); } private void clearForm() { populateForm(null); } private void populateForm(City value) { this.city = value; binder.readBean(this.city); } private TextField nameFilter; } // CustomerView.java package com.aksenov.monitor.views.customer; import com.aksenov.monitor.data.City; import com.aksenov.monitor.data.Customer; import com.aksenov.monitor.services.CityService; import com.aksenov.monitor.services.CustomerService; import com.aksenov.monitor.views.city.CityView; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.grid.HeaderRow; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification.Position; import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.splitlayout.SplitLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.binder.BeanValidationBinder; import com.vaadin.flow.data.binder.ValidationException; import com.vaadin.flow.data.provider.ListDataProvider; import com.vaadin.flow.function.SerializablePredicate; import com.vaadin.flow.router.BeforeEnterEvent; import com.vaadin.flow.router.BeforeEnterObserver; import com.vaadin.flow.router.Menu; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import java.util.List; import java.util.Optional; import java.util.logging.Filter; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.vaadin.lineawesome.LineAwesomeIconUrl; @PageTitle("Customer") @Route("customer/:customerID?/:action?(edit)") @Menu(order = 1, icon = LineAwesomeIconUrl.ADDRESS_CARD) public class CustomerView extends Div implements BeforeEnterObserver { private final String CUSTOMER_ID = "customerID"; private final String CUSTOMER_EDIT_ROUTE_TEMPLATE = "customer/%s/edit"; private final Grid grid = new Grid<>(Customer.class, false); private TextField name; private TextField email; private ComboBox city; private final Button cancel = new Button("Cancel"); private final Button save = new Button("Save"); private final Button delete = new Button("Delete"); private final BeanValidationBinder binder; private Customer customer; private List customers; private final CustomerService customerService; private final CityService cityService; private ListDataProvider dataProvider; public CustomerView(CustomerService customerService, CityService cityService) { this.customerService = customerService; this.cityService = cityService; addClassNames("customer-view"); // Create UI SplitLayout splitLayout = new SplitLayout(); createGridLayout(splitLayout); createEditorLayout(splitLayout); add(splitLayout); // Configure Grid grid.addColumn("name").setAutoWidth(true); grid.addColumn("email").setAutoWidth(true); grid.addColumn("city.name").setAutoWidth(true).setHeader("City"); //grid.setItems(query -> customerService.list(VaadinSpringDataHelpers.toSpringPageRequest(query)).stream()); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); customers = customerService.list(); dataProvider = new ListDataProvider<>(customers); grid.setDataProvider(dataProvider); // when a row is selected or deselected, populate form grid.asSingleSelect().addValueChangeListener(event -> { if (event.getValue() != null) { UI.getCurrent().navigate(String.format(CUSTOMER_EDIT_ROUTE_TEMPLATE, event.getValue().getId())); } else { clearForm(); UI.getCurrent().navigate(CustomerView.class); } }); // Configure Form binder = new BeanValidationBinder<>(Customer.class); addFilterRow(); // Bind fields. This is where you'd define e.g. validation rules binder.bindInstanceFields(this); cancel.addClickListener(e -> { clearForm(); refreshGrid(); }); save.addClickListener(e -> { try { if (this.customer == null) { this.customer = new Customer(); } if (name.isEmpty() || email.isEmpty() || city.isEmpty() ) { Notification.show("Please fill all required fields"); return; } binder.writeBean(this.customer); customerService.save(this.customer); clearForm(); refreshGrid(); Notification.show("Data updated"); UI.getCurrent().navigate(CustomerView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); delete.addClickListener(e -> { try { if (this.city == null) { return; } binder.writeBean(this.customer); customerService.delete(this.customer.getId()); clearForm(); refreshGrid(); Notification.show("Data deleted"); UI.getCurrent().navigate(CustomerView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); } @Override public void beforeEnter(BeforeEnterEvent event) { Optional customerId = event.getRouteParameters().get(CUSTOMER_ID).map(Long::parseLong); if (customerId.isPresent()) { Optional customerFromBackend = customerService.get(customerId.get()); if (customerFromBackend.isPresent()) { populateForm(customerFromBackend.get()); } else { Notification.show(String.format("The requested customer was not found, ID = %s", customerId.get()), 3000, Notification.Position.BOTTOM_START); // when a row is selected but the data is no longer available, // refresh grid refreshGrid(); event.forwardTo(CustomerView.class); } } } private void addFilterRow() { HeaderRow headerRow = grid.appendHeaderRow(); // Filter for name column nameFilter = new TextField(); nameFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if (filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(customer -> customer.getName().toLowerCase().contains(filterValue.toLowerCase())); } clearOtherFilters(nameFilter); clearForm(); refreshGrid(); }); nameFilter.setPlaceholder("Filter"); nameFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("name")).setComponent(nameFilter); // Filter for email column emailFilter = new TextField(); emailFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(customer -> customer.getEmail().toLowerCase().contains(filterValue.toLowerCase())); } clearOtherFilters(emailFilter); clearForm(); refreshGrid(); }); emailFilter.setPlaceholder("Filter"); emailFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("email")).setComponent(emailFilter); // Filter for cityName column cityFilter = new TextField(); cityFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); dataProvider.setFilter(customer -> customer.getCityName() != null && customer.getCityName().toLowerCase().contains(filterValue.toLowerCase())); clearOtherFilters(cityFilter); clearForm(); refreshGrid(); }); cityFilter.setPlaceholder("Filter"); cityFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("city.name")).setComponent(cityFilter); } private void clearOtherFilters(TextField currentFilter) { if (currentFilter != nameFilter) { nameFilter.clear(); } if (currentFilter != emailFilter) { emailFilter.clear(); } if (currentFilter != cityFilter) { cityFilter.clear(); } } private void createEditorLayout(SplitLayout splitLayout) { Div editorLayoutDiv = new Div(); editorLayoutDiv.setClassName("editor-layout"); Div editorDiv = new Div(); editorDiv.setClassName("editor"); editorLayoutDiv.add(editorDiv); FormLayout formLayout = new FormLayout(); name = new TextField("Name"); email = new TextField("Email"); city = new ComboBox("City"); city.setItemLabelGenerator(currCity -> String.valueOf(currCity.getId())); city.setAllowCustomValue(false); city.addFocusListener(event -> { List cityList = cityService.list(); city.setItems(cityList); }); city.setItemLabelGenerator(City::getName); formLayout.add(name, email, city); editorDiv.add(formLayout); createButtonLayout(editorLayoutDiv); splitLayout.addToSecondary(editorLayoutDiv); } private void createButtonLayout(Div editorLayoutDiv) { HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.setClassName("button-layout"); cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY); save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); delete.addThemeVariants(ButtonVariant.LUMO_PRIMARY); buttonLayout.add(save, cancel, delete); editorLayoutDiv.add(buttonLayout); } private void createGridLayout(SplitLayout splitLayout) { Div wrapper = new Div(); wrapper.setClassName("grid-wrapper"); splitLayout.addToPrimary(wrapper); wrapper.add(grid); } private void refreshGrid() { SerializablePredicate currentFilter = dataProvider.getFilter(); // 2. Обновить список customers = customerService.list(); // 3. Создать новый dataProvider и применить фильтр dataProvider = new ListDataProvider<>(customers); grid.setDataProvider(dataProvider); dataProvider.setFilter(currentFilter); // grid.select(null); dataProvider.refreshAll(); } private void clearForm() { populateForm(null); } private void populateForm(Customer value) { this.customer = value; // Загрузить список городов List cityList = cityService.list(); // Установить список городов в ComboBox city.setItems(cityList); // Установить значения клиента binder.readBean(this.customer); if (value != null && value.getCity() != null) { city.setValue(value.getCity()); } else { city.clear(); } } private TextField nameFilter; private TextField emailFilter; private TextField cityFilter; } // DashboardView.java package com.aksenov.monitor.views.dashboard; import com.aksenov.monitor.data.Sales; import com.aksenov.monitor.repositories.*; import com.aksenov.monitor.services.SalesService; import com.aksenov.monitor.views.dashboard.ServiceHealth.Status; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.board.Board; import com.vaadin.flow.component.charts.Chart; import com.vaadin.flow.component.charts.model.*; import com.vaadin.flow.component.grid.ColumnTextAlign; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.html.H2; import com.vaadin.flow.component.html.Main; import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.icon.Icon; import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.component.select.Select; import com.vaadin.flow.data.renderer.ComponentRenderer; import com.vaadin.flow.router.Menu; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.theme.lumo.LumoUtility.*; import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; import org.vaadin.lineawesome.LineAwesomeIconUrl; @PageTitle("Dashboard") @Route("dashboard") @Menu(order = 6, icon = LineAwesomeIconUrl.CHART_AREA_SOLID) @SpringComponent @Scope("prototype") public class DashboardView extends Main { private final CityRepository cityRepository; private final CustomerRepository customerRepository; private final ManufacturerRepository manufacturerRepository; private final MonitorRepository monitorRepository; private final SalesService salesService; private final SalesRepository salesRepository; public DashboardView(CityRepository cityRepository, CustomerRepository customerRepository, ManufacturerRepository manufacturerRepository, MonitorRepository monitorRepository, SalesService salesService, SalesRepository salesRepository) { this.cityRepository = cityRepository; this.customerRepository = customerRepository; this.manufacturerRepository = manufacturerRepository; this.monitorRepository = monitorRepository; this.salesService = salesService; this.salesRepository = salesRepository; addClassName("dashboard-view"); Board board = new Board(); board.addRow( createHighlight("Current amount of sales", String.valueOf(salesRepository.findTotalSalesByMonthAndYear(LocalDateTime.now().getMonthValue(), LocalDateTime.now().getYear())), 33.7), createHighlight("Current sales", String.valueOf((long) salesRepository.findAllBySaleDateMonthAndYear(LocalDateTime.now().getMonthValue(), LocalDateTime.now().getYear()).size()), 33.7), createHighlight("Current customer", String.valueOf(customerRepository.count()), -112.45)); board.addRow(createViewEvents()); board.addRow(createResponseTimes()); add(board); } private List getSalesSeries(int year) { List seriesList = new ArrayList<>(); String[] categories = new String[]{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; List dataSeriesItems = new ArrayList<>(); for (int i = 0; i < 12; i++) { List sales = salesService.findSalesByMonthAndYear(i + 1, year); int salesCount = sales != null ? sales.size() : 0; dataSeriesItems.add(new DataSeriesItem(categories[i], salesCount)); } DataSeries dataSeries = new DataSeries(); dataSeries.setData(dataSeriesItems); seriesList.add(dataSeries); return seriesList; } private Component createHighlight(String title, String value, Double percentage) { VaadinIcon icon = VaadinIcon.ARROW_UP; String prefix = ""; String theme = "badge"; if (percentage == 0) { prefix = "±"; } else if (percentage > 0) { prefix = "+"; theme += " success"; } else if (percentage < 0) { icon = VaadinIcon.ARROW_DOWN; theme += " error"; } H2 h2 = new H2(title); h2.addClassNames(FontWeight.NORMAL, Margin.NONE, TextColor.SECONDARY, FontSize.XSMALL); Span span = new Span(value); span.addClassNames(FontWeight.SEMIBOLD, FontSize.XXXLARGE); Icon i = icon.create(); i.addClassNames(BoxSizing.BORDER, Padding.XSMALL); Span badge = new Span(i, new Span(prefix + percentage.toString())); badge.getElement().getThemeList().add(theme); VerticalLayout layout = new VerticalLayout(h2, span, badge); layout.addClassName(Padding.LARGE); layout.setPadding(false); layout.setSpacing(false); return layout; } private Component createViewEvents() { // Header Select year = new Select<>(); year.setItems("2011", "2012", "2013", "2014", "2015", "2016", "2017", "2018", "2019", "2020", "2021", "2022", "2023", "2024", "2025"); year.setValue("2024"); year.setWidth("100px"); // Chart Chart chart = new Chart(ChartType.AREASPLINE); Configuration conf = chart.getConfiguration(); conf.getChart().setStyledMode(true); XAxis xAxis = new XAxis(); xAxis.setCategories("Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"); conf.addxAxis(xAxis); conf.getyAxis().setTitle("Values"); PlotOptionsAreaspline plotOptions = new PlotOptionsAreaspline(); plotOptions.setPointPlacement(PointPlacement.ON); plotOptions.setMarker(new Marker(false)); conf.addPlotOptions(plotOptions); // Load data for sales chart. List salesSeries = getSalesSeries(Integer.parseInt(year.getValue())); conf.setSeries(salesSeries); year.addValueChangeListener(event -> { chart.getConfiguration().setSeries(getSalesSeries(Integer.parseInt(event.getValue()))); chart.drawChart(); }); // Add it all together HorizontalLayout header = createHeader("Sales events", "Sales/month"); header.add(year); VerticalLayout viewEvents = new VerticalLayout(header, chart); viewEvents.addClassName(Padding.LARGE); viewEvents.setPadding(false); viewEvents.setSpacing(false); viewEvents.getElement().getThemeList().add("spacing-l"); return viewEvents; } private Component createResponseTimes() { HorizontalLayout header = createHeader("Sales by Model", "Average across all models"); // Chart Chart chart = new Chart(ChartType.PIE); Configuration conf = chart.getConfiguration(); conf.getChart().setStyledMode(true); chart.setThemeName("gradient"); DataSeries series = new DataSeries(); Map salesData = salesService.getSalesDataByModel(); for (Map.Entry entry : salesData.entrySet()) { series.add(new DataSeriesItem(entry.getKey(), entry.getValue())); } conf.addSeries(series); // Add it all together VerticalLayout serviceHealth = new VerticalLayout(header, chart); serviceHealth.addClassName(Padding.LARGE); serviceHealth.setPadding(false); serviceHealth.setSpacing(false); serviceHealth.getElement().getThemeList().add("spacing-l"); return serviceHealth; } private HorizontalLayout createHeader(String title, String subtitle) { H2 h2 = new H2(title); h2.addClassNames(FontSize.XLARGE, Margin.NONE); Span span = new Span(subtitle); span.addClassNames(TextColor.SECONDARY, FontSize.XSMALL); VerticalLayout column = new VerticalLayout(h2, span); column.setPadding(false); column.setSpacing(false); HorizontalLayout header = new HorizontalLayout(column); header.setJustifyContentMode(FlexComponent.JustifyContentMode.BETWEEN); header.setSpacing(false); header.setWidthFull(); return header; } private String getStatusDisplayName(ServiceHealth serviceHealth) { Status status = serviceHealth.getStatus(); if (status == Status.OK) { return "Ok"; } else if (status == Status.FAILING) { return "Failing"; } else if (status == Status.EXCELLENT) { return "Excellent"; } else { return status.toString(); } } private String getStatusTheme(ServiceHealth serviceHealth) { Status status = serviceHealth.getStatus(); String theme = "badge primary small"; if (status == Status.EXCELLENT) { theme += " success"; } else if (status == Status.FAILING) { theme += " error"; } return theme; } } // ServiceHealth.java package com.aksenov.monitor.views.dashboard; /** * Simple DTO class for the inbox list to demonstrate complex object data */ public class ServiceHealth { private Status status; private String city; private int input; private int output; private String theme; enum Status { EXCELLENT, OK, FAILING; } public ServiceHealth() { } public ServiceHealth(Status status, String city, int input, int output) { this.status = status; this.city = city; this.input = input; this.output = output; } public Status getStatus() { return status; } public void setStatus(Status status) { this.status = status; } public String getCity() { return city; } public void setCity(String city) { this.city = city; } public int getInput() { return input; } public void setInput(int input) { this.input = input; } public int getOutput() { return output; } public void setOutput(int output) { this.output = output; } } // MainLayout.java package com.aksenov.monitor.views; import com.vaadin.flow.component.applayout.AppLayout; import com.vaadin.flow.component.applayout.DrawerToggle; import com.vaadin.flow.component.html.Footer; import com.vaadin.flow.component.html.H1; import com.vaadin.flow.component.html.Header; import com.vaadin.flow.component.html.Span; import com.vaadin.flow.component.icon.SvgIcon; import com.vaadin.flow.component.orderedlayout.Scroller; import com.vaadin.flow.component.sidenav.SideNav; import com.vaadin.flow.component.sidenav.SideNavItem; import com.vaadin.flow.router.Layout; import com.vaadin.flow.server.auth.AnonymousAllowed; import com.vaadin.flow.server.menu.MenuConfiguration; import com.vaadin.flow.server.menu.MenuEntry; import com.vaadin.flow.theme.lumo.LumoUtility; import java.util.List; /** * The main view is a top-level placeholder for other views. */ @Layout @AnonymousAllowed public class MainLayout extends AppLayout { private H1 viewTitle; public MainLayout() { setPrimarySection(Section.DRAWER); addDrawerContent(); addHeaderContent(); } private void addHeaderContent() { DrawerToggle toggle = new DrawerToggle(); toggle.setAriaLabel("Menu toggle"); viewTitle = new H1(); viewTitle.addClassNames(LumoUtility.FontSize.LARGE, LumoUtility.Margin.NONE); addToNavbar(true, toggle, viewTitle); } private void addDrawerContent() { Span appName = new Span("Monitor"); appName.addClassNames(LumoUtility.FontWeight.SEMIBOLD, LumoUtility.FontSize.LARGE); Header header = new Header(appName); Scroller scroller = new Scroller(createNavigation()); addToDrawer(header, scroller, createFooter()); } private SideNav createNavigation() { SideNav nav = new SideNav(); List menuEntries = MenuConfiguration.getMenuEntries(); menuEntries.forEach(entry -> { if (entry.icon() != null) { nav.addItem(new SideNavItem(entry.title(), entry.path(), new SvgIcon(entry.icon()))); } else { nav.addItem(new SideNavItem(entry.title(), entry.path())); } }); return nav; } private Footer createFooter() { Footer layout = new Footer(); return layout; } @Override protected void afterNavigation() { super.afterNavigation(); viewTitle.setText(getCurrentPageTitle()); } private String getCurrentPageTitle() { return MenuConfiguration.getPageHeader(getContent()).orElse(""); } } // ManufacturerView.java package com.aksenov.monitor.views.manufacturer; import com.aksenov.monitor.data.Customer; import com.aksenov.monitor.data.Manufacturer; import com.aksenov.monitor.services.ManufacturerService; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.grid.HeaderRow; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification.Position; import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.splitlayout.SplitLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.binder.BeanValidationBinder; import com.vaadin.flow.data.binder.ValidationException; import com.vaadin.flow.data.provider.ListDataProvider; import com.vaadin.flow.function.SerializablePredicate; import com.vaadin.flow.router.BeforeEnterEvent; import com.vaadin.flow.router.BeforeEnterObserver; import com.vaadin.flow.router.Menu; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import java.util.ArrayList; import java.util.List; import java.util.Optional; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.vaadin.lineawesome.LineAwesomeIconUrl; @PageTitle("Manufacturer") @Route("manufacturer/:manufacturerID?/:action?(edit)") @Menu(order = 2, icon = LineAwesomeIconUrl.HAMMER_SOLID) public class ManufacturerView extends Div implements BeforeEnterObserver { private final String MANUFACTURER_ID = "manufacturerID"; private final String MANUFACTURER_EDIT_ROUTE_TEMPLATE = "manufacturer/%s/edit"; private final Grid grid = new Grid<>(Manufacturer.class, false); private TextField name; private TextField country; private TextField contactInfo; private final Button cancel = new Button("Cancel"); private final Button save = new Button("Save"); private final Button delete = new Button("Delete"); private final BeanValidationBinder binder; private Manufacturer manufacturer; private List manufacturers; private final ManufacturerService manufacturerService; private ListDataProvider dataProvider; public ManufacturerView(ManufacturerService manufacturerService) { this.manufacturerService = manufacturerService; addClassNames("manufacturer-view"); // Create UI SplitLayout splitLayout = new SplitLayout(); createGridLayout(splitLayout); createEditorLayout(splitLayout); add(splitLayout); // Configure Grid grid.addColumn("name").setAutoWidth(true); grid.addColumn("country").setAutoWidth(true); grid.addColumn("contactInfo").setAutoWidth(true); //grid.setItems(query -> manufacturerService.list(VaadinSpringDataHelpers.toSpringPageRequest(query)).stream()); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); manufacturers = manufacturerService.list(); dataProvider = new ListDataProvider<>(manufacturers); grid.setDataProvider(dataProvider); // when a row is selected or deselected, populate form grid.asSingleSelect().addValueChangeListener(event -> { if (event.getValue() != null) { UI.getCurrent().navigate(String.format(MANUFACTURER_EDIT_ROUTE_TEMPLATE, event.getValue().getId())); } else { clearForm(); UI.getCurrent().navigate(ManufacturerView.class); } }); // Configure Form binder = new BeanValidationBinder<>(Manufacturer.class); addFilterRow(); binder.bindInstanceFields(this); cancel.addClickListener(e -> { clearForm(); refreshGrid(); }); save.addClickListener(e -> { try { if (this.manufacturer == null) { this.manufacturer = new Manufacturer(); } if (name.isEmpty() || country.isEmpty() || contactInfo.isEmpty()) { Notification.show("Please fill all required fields"); return; } binder.writeBean(this.manufacturer); manufacturerService.save(this.manufacturer); clearForm(); refreshGrid(); Notification.show("Data updated"); UI.getCurrent().navigate(ManufacturerView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); delete.addClickListener(e -> { try { if (this.manufacturer == null) { return; } binder.writeBean(this.manufacturer); manufacturerService.delete(this.manufacturer.getId()); clearForm(); refreshGrid(); Notification.show("Data deleted"); UI.getCurrent().navigate(ManufacturerView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); } @Override public void beforeEnter(BeforeEnterEvent event) { Optional manufacturerId = event.getRouteParameters().get(MANUFACTURER_ID).map(Long::parseLong); if (manufacturerId.isPresent()) { Optional manufacturerFromBackend = manufacturerService.get(manufacturerId.get()); if (manufacturerFromBackend.isPresent()) { populateForm(manufacturerFromBackend.get()); } else { Notification.show( String.format("The requested manufacturer was not found, ID = %s", manufacturerId.get()), 3000, Notification.Position.BOTTOM_START); // when a row is selected but the data is no longer available, // refresh grid refreshGrid(); event.forwardTo(ManufacturerView.class); } } } private void addFilterRow() { HeaderRow headerRow = grid.appendHeaderRow(); // Filter for name column nameFilter = new TextField(); nameFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if (filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(manufacturer -> manufacturer.getName() != null && manufacturer.getName().toLowerCase().contains(filterValue.toLowerCase())); } clearOtherFilters(nameFilter); clearForm(); refreshGrid(); }); nameFilter.setPlaceholder("Filter"); nameFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("name")).setComponent(nameFilter); // Filter for country column countryFilter = new TextField(); countryFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(manufacturer -> manufacturer.getCountry() != null && manufacturer.getCountry().toLowerCase().contains(filterValue.toLowerCase())); } clearOtherFilters(countryFilter); clearForm(); refreshGrid(); }); countryFilter.setPlaceholder("Filter"); countryFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("country")).setComponent(countryFilter); // Filter for contactInfo column contactInfoFilter = new TextField(); contactInfoFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); dataProvider.setFilter(manufacturer -> manufacturer.getContactInfo() != null && manufacturer.getContactInfo().toLowerCase().contains(filterValue.toLowerCase())); clearOtherFilters(contactInfoFilter); clearForm(); refreshGrid(); }); contactInfoFilter.setPlaceholder("Filter"); contactInfoFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("contactInfo")).setComponent(contactInfoFilter); } private void clearOtherFilters(TextField currentFilter) { if (currentFilter != nameFilter) { nameFilter.clear(); } if (currentFilter != countryFilter) { countryFilter.clear(); } if (currentFilter != contactInfoFilter) { contactInfoFilter.clear(); } } private void createEditorLayout(SplitLayout splitLayout) { Div editorLayoutDiv = new Div(); editorLayoutDiv.setClassName("editor-layout"); Div editorDiv = new Div(); editorDiv.setClassName("editor"); editorLayoutDiv.add(editorDiv); FormLayout formLayout = new FormLayout(); name = new TextField("Name"); country = new TextField("Country"); contactInfo = new TextField("Contact Info"); formLayout.add(name, country, contactInfo); editorDiv.add(formLayout); createButtonLayout(editorLayoutDiv); splitLayout.addToSecondary(editorLayoutDiv); } private void createButtonLayout(Div editorLayoutDiv) { HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.setClassName("button-layout"); cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY); save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); delete.addThemeVariants(ButtonVariant.LUMO_PRIMARY); buttonLayout.add(save, cancel, delete); editorLayoutDiv.add(buttonLayout); } private void createGridLayout(SplitLayout splitLayout) { Div wrapper = new Div(); wrapper.setClassName("grid-wrapper"); splitLayout.addToPrimary(wrapper); wrapper.add(grid); } private void refreshGrid() { SerializablePredicate currentFilter = dataProvider.getFilter(); // 2. Обновить список manufacturers = manufacturerService.list(); // 3. Создать новый dataProvider и применить фильтр dataProvider = new ListDataProvider<>(manufacturers); grid.setDataProvider(dataProvider); dataProvider.setFilter(currentFilter); // grid.select(null); dataProvider.refreshAll(); } private void clearForm() { populateForm(null); } private void populateForm(Manufacturer value) { this.manufacturer = value; binder.readBean(this.manufacturer); } private TextField nameFilter; private TextField countryFilter; private TextField contactInfoFilter; } // MonitorView.java package com.aksenov.monitor.views.monitor; import com.aksenov.monitor.data.Manufacturer; import com.aksenov.monitor.data.Monitor; import com.aksenov.monitor.services.ManufacturerService; import com.aksenov.monitor.services.MonitorService; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.grid.HeaderRow; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification.Position; import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.splitlayout.SplitLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.binder.BeanValidationBinder; import com.vaadin.flow.data.binder.ValidationException; import com.vaadin.flow.data.converter.StringToIntegerConverter; import com.vaadin.flow.data.provider.ListDataProvider; import com.vaadin.flow.function.SerializablePredicate; import com.vaadin.flow.router.BeforeEnterEvent; import com.vaadin.flow.router.BeforeEnterObserver; import com.vaadin.flow.router.Menu; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import java.util.List; import java.util.Optional; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.vaadin.lineawesome.LineAwesomeIconUrl; @PageTitle("Monitor") @Route("monitor/:monitorID?/:action?(edit)") @Menu(order = 3, icon = LineAwesomeIconUrl.TV_SOLID) public class MonitorView extends Div implements BeforeEnterObserver { private final String MONITOR_ID = "monitorID"; private final String MONITOR_EDIT_ROUTE_TEMPLATE = "monitor/%s/edit"; private final Grid grid = new Grid<>(Monitor.class, false); private TextField model; private ComboBox manufacturer; private TextField screenSize; private TextField resolution; private TextField refreshrRate; private TextField price; private final Button cancel = new Button("Cancel"); private final Button save = new Button("Save"); private final Button delete = new Button("Delete"); private final BeanValidationBinder binder; private Monitor monitor; private List monitors; private final MonitorService monitorService; private final ManufacturerService manufacturerService; private ListDataProvider dataProvider; public MonitorView(MonitorService monitorService, ManufacturerService manufacturerService) { this.monitorService = monitorService; this.manufacturerService = manufacturerService; addClassNames("monitor-view"); // Create UI SplitLayout splitLayout = new SplitLayout(); createGridLayout(splitLayout); createEditorLayout(splitLayout); add(splitLayout); // Configure Grid grid.addColumn("model").setAutoWidth(true); grid.addColumn("manufacturer.name").setAutoWidth(true).setHeader("Manufacturer"); grid.addColumn("screenSize").setAutoWidth(true); grid.addColumn("resolution").setAutoWidth(true); grid.addColumn("refreshrRate").setAutoWidth(true); grid.addColumn("price").setAutoWidth(true); //grid.setItems(query -> monitorService.list(VaadinSpringDataHelpers.toSpringPageRequest(query)).stream()); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); monitors = monitorService.list(); dataProvider = new ListDataProvider<>(monitors); grid.setDataProvider(dataProvider); // when a row is selected or deselected, populate form grid.asSingleSelect().addValueChangeListener(event -> { if (event.getValue() != null) { UI.getCurrent().navigate(String.format(MONITOR_EDIT_ROUTE_TEMPLATE, event.getValue().getId())); } else { clearForm(); UI.getCurrent().navigate(MonitorView.class); } }); // Configure Form binder = new BeanValidationBinder<>(Monitor.class); // Bind fields. This is where you'd define e.g. validation rules binder.forField(model).bind("model"); binder.forField(manufacturer).bind("manufacturer"); binder.forField(resolution).bind("resolution"); binder.forField(screenSize).withConverter(new StringToIntegerConverter("Only numbers are allowed")) .bind("screenSize"); binder.forField(refreshrRate).withConverter(new StringToIntegerConverter("Only numbers are allowed")) .bind("refreshrRate"); binder.forField(price).withConverter(new StringToIntegerConverter("Only numbers are allowed")).bind("price"); addFilterRow(); cancel.addClickListener(e -> { clearForm(); refreshGrid(); }); save.addClickListener(e -> { try { if (this.monitor == null) { this.monitor = new Monitor(); } if (model.isEmpty() || manufacturer.isEmpty() || screenSize.isEmpty() || resolution.isEmpty() || refreshrRate.isEmpty() || price.isEmpty()) { Notification.show("Please fill all required fields"); return; } if (binder.validate().isOk()) { binder.writeBean(this.monitor); monitorService.save(this.monitor); clearForm(); refreshGrid(); Notification.show("Data updated"); UI.getCurrent().navigate(MonitorView.class); }else{ Notification.show("Please fill all required fields"); } } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); delete.addClickListener(e -> { try { if (this.monitor == null) { return; } binder.writeBean(this.monitor); monitorService.delete(this.monitor.getId()); clearForm(); refreshGrid(); Notification.show("Data deleted"); UI.getCurrent().navigate(MonitorView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); } @Override public void beforeEnter(BeforeEnterEvent event) { Optional monitorId = event.getRouteParameters().get(MONITOR_ID).map(Long::parseLong); if (monitorId.isPresent()) { Optional monitorFromBackend = monitorService.get(monitorId.get()); if (monitorFromBackend.isPresent()) { populateForm(monitorFromBackend.get()); } else { Notification.show(String.format("The requested monitor was not found, ID = %s", monitorId.get()), 3000, Notification.Position.BOTTOM_START); // when a row is selected but the data is no longer available, // refresh grid refreshGrid(); event.forwardTo(MonitorView.class); } } } private void addFilterRow() { HeaderRow headerRow = grid.appendHeaderRow(); // Filter for model column modelFilter = new TextField(); modelFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if (filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(monitor -> monitor.getModel().toLowerCase().contains(filterValue.toLowerCase())); } clearOtherFilters(modelFilter); clearForm(); refreshGrid(); }); modelFilter.setPlaceholder("Filter"); modelFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("model")).setComponent(modelFilter); // Filter for manufacturerId column manufacturerIdFilter = new TextField(); manufacturerIdFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(monitor -> String.valueOf(monitor.getManufacturer()).contains(filterValue)); } clearOtherFilters(manufacturerIdFilter); clearForm(); refreshGrid(); }); manufacturerIdFilter.setPlaceholder("Filter"); manufacturerIdFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("manufacturer.name")).setComponent(manufacturerIdFilter); // Filter for screenSize column screenSizeFilter = new TextField(); screenSizeFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(monitor -> String.valueOf(monitor.getScreenSize()).contains(filterValue)); } clearOtherFilters(screenSizeFilter); clearForm(); refreshGrid(); }); screenSizeFilter.setPlaceholder("Filter"); screenSizeFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("screenSize")).setComponent(screenSizeFilter); // Filter for resolution column resolutionFilter = new TextField(); resolutionFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(monitor -> monitor.getResolution().toLowerCase().contains(filterValue.toLowerCase())); } clearOtherFilters(resolutionFilter); clearForm(); refreshGrid(); }); resolutionFilter.setPlaceholder("Filter"); resolutionFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("resolution")).setComponent(resolutionFilter); // Filter for refreshrRate column refreshrRateFilter = new TextField(); refreshrRateFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(monitor -> String.valueOf(monitor.getRefreshrRate()).contains(filterValue)); } clearOtherFilters(refreshrRateFilter); clearForm(); refreshGrid(); }); refreshrRateFilter.setPlaceholder("Filter"); refreshrRateFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("refreshrRate")).setComponent(refreshrRateFilter); // Filter for price column priceFilter = new TextField(); priceFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(monitor -> String.valueOf(monitor.getPrice()).contains(filterValue)); } clearOtherFilters(priceFilter); clearForm(); refreshGrid(); }); priceFilter.setPlaceholder("Filter"); priceFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("price")).setComponent(priceFilter); } private void clearOtherFilters(TextField currentFilter) { if (currentFilter != modelFilter) { modelFilter.clear(); } if (currentFilter != manufacturerIdFilter) { manufacturerIdFilter.clear(); } if (currentFilter != screenSizeFilter) { screenSizeFilter.clear(); } if (currentFilter != resolutionFilter) { resolutionFilter.clear(); } if (currentFilter != refreshrRateFilter) { refreshrRateFilter.clear(); } if (currentFilter != priceFilter) { priceFilter.clear(); } } private void createEditorLayout(SplitLayout splitLayout) { Div editorLayoutDiv = new Div(); editorLayoutDiv.setClassName("editor-layout"); Div editorDiv = new Div(); editorDiv.setClassName("editor"); editorLayoutDiv.add(editorDiv); FormLayout formLayout = new FormLayout(); model = new TextField("Model"); model.setRequired(true); manufacturer = new ComboBox("Manufacturer"); manufacturer.setItemLabelGenerator(currManufacturer -> String.valueOf(currManufacturer.getId())); manufacturer.setAllowCustomValue(false); manufacturer.setRequired(true); manufacturer.addFocusListener(event -> { List manufacturerList = manufacturerService.list(); manufacturer.setItems(manufacturerList); }); manufacturer.setItemLabelGenerator(manufacturer -> manufacturer != null ? manufacturer.getName() : ""); screenSize = new TextField("Screen Size"); screenSize.setRequired(true); resolution = new TextField("Resolution"); resolution.setRequired(true); refreshrRate = new TextField("Refreshr Rate"); refreshrRate.setRequired(true); price = new TextField("Price"); price.setRequired(true); formLayout.add(model, manufacturer, screenSize, resolution, refreshrRate, price); editorDiv.add(formLayout); createButtonLayout(editorLayoutDiv); splitLayout.addToSecondary(editorLayoutDiv); } private void createButtonLayout(Div editorLayoutDiv) { HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.setClassName("button-layout"); cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY); save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); delete.addThemeVariants(ButtonVariant.LUMO_PRIMARY); buttonLayout.add(save, cancel, delete); editorLayoutDiv.add(buttonLayout); } private void createGridLayout(SplitLayout splitLayout) { Div wrapper = new Div(); wrapper.setClassName("grid-wrapper"); splitLayout.addToPrimary(wrapper); wrapper.add(grid); } private void refreshGrid() { SerializablePredicate currentFilter = dataProvider.getFilter(); // 2. Обновить список monitors = monitorService.list(); // 3. Создать новый dataProvider и применить фильтр dataProvider = new ListDataProvider<>(monitors); grid.setDataProvider(dataProvider); dataProvider.setFilter(currentFilter); // grid.select(null); dataProvider.refreshAll(); } private void clearForm() { populateForm(null); } private void populateForm(Monitor value) { this.monitor = value; // Загрузить список городов List manufacturerList = manufacturerService.list(); // Установить список городов в ComboBox manufacturer.setItems(manufacturerList); binder.readBean(this.monitor); } private TextField modelFilter; private TextField manufacturerIdFilter; private TextField screenSizeFilter; private TextField resolutionFilter; private TextField refreshrRateFilter; private TextField priceFilter; } // SalesView.java package com.aksenov.monitor.views.sales; import com.aksenov.monitor.data.Customer; import com.aksenov.monitor.data.Monitor; import com.aksenov.monitor.data.Sales; import com.aksenov.monitor.services.CustomerService; import com.aksenov.monitor.services.MonitorService; import com.aksenov.monitor.services.SalesService; import com.vaadin.flow.component.UI; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.combobox.ComboBox; import com.vaadin.flow.component.datetimepicker.DateTimePicker; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.grid.GridVariant; import com.vaadin.flow.component.grid.HeaderRow; import com.vaadin.flow.component.html.Div; import com.vaadin.flow.component.notification.Notification; import com.vaadin.flow.component.notification.Notification.Position; import com.vaadin.flow.component.notification.NotificationVariant; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.splitlayout.SplitLayout; import com.vaadin.flow.component.textfield.TextField; import com.vaadin.flow.data.binder.BeanValidationBinder; import com.vaadin.flow.data.binder.ValidationException; import com.vaadin.flow.data.converter.StringToIntegerConverter; import com.vaadin.flow.data.provider.ListDataProvider; import com.vaadin.flow.function.SerializablePredicate; import com.vaadin.flow.router.BeforeEnterEvent; import com.vaadin.flow.router.BeforeEnterObserver; import com.vaadin.flow.router.Menu; import com.vaadin.flow.router.PageTitle; import com.vaadin.flow.router.Route; import java.time.Duration; import java.util.List; import java.util.Optional; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.vaadin.lineawesome.LineAwesomeIconUrl; @PageTitle("Sales") @Route("sales/:salesID?/:action?(edit)") @Menu(order = 4, icon = LineAwesomeIconUrl.DOLLAR_SIGN_SOLID) public class SalesView extends Div implements BeforeEnterObserver { private final String SALES_ID = "salesID"; private final String SALES_EDIT_ROUTE_TEMPLATE = "sales/%s/edit"; private final Grid grid = new Grid<>(Sales.class, false); private ComboBox monitor; private ComboBox customer; private DateTimePicker saleDate; private TextField quantity; private TextField totalPrice; private final Button cancel = new Button("Cancel"); private final Button save = new Button("Save"); private final Button delete = new Button("Delete"); private final BeanValidationBinder binder; private Sales sales; private List salesList; private ListDataProvider dataProvider; private final SalesService salesService; private final MonitorService monitorService; private final CustomerService customerService; public SalesView(SalesService salesService, MonitorService monitorService, CustomerService customerService) { this.salesService = salesService; this.monitorService = monitorService; this.customerService = customerService; addClassNames("sales-view"); // Create UI SplitLayout splitLayout = new SplitLayout(); createGridLayout(splitLayout); createEditorLayout(splitLayout); add(splitLayout); // Configure Grid grid.addColumn("monitor.model").setAutoWidth(true).setHeader("Monitor"); grid.addColumn("customer.name").setAutoWidth(true).setHeader("Customer"); grid.addColumn("saleDate").setAutoWidth(true); grid.addColumn("quantity").setAutoWidth(true); grid.addColumn("totalPrice").setAutoWidth(true); //grid.setItems(query -> salesService.list(VaadinSpringDataHelpers.toSpringPageRequest(query)).stream()); grid.addThemeVariants(GridVariant.LUMO_NO_BORDER); salesList = salesService.list(); dataProvider = new ListDataProvider<>(salesList); grid.setDataProvider(dataProvider); // when a row is selected or deselected, populate form grid.asSingleSelect().addValueChangeListener(event -> { if (event.getValue() != null) { UI.getCurrent().navigate(String.format(SALES_EDIT_ROUTE_TEMPLATE, event.getValue().getId())); } else { clearForm(); UI.getCurrent().navigate(SalesView.class); } }); // Configure Form binder = new BeanValidationBinder<>(Sales.class); // Bind fields. This is where you'd define e.g. validation rules binder.forField(monitor).bind("monitor"); binder.forField(customer).bind("customer"); binder.forField(saleDate).bind("saleDate"); binder.forField(quantity).withConverter(new StringToIntegerConverter("Only numbers are allowed")) .bind("quantity"); binder.forField(totalPrice).withConverter(new StringToIntegerConverter("Only numbers are allowed")) .bind("totalPrice"); addFilterRow(); cancel.addClickListener(e -> { clearForm(); refreshGrid(); }); save.addClickListener(e -> { try { if (this.sales == null) { this.sales = new Sales(); } if (monitor.isEmpty() || customer.isEmpty() || saleDate.isEmpty() || quantity.isEmpty() ){ Notification.show("Please fill all required fields"); return; } // Check if the binder has errors before writing if (binder.validate().isOk()) { binder.writeBean(this.sales); salesService.save(this.sales); clearForm(); refreshGrid(); Notification.show("Data updated"); UI.getCurrent().navigate(SalesView.class); }else{ Notification.show("Please fill all required fields"); } } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); delete.addClickListener(e -> { try { if (this.sales == null) { return; } binder.writeBean(this.sales); salesService.delete(this.sales.getId()); clearForm(); refreshGrid(); Notification.show("Data deleted"); UI.getCurrent().navigate(SalesView.class); } catch (ObjectOptimisticLockingFailureException exception) { Notification n = Notification.show( "Error updating the data. Somebody else has updated the record while you were making changes."); n.setPosition(Position.MIDDLE); n.addThemeVariants(NotificationVariant.LUMO_ERROR); } catch (ValidationException validationException) { Notification.show("Failed to update the data. Check again that all values are valid"); } }); } @Override public void beforeEnter(BeforeEnterEvent event) { Optional salesId = event.getRouteParameters().get(SALES_ID).map(Long::parseLong); if (salesId.isPresent()) { Optional salesFromBackend = salesService.get(salesId.get()); if (salesFromBackend.isPresent()) { populateForm(salesFromBackend.get()); } else { Notification.show(String.format("The requested sales was not found, ID = %s", salesId.get()), 3000, Notification.Position.BOTTOM_START); // when a row is selected but the data is no longer available, // refresh grid refreshGrid(); event.forwardTo(SalesView.class); } } } private void addFilterRow() { HeaderRow headerRow = grid.appendHeaderRow(); // Filter for monitor column monitorIdFilter = new TextField(); monitorIdFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(sales -> String.valueOf(sales.getMonitor()).contains(filterValue)); } clearOtherFilters(monitorIdFilter); clearForm(); refreshGrid(); }); monitorIdFilter.setPlaceholder("Filter"); monitorIdFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("monitor.model")).setComponent(monitorIdFilter); // Filter for customerId column customerIdFilter = new TextField(); customerIdFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(sales -> String.valueOf(sales.getCustomer()).contains(filterValue)); } clearOtherFilters(customerIdFilter); clearForm(); refreshGrid(); }); customerIdFilter.setPlaceholder("Filter"); customerIdFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("customer.name")).setComponent(customerIdFilter); // Filter for saleDate column saleDateFilter = new TextField(); saleDateFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(sales -> String.valueOf(sales.getSaleDate()).contains(filterValue)); } clearOtherFilters(saleDateFilter); clearForm(); refreshGrid(); }); saleDateFilter.setPlaceholder("Filter"); saleDateFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("saleDate")).setComponent(saleDateFilter); // Filter for quantity column quantityFilter = new TextField(); quantityFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(sales -> String.valueOf(sales.getQuantity()).contains(filterValue)); } clearOtherFilters(quantityFilter); clearForm(); refreshGrid(); }); quantityFilter.setPlaceholder("Filter"); quantityFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("quantity")).setComponent(quantityFilter); // Filter for totalPrice column totalPriceFilter = new TextField(); totalPriceFilter.addValueChangeListener(event -> { String filterValue = ((TextField) event.getSource()).getValue(); if(filterValue.isEmpty()) { dataProvider.clearFilters(); } else { dataProvider.setFilter(sales -> String.valueOf(sales.getTotalPrice()).contains(filterValue)); } clearOtherFilters(totalPriceFilter); clearForm(); refreshGrid(); }); totalPriceFilter.setPlaceholder("Filter"); totalPriceFilter.setWidthFull(); headerRow.getCell(grid.getColumnByKey("totalPrice")).setComponent(totalPriceFilter); } private void clearOtherFilters(TextField currentFilter) { if (currentFilter != monitorIdFilter) { monitorIdFilter.clear(); } if (currentFilter != customerIdFilter) { customerIdFilter.clear(); } if (currentFilter != saleDateFilter) { saleDateFilter.clear(); } if (currentFilter != quantityFilter) { quantityFilter.clear(); } if (currentFilter != totalPriceFilter) { totalPriceFilter.clear(); } } private void createEditorLayout(SplitLayout splitLayout) { Div editorLayoutDiv = new Div(); editorLayoutDiv.setClassName("editor-layout"); Div editorDiv = new Div(); editorDiv.setClassName("editor"); editorLayoutDiv.add(editorDiv); FormLayout formLayout = new FormLayout(); monitor = new ComboBox("Monitor"); monitor.setItemLabelGenerator(currMonitor -> String.valueOf(currMonitor.getId())); monitor.setAllowCustomValue(false); monitor.setRequired(true); // Make it required monitor.addValueChangeListener(event -> { if(event.getValue() != null) { calculateTotalPrice(); } else { totalPrice.clear(); } }); monitor.addFocusListener(event -> { List manufacturerList = monitorService.list(); monitor.setItems(manufacturerList); }); monitor.setItemLabelGenerator(monitor -> monitor != null ? monitor.getModel() : ""); customer = new ComboBox("Customer"); customer.setItemLabelGenerator(currMonitor -> String.valueOf(currMonitor.getId())); customer.setAllowCustomValue(false); customer.setRequired(true); // Make it required customer.addFocusListener(event -> { List customerList = customerService.list(); customer.setItems(customerList); }); customer.setItemLabelGenerator(customer -> customer != null ? customer.getName() : ""); saleDate = new DateTimePicker("Sale Date"); saleDate.setStep(Duration.ofSeconds(1)); saleDate.setRequiredIndicatorVisible(true); // Make it required quantity = new TextField("Quantity"); quantity.setRequired(true); // Make it required quantity.addValueChangeListener(event -> calculateTotalPrice()); totalPrice = new TextField("Total Price"); totalPrice.setReadOnly(true); formLayout.add(monitor, customer, saleDate, quantity, totalPrice); editorDiv.add(formLayout); createButtonLayout(editorLayoutDiv); splitLayout.addToSecondary(editorLayoutDiv); } private void calculateTotalPrice() { if(monitor.getValue() != null && !quantity.isEmpty()) { Integer quantityValue = Integer.parseInt(quantity.getValue()); Integer monitorPrice = monitor.getValue().getPrice(); Integer totalPriceValue = quantityValue * monitorPrice; totalPrice.setValue(String.valueOf(totalPriceValue)); } else { totalPrice.clear(); } } private void createButtonLayout(Div editorLayoutDiv) { HorizontalLayout buttonLayout = new HorizontalLayout(); buttonLayout.setClassName("button-layout"); cancel.addThemeVariants(ButtonVariant.LUMO_TERTIARY); save.addThemeVariants(ButtonVariant.LUMO_PRIMARY); delete.addThemeVariants(ButtonVariant.LUMO_PRIMARY); buttonLayout.add(save, cancel, delete); editorLayoutDiv.add(buttonLayout); } private void createGridLayout(SplitLayout splitLayout) { Div wrapper = new Div(); wrapper.setClassName("grid-wrapper"); splitLayout.addToPrimary(wrapper); wrapper.add(grid); } private void refreshGrid() { SerializablePredicate currentFilter = dataProvider.getFilter(); salesList = salesService.list(); dataProvider = new ListDataProvider<>(salesList); grid.setDataProvider(dataProvider); dataProvider.setFilter(currentFilter); dataProvider.refreshAll(); } private void clearForm() { populateForm(null); } private void populateForm(Sales value) { this.sales = value; // Загрузить список городов List monitorList = monitorService.list(); // Установить список городов в ComboBox monitor.setItems(monitorList); List customerList = customerService.list(); // Установить список городов в ComboBox customer.setItems(customerList); // Установить значения клиента binder.readBean(this.sales); if (value != null && value.getMonitor() != null) { monitor.setValue(value.getMonitor()); } else { monitor.clear(); } if (value != null && value.getCustomer() != null) { customer.setValue(value.getCustomer()); } else { customer.clear(); } if(value != null && value.getSaleDate() != null) { saleDate.setValue(value.getSaleDate()); // Set the date from the Sales entity } else { saleDate.clear(); } calculateTotalPrice(); } private TextField monitorIdFilter; private TextField customerIdFilter; private TextField saleDateFilter; private TextField quantityFilter; private TextField totalPriceFilter; }