From 05ec0fc8dacb6585d6a5ea4c233614bebb71ab03 Mon Sep 17 00:00:00 2001 From: filippo-ferrari Date: Thu, 12 Sep 2024 11:32:47 +0200 Subject: [PATCH] feat: export to CSV --- .../munera/services/CSVService.java | 39 +++++++++++++++ .../application/munera/views/MainLayout.java | 26 +++++++--- .../munera/views/expenses/ExpensesView.java | 49 ------------------- 3 files changed, 59 insertions(+), 55 deletions(-) create mode 100644 src/main/java/com/application/munera/services/CSVService.java diff --git a/src/main/java/com/application/munera/services/CSVService.java b/src/main/java/com/application/munera/services/CSVService.java new file mode 100644 index 0000000..e596471 --- /dev/null +++ b/src/main/java/com/application/munera/services/CSVService.java @@ -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 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()); + }); + } +} diff --git a/src/main/java/com/application/munera/views/MainLayout.java b/src/main/java/com/application/munera/views/MainLayout.java index 1e39091..fdc570f 100644 --- a/src/main/java/com/application/munera/views/MainLayout.java +++ b/src/main/java/com/application/munera/views/MainLayout.java @@ -1,5 +1,7 @@ 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.dashboard.DashboardView; 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.DrawerToggle; import com.vaadin.flow.component.button.Button; -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.html.*; import com.vaadin.flow.component.orderedlayout.FlexComponent; import com.vaadin.flow.component.orderedlayout.HorizontalLayout; 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.PageTitle; +import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.spring.security.AuthenticationContext; import com.vaadin.flow.theme.lumo.LumoUtility; import org.vaadin.lineawesome.LineAwesomeIcon; @@ -30,9 +30,13 @@ public class MainLayout extends AppLayout { private H1 viewTitle; 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.csvService = csvService; + this.expenseService = expenseService; setPrimarySection(Section.DRAWER); addDrawerContent(); addHeaderContent(); @@ -64,8 +68,18 @@ public class MainLayout extends AppLayout { Button logout = new Button("Logout", click -> this.authContext.logout()); 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 - HorizontalLayout header = new HorizontalLayout(userInfoLayout, logout); + HorizontalLayout header = new HorizontalLayout(exportToCSVButton, userInfoLayout, logout); header.setWidthFull(); // Make the header take the full width header.setDefaultVerticalComponentAlignment(FlexComponent.Alignment.CENTER); header.setJustifyContentMode(FlexComponent.JustifyContentMode.END); // Align items to the right diff --git a/src/main/java/com/application/munera/views/expenses/ExpensesView.java b/src/main/java/com/application/munera/views/expenses/ExpensesView.java index c76b012..60f6648 100644 --- a/src/main/java/com/application/munera/views/expenses/ExpensesView.java +++ b/src/main/java/com/application/munera/views/expenses/ExpensesView.java @@ -13,7 +13,6 @@ import com.vaadin.flow.component.datepicker.DatePicker; import com.vaadin.flow.component.dependency.Uses; import com.vaadin.flow.component.formlayout.FormLayout; 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.icon.Icon; 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.renderer.ComponentRenderer; import com.vaadin.flow.router.*; -import com.vaadin.flow.server.StreamResource; import jakarta.annotation.security.PermitAll; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.orm.ObjectOptimisticLockingFailureException; import org.springframework.security.core.userdetails.UserDetails; 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 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.Optional; @@ -62,7 +52,6 @@ public class ExpensesView extends Div implements BeforeEnterObserver { private final Button cancel = new Button("Cancel"); private final Button save = new Button("Save"); private final Button delete = new Button("Delete"); - private final Button exportToCSVButton = new Button("Export to CSV"); private final BeanValidationBinder binder; private Expense expense; @@ -120,22 +109,6 @@ public class ExpensesView extends Div implements BeforeEnterObserver { grid.setPageSize(22); // setting page size 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 grid.asSingleSelect().addValueChangeListener(event -> { 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 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()); - }); - } } \ No newline at end of file