Rama-Ricardo #14

Merged
alex merged 8 commits from Rama-Ricardo into main 2024-09-04 17:53:01 +00:00
6 changed files with 336 additions and 146 deletions

View File

@ -110,6 +110,11 @@
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.9.4</version>
</dependency>
<dependency>
<groupId>org.mockito</groupId>
<artifactId>mockito-core</artifactId>

View File

@ -1,29 +1,47 @@
package com.primefactorsolutions.model;
package com.primefactorsolutions.model;
Review

quitar espacios blancos al inicio de cada linea

quitar espacios blancos al inicio de cada linea
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import io.hypersistence.utils.hibernate.type.json.JsonType;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import org.hibernate.annotations.Type;
import java.time.LocalDate;
import java.time.LocalDate;
import java.util.List;
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity {
private String firstName;
private String lastName;
@Enumerated(EnumType.STRING)
private Status status;
public enum Status {
ACTIVE,
INACTIVE
@Data
@Entity
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public class Employee extends BaseEntity {
private String firstName;
private String lastName;
private LocalDate birthday;
private String birthCity;
@Enumerated(EnumType.STRING)
private MaritalStatus maritalStatus;
private String residenceAddress;
private String phoneNumber;
private String personalEmail;
private String emergencyCName;
private String emergencyCAddress;
private String emergencyCPhone;
private String emergencyCEmail;
@Enumerated(EnumType.STRING)
private Status status;
public enum Status {
ACTIVE,
INACTIVE
}
public enum MaritalStatus {
SINGLE,
MARRIED,
WIDOWED,
DIVORCED
}
public Status getStatus() {
return status;
}
public void setStatus(final Status status) {
this.status = status;
}
}
}

View File

@ -1,34 +1,52 @@
package com.primefactorsolutions.service;
import com.primefactorsolutions.model.Candidate;
import com.primefactorsolutions.model.Employee;
import org.apache.commons.beanutils.BeanComparator;
import com.primefactorsolutions.repositories.EmployeeRepository;
import lombok.Data;
import org.apache.commons.lang3.NotImplementedException;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.UUID;
import java.util.Optional;
import java.util.Collections;
@Service
@Data
public class EmployeeService {
private final EmployeeRepository employeeRepository;
public EmployeeService(EmployeeRepository employeeRepository) {
public EmployeeService(final EmployeeRepository employeeRepository) {
this.employeeRepository = employeeRepository;
}
public Employee createOrUpdate(final Employee employee) {
final Employee saved = employeeRepository.save(employee);
return saved;
public List<Employee> findEmployees(
final int start, final int pageSize, final String sortProperty, final boolean asc) {
List<Employee> employees = employeeRepository.findAll();
Outdated
Review

no se puede guardar en un campo de la clase porque la clase es un singleton (si hay varios llamados van a haber problemas de concurrencia). solucion: no usar un campo en la clase y simplemente retornar toda la lista cada vez sin guardar en un campo.

no se puede guardar en un campo de la clase porque la clase es un singleton (si hay varios llamados van a haber problemas de concurrencia). solucion: no usar un campo en la clase y simplemente retornar toda la lista cada vez sin guardar en un campo.
int end = Math.min(start + pageSize, employees.size());
employees.sort(new BeanComparator<>(sortProperty));
if (!asc) {
Collections.reverse(employees);
}
Outdated
Review

usar logger en vez de system println. ver lombok @Slf4j https://projectlombok.org/features/log

usar logger en vez de system println. ver lombok @Slf4j https://projectlombok.org/features/log
return employees.subList(start, end);
}
public List<Employee> getEmployees() {
return employeeRepository.findAll();
public List<Employee> findEmployees(final int start, final int pageSize) {
List<Employee> employees = employeeRepository.findAll();
int end = Math.min(start + pageSize, employees.size());
return employees.subList(start, end);
}
public Employee createOrUpdate(final Employee employee) {
return employeeRepository.save(employee);
}
public Employee getEmployee(final UUID id) {
return null;
Optional<Employee> employee = employeeRepository.findById(id);
return employee.orElse(null);
}
}

View File

@ -3,8 +3,16 @@ package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.H3;
import com.vaadin.flow.component.notification.Notification;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.component.orderedlayout.VerticalLayout;
import com.vaadin.flow.component.textfield.EmailField;
import com.vaadin.flow.component.textfield.TextField;
import com.vaadin.flow.data.value.ValueChangeMode;
import com.vaadin.flow.router.BeforeEvent;
import com.vaadin.flow.router.HasUrlParameter;
import com.vaadin.flow.router.PageTitle;
@ -13,6 +21,7 @@ import com.vaadin.flow.spring.annotation.SpringComponent;
import jakarta.annotation.security.PermitAll;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.datepicker.VDatePicker;
import org.vaadin.firitin.form.BeanValidationForm;
import java.util.List;
@ -24,21 +33,150 @@ import java.util.UUID;
@PageTitle("Employee")
@Route(value = "/employees", layout = MainLayout.class)
public class EmployeeView extends BeanValidationForm<Employee> implements HasUrlParameter<String> {
private final EmployeeService employeeService;
private TextField name = null;
private final TextField firstName;
private final TextField lastName;
private final ComboBox<String> status;
private final VDatePicker birthday;
private final TextField birthCity;
private final ComboBox<String> maritalStatus;
private final TextField residenceAddress;
private final TextField phoneNumber;
private final EmailField personalEmail;
private final TextField emergencyCName;
private final TextField emergencyCAddress;
private final TextField emergencyCPhone;
private final EmailField emergencyCEmail;
private final H2 mt;
private final H3 fs;
private final H3 ss;
public EmployeeView(final EmployeeService employeeService) {
super(Employee.class);
this.employeeService = employeeService;
name = new TextField();
name.setWidthFull();
name.setLabel("Name");
mt = new H2("Información General del Empleado");
fs = new H3("Información Personal");
ss = new H3("Datos de Contacto de Emergencia");
final HorizontalLayout mainLayout = new HorizontalLayout();
final VerticalLayout sidebar = createSidebar();
final VerticalLayout contentLayout = createContentLayout();
final VerticalLayout contentLayout2 = createContentLayout();
firstName = new TextField("Nombres: ");
firstName.setWidthFull();
firstName.setMaxLength(30);
firstName.setRequired(true);
lastName = new TextField("Apellidos");
lastName.setWidthFull();
lastName.setMaxLength(30);
lastName.setRequired(true);
status = new ComboBox<>("Estado");
status.setItems(List.of("ACTIVE", "INACTIVE"));
lastName.setWidthFull();
lastName.setMaxLength(30);
status.setRequired(true);
birthday = new VDatePicker("Fecha de Nacimiento");
birthday.setWidthFull();
birthCity = new TextField("Ciudad y País de Nacimiento");
birthCity.setWidthFull();
birthCity.setMaxLength(20);
maritalStatus = new ComboBox<>("Estado Civil");
maritalStatus.setItems(List.of("Soltero", "Casado", "Viudo", "Divorciado"));
maritalStatus.setWidthFull();
residenceAddress = new TextField("Dirección de Residencia");
residenceAddress.setWidthFull();
residenceAddress.setMaxLength(50);
phoneNumber = new TextField("Teléfono");
phoneNumber.setWidthFull();
phoneNumber.setMaxLength(8);
phoneNumber.setValueChangeMode(ValueChangeMode.EAGER);
phoneNumber.addValueChangeListener(e -> {
if (!e.getValue().matches("\\d*")) {
phoneNumber.setErrorMessage("El teléfono debe contener solo números.");
}
});
personalEmail = new EmailField("E-mail");
personalEmail.setWidthFull();
personalEmail.setMaxLength(30);
emergencyCName = new TextField("Nombres y Apellidos de Contacto");
emergencyCName.setWidthFull();
emergencyCName.setMaxLength(50);
emergencyCAddress = new TextField("Dirección de Contacto");
emergencyCAddress.setWidthFull();
emergencyCAddress.setMaxLength(50);
emergencyCPhone = new TextField("Teléfono de Contacto");
emergencyCPhone.setWidthFull();
emergencyCPhone.setMaxLength(8);
emergencyCPhone.setValueChangeMode(ValueChangeMode.EAGER);
emergencyCPhone.addValueChangeListener(e -> {
if (!e.getValue().matches("\\d*")) {
emergencyCPhone.setErrorMessage("El teléfono debe contener solo números.");
}
});
emergencyCEmail = new EmailField("Email de Contacto");
emergencyCEmail.setWidthFull();
emergencyCEmail.setMaxLength(30);
contentLayout.add(
mt, fs, firstName, lastName, status, birthday, birthCity, maritalStatus,
residenceAddress, phoneNumber, personalEmail);
contentLayout2.add(
ss, emergencyCName, emergencyCAddress, emergencyCPhone, emergencyCEmail);
setSavedHandler((SavedHandler<Employee>) employee -> {
final Employee saved = employeeService.createOrUpdate(employee);
setEntityWithEnabledSave(saved);
if (validateForm()) {
final Employee saved = employeeService.createOrUpdate(employee);
Notification.show("Employee saved successfully.");
getUI().ifPresent(ui -> ui.navigate(EmployeesListView.class));
setEntityWithEnabledSave(saved);
} else {
Notification.show("Please complete the required fields correctly.", 3000, Notification.Position.MIDDLE);
}
});
mainLayout.add(sidebar, contentLayout, contentLayout2);
addClassName("main-layout");
}
private boolean validateForm() {
return !firstName.isEmpty() && !lastName.isEmpty() && status.getValue() != null;
}
private VerticalLayout createSidebar() {
VerticalLayout sidebar = new VerticalLayout();
sidebar.setWidth("250px");
sidebar.add(new Button("Información General", e -> navigateToSection("Información General")));
sidebar.add(new Button("Detalles Profesionales", e -> navigateToSection("Detalles Profesionales")));
return sidebar;
}
private void navigateToSection(final String section) {
Notification.show("Navigating to " + section);
}
private VerticalLayout createContentLayout() {
VerticalLayout contentLayout = new VerticalLayout();
contentLayout.setWidth("100%");
return contentLayout;
}
@Override
@ -53,7 +191,24 @@ public class EmployeeView extends BeanValidationForm<Employee> implements HasUrl
@Override
protected List<Component> getFormComponents() {
return List.of(name);
return List.of(
mt,
fs,
firstName,
lastName,
status,
birthday,
birthCity,
maritalStatus,
residenceAddress,
phoneNumber,
personalEmail,
ss,
emergencyCName,
emergencyCAddress,
emergencyCPhone,
emergencyCEmail
);
}
}

View File

@ -2,27 +2,20 @@ package com.primefactorsolutions.views;
import com.primefactorsolutions.model.Employee;
import com.primefactorsolutions.service.EmployeeService;
import com.vaadin.flow.component.ClickEvent;
import com.vaadin.flow.component.Component;
import com.vaadin.flow.component.ComponentEventListener;
import com.vaadin.flow.component.button.Button;
import com.vaadin.flow.component.combobox.ComboBox;
import com.vaadin.flow.component.html.H2;
import com.vaadin.flow.component.html.Main;
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
import com.vaadin.flow.data.provider.DataProvider;
import com.vaadin.flow.data.provider.DataProviderListener;
import com.vaadin.flow.data.provider.Query;
import com.vaadin.flow.function.ValueProvider;
import com.vaadin.flow.router.PageTitle;
import com.vaadin.flow.router.Route;
import com.vaadin.flow.shared.Registration;
import com.vaadin.flow.spring.annotation.SpringComponent;
import org.vaadin.firitin.components.grid.PagingGrid;
import com.vaadin.flow.component.grid.GridSortOrder;
import com.vaadin.flow.data.provider.SortDirection;
import jakarta.annotation.security.PermitAll;
import org.springframework.context.annotation.Scope;
import org.vaadin.firitin.components.grid.VGrid;
import java.util.stream.Stream;
import java.util.List;
@SpringComponent
@Scope("prototype")
@ -30,97 +23,87 @@ import java.util.stream.Stream;
@Route(value = "/employees", layout = MainLayout.class)
@PermitAll
public class EmployeesListView extends Main {
private final EmployeeService employeeService;
private final PagingGrid<Employee> table = new PagingGrid<>(Employee.class);
public EmployeesListView(final EmployeeService employeeService) {
this.employeeService = employeeService;
final H2 title = new H2("Employees list");
final HorizontalLayout hl = new HorizontalLayout();
final HorizontalLayout hf = new HorizontalLayout();
final Button employeeListAscendingOrder = new Button("Employee List in Ascending Order");
employeeListAscendingOrder.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
// no-op
});
hl.add(employeeListAscendingOrder);
final Button employeeListDescendingOrder = new Button("Employee List in Descending Order");
employeeListDescendingOrder.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
// no-op
});
hl.add(employeeListDescendingOrder);
final VGrid<Employee> grid = new VGrid<>(Employee.class);
grid.setColumns("firstName", "lastName", "status");
grid.setAllRowsVisible(true);
grid.addComponentColumn((ValueProvider<Employee, Component>) employee -> {
ComboBox<String> statusComboBox = new ComboBox<>();
statusComboBox.setItems("Active", "Inactive");
return statusComboBox;
}).setHeader("Change Status");
grid.addComponentColumn((ValueProvider<Employee, Component>) employee -> {
final Button edit = new Button("Edit");
edit.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
// no-op
});
return edit;
});
grid.addComponentColumn((ValueProvider<Employee, Component>) employee -> {
final Button save = new Button("Save");
save.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
// no-op
});
return save;
});
setupView();
refreshGrid();
}
grid.setDataProvider(new DataProvider<>() {
@Override
public boolean isInMemory() {
return false;
}
private void setupView() {
add(new H2("Employee List"));
configureTable();
add(createAddEmployeeButton());
add(table);
}
@Override
public int size(final Query<Employee, Object> query) {
return employeeService.getEmployees().size();
}
private void configureTable() {
table.setColumns("firstName", "lastName", "status");
addEditButtonColumn("Edit", this::navigateToEditView);
setupPagingGrid();
}
@Override
public Stream<Employee> fetch(final Query<Employee, Object> query) {
int limit = query.getLimit();
int pagerSize = query.getPageSize();
int page = query.getPage();
return employeeService.getEmployees().stream();
}
private void updateEmployeeStatus(final Employee employee, final boolean isActive) {
employee.setStatus(isActive ? Employee.Status.ACTIVE : Employee.Status.INACTIVE);
employeeService.createOrUpdate(employee);
refreshGrid();
}
@Override
public void refreshItem(final Employee employee) {
// no-op
}
private void addEditButtonColumn(final String label, final ButtonClickHandler handler) {
table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee)));
}
@Override
public void refreshAll() {
// no-op
}
private Button createButton(final String label, final Runnable onClickAction) {
Button button = new Button(label);
button.addClickListener(event -> onClickAction.run());
return button;
}
@Override
public Registration addDataProviderListener(final DataProviderListener<Employee> dataProviderListener) {
return null;
}
});
private Button createAddEmployeeButton() {
return createButton("Add Employee", this::navigateToAddEmployeeView);
}
final Button previous = new Button("Previous");
previous.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
// no-op
});
hf.add(previous);
final Button next = new Button("Next");
next.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
// no-op
});
hf.add(next);
final Button addEmployee = new Button("Add Employee");
addEmployee.addClickListener((ComponentEventListener<ClickEvent<Button>>) buttonClickEvent -> {
this.getUI().get().navigate(EmployeeView.class, "new");
});
hf.add(addEmployee);
private void navigateToEditView(final Employee employee) {
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, employee.getId().toString()));
Outdated
Review

podemos usar el componente PagingGrid de la llibreria Viritin (ya esta incluida). Ejemplo: https://addons.dokku1.parttio.org/paginggrid y codigo https://github.com/parttio/addon-demos/blob/main/src/main/java/org/example/views/PagingGridView.java

asi ya no necesitamos crear los botones y navegacion manualmente.

podemos usar el componente PagingGrid de la llibreria Viritin (ya esta incluida). Ejemplo: https://addons.dokku1.parttio.org/paginggrid y codigo https://github.com/parttio/addon-demos/blob/main/src/main/java/org/example/views/PagingGridView.java asi ya no necesitamos crear los botones y navegacion manualmente.
}
add(title, hl, grid, hf);
private void navigateToAddEmployeeView() {
getUI().ifPresent(ui -> ui.navigate(EmployeeView.class, "new"));
}
private void setupPagingGrid() {
table.setPaginationBarMode(PagingGrid.PaginationBarMode.BOTTOM);
table.setPageSize(5);
}
private void refreshGrid() {
table.setPagingDataProvider((page, pageSize) -> fetchEmployees((int) page, pageSize));
}
private List<Employee> fetchEmployees(final int page, final int pageSize) {
int start = page * pageSize;
if (hasSortOrder()) {
return fetchSortedEmployees(start, pageSize);
}
return employeeService.findEmployees(start, pageSize);
}
private boolean hasSortOrder() {
return !table.getSortOrder().isEmpty();
}
private List<Employee> fetchSortedEmployees(final int start, final int pageSize) {
GridSortOrder<Employee> sortOrder = table.getSortOrder().getFirst();
return employeeService.findEmployees(start, pageSize,
sortOrder.getSorted().getKey(),
sortOrder.getDirection() == SortDirection.ASCENDING);
}
@FunctionalInterface
private interface ButtonClickHandler {
void handle(Employee employee);
}
}

View File

@ -14,3 +14,14 @@ insert into employee (id, version, first_name, last_name, status) values ('f6ab3
insert into employee (id, version, first_name, last_name, status) values ('2e2293b1-3f9a-4f3d-abc8-32639b0a5e15', 1, 'Carlos', 'Lopez Mendoza', 'INACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('4b1c6c35-4627-4b35-b6e9-dc75c68b2c31', 1, 'Maria', 'Fernandez Villca', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('afc5c741-f70a-4394-853b-39d51b118927', 1, 'Luis', 'Gutierrez Mamani', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('b2436b82-7b9f-4f0d-9463-f2c3173a45c3', 1, 'Laura', 'Martinez Paredes', 'INACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('6e6a8a4e-9f6b-44eb-8c69-40acfdc86756', 1, 'Roberto', 'Santos Escobar', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('36b0d1c6-bdc0-4d98-94bb-08b9bce3f0d5', 1, 'Valeria', 'Morales Ochoa', 'INACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('5a1c6d80-58b3-43e3-a5a5-24b4a2d1d54a', 1, 'Jorge', 'Ramirez Tapia', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('9d6a5b2e-6d0b-4b89-8d6a-d3f3d1bfc047', 1, 'Sandra', 'Torres Huanca', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('f8b3e0c0-0d5a-4e5c-bf9d-207b9b5e8279', 1, 'Felipe', 'Quispe Huanca', 'INACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('cd80e1d0-9a08-44a6-bd63-2c63eaa003d4', 1, 'Gabriela', 'Rivas Arana', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('62d3c1b7-815e-4e96-8d7e-f8c4236bca55', 1, 'Oscar', 'Flores Quiroga', 'INACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('f20b7c5a-5a67-44f0-9ec1-4c1b8e80de05', 1, 'Marta', 'Vargas Soria', 'ACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('19b5a76e-d7b1-4b76-8b02-4d0748e85809', 1, 'Andres', 'Espinoza Chura', 'INACTIVE');
insert into employee (id, version, first_name, last_name, status) values ('5c1a7b82-832d-4f24-8377-54b77b91b6a8', 1, 'Carla', 'Villanueva Arce', 'ACTIVE');