feat: export to CSV
This commit is contained in:
parent
ac67892cae
commit
05ec0fc8da
3 changed files with 59 additions and 55 deletions
|
@ -0,0 +1,39 @@
|
||||||
|
package com.application.munera.services;
|
||||||
|
|
||||||
|
import com.application.munera.data.Expense;
|
||||||
|
import com.vaadin.flow.server.StreamResource;
|
||||||
|
import org.apache.commons.csv.CSVFormat;
|
||||||
|
import org.apache.commons.csv.CSVPrinter;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.io.ByteArrayInputStream;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class CSVService {
|
||||||
|
|
||||||
|
public StreamResource createCSVResource(List<Expense> expenses) {
|
||||||
|
return new StreamResource("expenses.csv", () -> {
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
try (OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
|
||||||
|
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.withHeader("Name", "Cost", "Category", "Date", "Payment date"))) {
|
||||||
|
|
||||||
|
for (Expense expense : expenses) {
|
||||||
|
csvPrinter.printRecord(
|
||||||
|
expense.getName(),
|
||||||
|
expense.getCost(),
|
||||||
|
expense.getCategory() != null ? expense.getCategory().getName() : "",
|
||||||
|
expense.getDate(),
|
||||||
|
expense.getPaymentDate() != null ? expense.getPaymentDate().toLocalDate() : "Unpaid"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return new ByteArrayInputStream(stream.toByteArray());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
package com.application.munera.views;
|
package com.application.munera.views;
|
||||||
|
|
||||||
|
import com.application.munera.services.CSVService;
|
||||||
|
import com.application.munera.services.ExpenseService;
|
||||||
import com.application.munera.views.categories.CategoriesView;
|
import com.application.munera.views.categories.CategoriesView;
|
||||||
import com.application.munera.views.dashboard.DashboardView;
|
import com.application.munera.views.dashboard.DashboardView;
|
||||||
import com.application.munera.views.events.EventsView;
|
import com.application.munera.views.events.EventsView;
|
||||||
|
@ -9,16 +11,14 @@ import com.application.munera.views.settings.SettingsView;
|
||||||
import com.vaadin.flow.component.applayout.AppLayout;
|
import com.vaadin.flow.component.applayout.AppLayout;
|
||||||
import com.vaadin.flow.component.applayout.DrawerToggle;
|
import com.vaadin.flow.component.applayout.DrawerToggle;
|
||||||
import com.vaadin.flow.component.button.Button;
|
import com.vaadin.flow.component.button.Button;
|
||||||
import com.vaadin.flow.component.html.Footer;
|
import com.vaadin.flow.component.html.*;
|
||||||
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.orderedlayout.FlexComponent;
|
import com.vaadin.flow.component.orderedlayout.FlexComponent;
|
||||||
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
import com.vaadin.flow.component.orderedlayout.HorizontalLayout;
|
||||||
import com.vaadin.flow.component.orderedlayout.Scroller;
|
import com.vaadin.flow.component.orderedlayout.Scroller;
|
||||||
import com.vaadin.flow.component.sidenav.SideNav;
|
import com.vaadin.flow.component.sidenav.SideNav;
|
||||||
import com.vaadin.flow.component.sidenav.SideNavItem;
|
import com.vaadin.flow.component.sidenav.SideNavItem;
|
||||||
import com.vaadin.flow.router.PageTitle;
|
import com.vaadin.flow.router.PageTitle;
|
||||||
|
import com.vaadin.flow.server.StreamResource;
|
||||||
import com.vaadin.flow.spring.security.AuthenticationContext;
|
import com.vaadin.flow.spring.security.AuthenticationContext;
|
||||||
import com.vaadin.flow.theme.lumo.LumoUtility;
|
import com.vaadin.flow.theme.lumo.LumoUtility;
|
||||||
import org.vaadin.lineawesome.LineAwesomeIcon;
|
import org.vaadin.lineawesome.LineAwesomeIcon;
|
||||||
|
@ -30,9 +30,13 @@ public class MainLayout extends AppLayout {
|
||||||
|
|
||||||
private H1 viewTitle;
|
private H1 viewTitle;
|
||||||
private final transient AuthenticationContext authContext;
|
private final transient AuthenticationContext authContext;
|
||||||
|
private final CSVService csvService;
|
||||||
|
private final ExpenseService expenseService;
|
||||||
|
|
||||||
public MainLayout(AuthenticationContext authContext) {
|
public MainLayout(AuthenticationContext authContext, CSVService csvService, ExpenseService expenseService) {
|
||||||
this.authContext = authContext;
|
this.authContext = authContext;
|
||||||
|
this.csvService = csvService;
|
||||||
|
this.expenseService = expenseService;
|
||||||
setPrimarySection(Section.DRAWER);
|
setPrimarySection(Section.DRAWER);
|
||||||
addDrawerContent();
|
addDrawerContent();
|
||||||
addHeaderContent();
|
addHeaderContent();
|
||||||
|
@ -64,8 +68,18 @@ public class MainLayout extends AppLayout {
|
||||||
Button logout = new Button("Logout", click -> this.authContext.logout());
|
Button logout = new Button("Logout", click -> this.authContext.logout());
|
||||||
logout.getStyle().set("padding", "10px"); // add padding to the logout button
|
logout.getStyle().set("padding", "10px"); // add padding to the logout button
|
||||||
|
|
||||||
|
// Create the Export to CSV button
|
||||||
|
Button exportToCSVButton = new Button("Export to CSV");
|
||||||
|
exportToCSVButton.addClickListener(event -> {
|
||||||
|
// Call a method in ExpensesView to handle CSV creation
|
||||||
|
StreamResource resource = this.csvService.createCSVResource(this.expenseService.findAll());
|
||||||
|
Anchor downloadLink = new Anchor(resource, "Download CSV");
|
||||||
|
downloadLink.getElement().setAttribute("download", true);
|
||||||
|
addToNavbar(downloadLink); // Add the download link to the navbar
|
||||||
|
});
|
||||||
|
|
||||||
// Create the header layout and add all elements
|
// Create the header layout and add all elements
|
||||||
HorizontalLayout header = new HorizontalLayout(userInfoLayout, logout);
|
HorizontalLayout header = new HorizontalLayout(exportToCSVButton, userInfoLayout, logout);
|
||||||
header.setWidthFull(); // Make the header take the full width
|
header.setWidthFull(); // Make the header take the full width
|
||||||
header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER);
|
||||||
header.setJustifyContentMode(FlexComponent.JustifyContentMode.END); // Align items to the right
|
header.setJustifyContentMode(FlexComponent.JustifyContentMode.END); // Align items to the right
|
||||||
|
|
|
@ -13,7 +13,6 @@ import com.vaadin.flow.component.datepicker.DatePicker;
|
||||||
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.GridVariant;
|
import com.vaadin.flow.component.grid.GridVariant;
|
||||||
import com.vaadin.flow.component.html.Anchor;
|
|
||||||
import com.vaadin.flow.component.html.Div;
|
import com.vaadin.flow.component.html.Div;
|
||||||
import com.vaadin.flow.component.icon.Icon;
|
import com.vaadin.flow.component.icon.Icon;
|
||||||
import com.vaadin.flow.component.notification.Notification;
|
import com.vaadin.flow.component.notification.Notification;
|
||||||
|
@ -28,22 +27,13 @@ import com.vaadin.flow.data.binder.ValidationException;
|
||||||
import com.vaadin.flow.data.converter.StringToBigDecimalConverter;
|
import com.vaadin.flow.data.converter.StringToBigDecimalConverter;
|
||||||
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
import com.vaadin.flow.data.renderer.ComponentRenderer;
|
||||||
import com.vaadin.flow.router.*;
|
import com.vaadin.flow.router.*;
|
||||||
import com.vaadin.flow.server.StreamResource;
|
|
||||||
import jakarta.annotation.security.PermitAll;
|
import jakarta.annotation.security.PermitAll;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
import org.springframework.orm.ObjectOptimisticLockingFailureException;
|
||||||
import org.springframework.security.core.userdetails.UserDetails;
|
import org.springframework.security.core.userdetails.UserDetails;
|
||||||
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
import org.springframework.security.core.userdetails.UsernameNotFoundException;
|
||||||
import org.apache.commons.csv.CSVPrinter;
|
|
||||||
import org.apache.commons.csv.CSVFormat;
|
|
||||||
|
|
||||||
import org.vaadin.klaudeta.PaginatedGrid;
|
import org.vaadin.klaudeta.PaginatedGrid;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.OutputStreamWriter;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
|
@ -62,7 +52,6 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
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");
|
||||||
private final Button delete = new Button("Delete");
|
private final Button delete = new Button("Delete");
|
||||||
private final Button exportToCSVButton = new Button("Export to CSV");
|
|
||||||
private final BeanValidationBinder<Expense> binder;
|
private final BeanValidationBinder<Expense> binder;
|
||||||
|
|
||||||
private Expense expense;
|
private Expense expense;
|
||||||
|
@ -120,22 +109,6 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
grid.setPageSize(22); // setting page size
|
grid.setPageSize(22); // setting page size
|
||||||
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
grid.addThemeVariants(GridVariant.LUMO_NO_BORDER);
|
||||||
|
|
||||||
// Export to CSV button
|
|
||||||
exportToCSVButton.addClickListener(CSVClickEvent -> {
|
|
||||||
StreamResource resource = createCSVResource(expenseService.findAll());
|
|
||||||
Anchor downloadLink = new Anchor(resource, "Download CSV");
|
|
||||||
downloadLink.getElement().setAttribute("download", true);
|
|
||||||
|
|
||||||
// Remove old download links if any
|
|
||||||
this.getChildren().filter(Anchor.class::isInstance)
|
|
||||||
.forEach(child -> remove((Anchor) child));
|
|
||||||
|
|
||||||
// Add the new download link
|
|
||||||
add(downloadLink);
|
|
||||||
});
|
|
||||||
|
|
||||||
add(exportToCSVButton);
|
|
||||||
|
|
||||||
// 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) UI.getCurrent().navigate(String.format(EXPENSE_EDIT_ROUTE_TEMPLATE, event.getValue().getId()));
|
if (event.getValue() != null) UI.getCurrent().navigate(String.format(EXPENSE_EDIT_ROUTE_TEMPLATE, event.getValue().getId()));
|
||||||
|
@ -356,26 +329,4 @@ public class ExpensesView extends Div implements BeforeEnterObserver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private StreamResource createCSVResource(List<Expense> expenses) {
|
|
||||||
return new StreamResource("expenses.csv", () -> {
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
try (OutputStreamWriter writer = new OutputStreamWriter(stream, StandardCharsets.UTF_8);
|
|
||||||
CSVPrinter csvPrinter = new CSVPrinter(writer, CSVFormat.DEFAULT.withHeader("Name", "Cost", "Category", "Date", "Payment date"))) {
|
|
||||||
|
|
||||||
for (Expense expense : expenses) {
|
|
||||||
csvPrinter.printRecord(
|
|
||||||
expense.getName(),
|
|
||||||
expense.getCost(),
|
|
||||||
expense.getCategory() != null ? expense.getCategory().getName() : "",
|
|
||||||
expense.getDate(),
|
|
||||||
expense.getPaymentDate() != null ? expense.getPaymentDate().toLocalDate() : "Unpaid"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
return new ByteArrayInputStream(stream.toByteArray());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
Loading…
Reference in a new issue