feat: TreeGrid first iteration
work in progress
This commit is contained in:
parent
2415858d52
commit
b7a6a2fdb3
5 changed files with 67 additions and 30 deletions
|
@ -0,0 +1,10 @@
|
||||||
|
package com.application.munera.data;
|
||||||
|
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
@Getter
|
||||||
|
@Setter
|
||||||
|
public abstract class AbstractEntity {
|
||||||
|
private Long id;
|
||||||
|
}
|
|
@ -13,7 +13,7 @@ import java.util.Set;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Table(name = "expenses")
|
@Table(name = "expenses")
|
||||||
public class Expense {
|
public class Expense extends AbstractEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
|
|
@ -14,7 +14,7 @@ import java.util.Set;
|
||||||
@Getter
|
@Getter
|
||||||
@Setter
|
@Setter
|
||||||
@Table(name = "people")
|
@Table(name = "people")
|
||||||
public class Person {
|
public class Person extends AbstractEntity {
|
||||||
|
|
||||||
@Id
|
@Id
|
||||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||||
|
|
|
@ -11,6 +11,7 @@ import org.springframework.stereotype.Service;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
@Service
|
@Service
|
||||||
public class ExpenseService {
|
public class ExpenseService {
|
||||||
|
@ -40,6 +41,13 @@ public class ExpenseService {
|
||||||
public Collection<Expense> findUnpaidCreditByUser(final Person person) {
|
public Collection<Expense> findUnpaidCreditByUser(final Person person) {
|
||||||
return repository.findUnpaidCreditorsExpensesByPersonId(person.getId());
|
return repository.findUnpaidCreditorsExpensesByPersonId(person.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Expense> findExpenseByUser(final Person person) {
|
||||||
|
final var credits = this.findCreditByUser(person);
|
||||||
|
final var debits = this.findDebtByUser(person);
|
||||||
|
return Stream.concat(credits.stream(), debits.stream()).toList();
|
||||||
|
}
|
||||||
|
|
||||||
public List<Expense> findAll() {return repository.findAll();}
|
public List<Expense> findAll() {return repository.findAll();}
|
||||||
|
|
||||||
public void update(Expense entity) {
|
public void update(Expense entity) {
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package com.application.munera.views.people;
|
package com.application.munera.views.people;
|
||||||
|
|
||||||
|
import com.application.munera.data.Expense;
|
||||||
import com.application.munera.data.Person;
|
import com.application.munera.data.Person;
|
||||||
|
import com.application.munera.services.ExpenseService;
|
||||||
import com.application.munera.services.PersonService;
|
import com.application.munera.services.PersonService;
|
||||||
import com.application.munera.views.MainLayout;
|
import com.application.munera.views.MainLayout;
|
||||||
import com.vaadin.flow.component.UI;
|
import com.vaadin.flow.component.UI;
|
||||||
|
@ -8,7 +10,6 @@ import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.button.ButtonVariant;
|
import com.vaadin.flow.component.button.ButtonVariant;
|
||||||
import com.vaadin.flow.component.dependency.Uses;
|
import com.vaadin.flow.component.dependency.Uses;
|
||||||
import com.vaadin.flow.component.formlayout.FormLayout;
|
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.GridVariant;
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.html.Span;
|
import com.vaadin.flow.component.html.Span;
|
||||||
|
@ -20,19 +21,18 @@ import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.splitlayout.SplitLayout;
|
import com.vaadin.flow.component.splitlayout.SplitLayout;
|
||||||
import com.vaadin.flow.component.textfield.EmailField;
|
import com.vaadin.flow.component.textfield.EmailField;
|
||||||
import com.vaadin.flow.component.textfield.TextField;
|
import com.vaadin.flow.component.textfield.TextField;
|
||||||
|
import com.vaadin.flow.component.treegrid.TreeGrid;
|
||||||
import com.vaadin.flow.data.binder.BeanValidationBinder;
|
import com.vaadin.flow.data.binder.BeanValidationBinder;
|
||||||
import com.vaadin.flow.data.binder.ValidationException;
|
import com.vaadin.flow.data.binder.ValidationException;
|
||||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
|
||||||
import com.vaadin.flow.router.BeforeEnterEvent;
|
import com.vaadin.flow.router.BeforeEnterEvent;
|
||||||
import com.vaadin.flow.router.BeforeEnterObserver;
|
import com.vaadin.flow.router.BeforeEnterObserver;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
import com.vaadin.flow.router.Route;
|
import com.vaadin.flow.router.Route;
|
||||||
import com.vaadin.flow.spring.data.VaadinSpringDataHelpers;
|
|
||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.data.domain.PageRequest;
|
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
|
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
@PageTitle("People")
|
@PageTitle("People")
|
||||||
|
@ -44,7 +44,7 @@ public class PeopleView extends Div implements BeforeEnterObserver {
|
||||||
private static final String PERSON_ID = "personID";
|
private static final String PERSON_ID = "personID";
|
||||||
private static final String PERSON_EDIT_ROUTE_TEMPLATE = "people/%s/edit";
|
private static final String PERSON_EDIT_ROUTE_TEMPLATE = "people/%s/edit";
|
||||||
|
|
||||||
private final Grid<Person> grid = new Grid<>(Person.class, false);
|
private final TreeGrid<Object> grid = new TreeGrid<>();
|
||||||
|
|
||||||
private final Button cancel = new Button("Cancel");
|
private final Button cancel = new Button("Cancel");
|
||||||
private final Button save = new Button("Save");
|
private final Button save = new Button("Save");
|
||||||
|
@ -54,12 +54,14 @@ public class PeopleView extends Div implements BeforeEnterObserver {
|
||||||
|
|
||||||
private Person person;
|
private Person person;
|
||||||
private final PersonService personService;
|
private final PersonService personService;
|
||||||
|
private final ExpenseService expenseService;
|
||||||
private TextField firstName;
|
private TextField firstName;
|
||||||
private TextField lastName;
|
private TextField lastName;
|
||||||
private EmailField email;
|
private EmailField email;
|
||||||
|
|
||||||
public PeopleView(PersonService personService) {
|
public PeopleView(PersonService personService, ExpenseService expenseService) {
|
||||||
this.personService = personService;
|
this.personService = personService;
|
||||||
|
this.expenseService = expenseService;
|
||||||
addClassNames("expenses-view");
|
addClassNames("expenses-view");
|
||||||
|
|
||||||
// Create UI
|
// Create UI
|
||||||
|
@ -70,32 +72,32 @@ public class PeopleView extends Div implements BeforeEnterObserver {
|
||||||
|
|
||||||
add(splitLayout);
|
add(splitLayout);
|
||||||
|
|
||||||
// Configure Grid
|
|
||||||
grid.addColumn(Person::getFirstName).setHeader("First Name").setSortable(true);
|
|
||||||
grid.addColumn(Person::getLastName).setHeader("Last Name").setSortable(true);
|
|
||||||
grid.addColumn(Person::getEmail).setHeader("Email").setSortable(true);
|
|
||||||
grid.addColumn(personService::calculateDebt).setHeader("Debt").setSortable(true);
|
|
||||||
grid.addColumn(personService::calculateCredit).setHeader("Credit").setSortable(true);
|
|
||||||
grid.addColumn(personService::calculateNetBalance).setHeader("Total Expenses value").setSortable(true);
|
|
||||||
grid.addColumn(new ComponentRenderer<>(persona -> {
|
|
||||||
final var netBalance = personService.calculateNetBalance(persona);
|
|
||||||
return createBadge(netBalance);
|
|
||||||
})).setHeader("Balance Status").setSortable(true);
|
|
||||||
|
|
||||||
|
|
||||||
grid.getColumns().forEach(col -> col.setAutoWidth(true));
|
|
||||||
grid.setItems(query -> personService.list(
|
|
||||||
PageRequest.of(query.getPage(), query.getPageSize(), VaadinSpringDataHelpers.toSpringDataSort(query)))
|
|
||||||
.stream());
|
|
||||||
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
||||||
|
grid.addHierarchyColumn(this::getNodeName).setHeader("Name");
|
||||||
|
grid.addColumn(this::getNodeType).setHeader("Type");
|
||||||
|
|
||||||
|
List<Person> people = (List<Person>) personService.findAll();
|
||||||
|
|
||||||
|
for (Person person : people) {
|
||||||
|
// Add the person as a root item
|
||||||
|
grid.getTreeData().addItem(null, person);
|
||||||
|
|
||||||
|
// Fetch expenses for the current person
|
||||||
|
List<Expense> expenses = expenseService.findExpenseByUser(person);
|
||||||
|
|
||||||
|
// Add each expense as a child item under the person
|
||||||
|
for (Expense expense : expenses) {
|
||||||
|
grid.getTreeData().addItem(person, expense);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// when a row is selected or deselected, populate form
|
// when a row is selected or deselected, populate form
|
||||||
grid.asSingleSelect().addValueChangeListener(event -> {
|
grid.asSingleSelect().addValueChangeListener(event -> {
|
||||||
if (event.getValue() != null) {
|
Object selectedItem = event.getValue();
|
||||||
UI.getCurrent().navigate(String.format(PERSON_EDIT_ROUTE_TEMPLATE, event.getValue().getId()));
|
if (selectedItem instanceof Person) {
|
||||||
} else {
|
Person selectedPerson = (Person) selectedItem;
|
||||||
clearForm();
|
UI.getCurrent().navigate(String.format("people/%d/edit", selectedPerson.getId()));
|
||||||
UI.getCurrent().navigate(PeopleView.class);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -149,6 +151,23 @@ public class PeopleView extends Div implements BeforeEnterObserver {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getNodeName(Object node) {
|
||||||
|
if (node instanceof Person) {
|
||||||
|
return ((Person) node).getFirstName() + " " + ((Person) node).getLastName();
|
||||||
|
} else if (node instanceof Expense) {
|
||||||
|
return ((Expense) node).getName() + " - $" + ((Expense) node).getCost();
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getNodeType(Object node) {
|
||||||
|
if (node instanceof Person) {
|
||||||
|
return "Person";
|
||||||
|
} else if (node instanceof Expense) {
|
||||||
|
return "Expense";
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public void beforeEnter(BeforeEnterEvent event) {
|
public void beforeEnter(BeforeEnterEvent event) {
|
||||||
Optional<Long> personId = event.getRouteParameters().get(PERSON_ID).map(Long::parseLong);
|
Optional<Long> personId = event.getRouteParameters().get(PERSON_ID).map(Long::parseLong);
|
||||||
|
|
Loading…
Reference in a new issue