diff --git a/src/main/bundles/prod.bundle b/src/main/bundles/prod.bundle index a93fb73..beeca76 100644 Binary files a/src/main/bundles/prod.bundle and b/src/main/bundles/prod.bundle differ diff --git a/src/main/java/com/primefactorsolutions/views/EmployeeView.java b/src/main/java/com/primefactorsolutions/views/EmployeeView.java index 2e82562..20ff9ef 100644 --- a/src/main/java/com/primefactorsolutions/views/EmployeeView.java +++ b/src/main/java/com/primefactorsolutions/views/EmployeeView.java @@ -33,6 +33,7 @@ import com.vaadin.flow.router.*; import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.spring.annotation.SpringComponent; import jakarta.annotation.security.PermitAll; +import lombok.extern.slf4j.Slf4j; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.datepicker.VDatePicker; import org.vaadin.firitin.form.BeanValidationForm; @@ -52,6 +53,7 @@ import java.util.UUID; @Scope("prototype") @PageTitle("Employee") @Route(value = "/employees/:employeeId?/:action?", layout = MainLayout.class) +@Slf4j public class EmployeeView extends BeanValidationForm implements HasUrlParameter { private final EmployeeService employeeService; private final ReportService reportService; @@ -340,10 +342,10 @@ public class EmployeeView extends BeanValidationForm implements HasUrl profileImagePreview.setMaxHeight("150px"); } catch (IOException e) { Notification.show("Error al subir la imagen: " + e.getMessage()); - e.printStackTrace(); + log.error("Error uploading image", e); } catch (Exception e) { Notification.show("Error en el servidor al procesar la imagen."); - e.printStackTrace(); + log.error("Error uploading image", e); } }); } diff --git a/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java b/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java index ea18971..607564b 100644 --- a/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java +++ b/src/main/java/com/primefactorsolutions/views/admin/EmployeesListView.java @@ -6,6 +6,7 @@ import com.primefactorsolutions.views.BaseView; import com.primefactorsolutions.views.Constants; import com.primefactorsolutions.views.EmployeeView; import com.primefactorsolutions.views.MainLayout; +import com.primefactorsolutions.views.util.MenuBarUtils; import com.vaadin.flow.component.button.Button; import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.html.Anchor; @@ -16,6 +17,8 @@ import com.vaadin.flow.server.StreamResource; import com.vaadin.flow.spring.annotation.SpringComponent; import com.vaadin.flow.spring.security.AuthenticationContext; import jakarta.annotation.security.RolesAllowed; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.tuple.Pair; import org.apache.poi.ss.usermodel.*; import org.apache.poi.xssf.usermodel.XSSFWorkbook; import org.vaadin.firitin.components.grid.PagingGrid; @@ -33,6 +36,7 @@ import java.util.List; @PageTitle("Employees") @Route(value = "/employees", layout = MainLayout.class) @RolesAllowed("ROLE_ADMIN") +@Slf4j public class EmployeesListView extends BaseView { private final EmployeeService employeeService; @@ -131,7 +135,7 @@ public class EmployeesListView extends BaseView { workbook.write(out); return new ByteArrayInputStream(out.toByteArray()); } catch (IOException e) { - e.printStackTrace(); + log.error("Error generating excel", e); return null; } } @@ -139,15 +143,12 @@ public class EmployeesListView extends BaseView { private void configureTable() { table.setColumns("firstName", "lastName", "status"); - addEditButtonColumn("View", this::navigateToEmployeeView); - addEditButtonColumn("Edit", this::navigateToEditView); + table.addComponentColumn(employee -> MenuBarUtils.menuBar( + Pair.of("View", __ -> navigateToEmployeeView(employee)), + Pair.of("Edit", __ -> navigateToEditView(employee)))); setupPagingGrid(); } - private void addEditButtonColumn(final String label, final ButtonClickHandler handler) { - table.addComponentColumn(employee -> createButton(label, () -> handler.handle(employee), false)); - } - private Button createButton(final String label, final Runnable onClickAction, final boolean isPrimary) { Button button = new Button(label); diff --git a/src/main/java/com/primefactorsolutions/views/assessment/AssessmentsListView.java b/src/main/java/com/primefactorsolutions/views/assessment/AssessmentsListView.java index 0baa3b5..59e4132 100644 --- a/src/main/java/com/primefactorsolutions/views/assessment/AssessmentsListView.java +++ b/src/main/java/com/primefactorsolutions/views/assessment/AssessmentsListView.java @@ -5,30 +5,24 @@ import com.primefactorsolutions.service.AssessmentService; import com.primefactorsolutions.views.BaseView; import com.primefactorsolutions.views.MainLayout; import com.primefactorsolutions.views.SubmissionView; -import com.vaadin.flow.component.ClickEvent; -import com.vaadin.flow.component.Component; -import com.vaadin.flow.component.ComponentEventListener; +import com.primefactorsolutions.views.util.MenuBarUtils; import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.confirmdialog.ConfirmDialog; import com.vaadin.flow.component.grid.Grid; import com.vaadin.flow.component.notification.Notification; 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.data.provider.ListDataProvider; 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 com.vaadin.flow.spring.security.AuthenticationContext; import jakarta.annotation.security.PermitAll; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.context.annotation.Scope; import org.vaadin.addon.stefan.clipboard.ClientsideClipboard; import org.vaadin.firitin.components.grid.VGrid; -import java.util.stream.Stream; - @SpringComponent @Scope("prototype") @PageTitle("Assessments") @@ -41,90 +35,48 @@ public class AssessmentsListView extends BaseView { super(authenticationContext); final HorizontalLayout hl = new HorizontalLayout(); final Button addAssessment = new Button("Add Assessment"); - addAssessment.addClickListener((ComponentEventListener>) buttonClickEvent -> { - this.getUI().flatMap(ui -> ui.navigate(AssessmentView.class, "new")); - }); + addAssessment.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + addAssessment.addClickListener(buttonClickEvent -> + getUI().flatMap(ui -> ui.navigate(AssessmentView.class, "new"))); hl.add(addAssessment); final VGrid grid = new VGrid<>(Assessment.class); grid.setColumns("id", "candidate.email"); - final Grid.Column statusColumn = grid.addColumn((ValueProvider) assessment -> + final Grid.Column statusColumn = grid.addColumn(assessment -> assessment.getAssessmentEvents().isEmpty() ? "N/A" : assessment.getAssessmentEvents().getLast().getStatus().name()); statusColumn.setHeader("Status"); - grid.addComponentColumn((ValueProvider) assessment -> { - var result = new Button("Result", event -> - this.getUI().flatMap(ui -> ui.navigate(SubmissionView.class, assessment.getId().toString())) - ); - result.setEnabled(assessment.isCompleted()); - - return result; - }); - - grid.addComponentColumn((ValueProvider) assessment -> new Button("Copy Link", event -> - ClientsideClipboard.writeToClipboard( - String.format("email: %s link: https://careers.primefactorsolutions.com/evaluation/%s", - assessment.getCandidate().getEmail(), - assessment.getId())) + grid.addComponentColumn(assessment -> MenuBarUtils.menuBar( + Pair.of("View", __ -> + getUI().flatMap(ui -> ui.navigate(SubmissionView.class, assessment.getId().toString()))), + Pair.of("Copy", __ -> + ClientsideClipboard.writeToClipboard( + String.format("email: %s link: https://careers.primefactorsolutions.com/evaluation/%s", + assessment.getCandidate().getEmail(), + assessment.getId()))), + Pair.of("Email", __ -> { + ConfirmDialog dialog = new ConfirmDialog(); + dialog.setHeader("Send Link Email"); + dialog.setText(String.format("Enviar link por email al candidato %s?", + assessment.getCandidate().getEmail())); + dialog.setCancelable(true); + dialog.setConfirmText("Enviar"); + dialog.setConfirmButtonTheme("primary"); + dialog.addConfirmListener(confirmEvent -> { + try { + assessmentService.sendEmail(assessment); + } catch (Exception e) { + Notification.show("Error sending email: " + e.getMessage(), 10_000, + Notification.Position.TOP_CENTER); + } + }); + dialog.open(); + }) )); - grid.addComponentColumn((ValueProvider) assessment -> - new Button("Send Email", event -> { - ConfirmDialog dialog = new ConfirmDialog(); - dialog.setHeader("Send Link Email"); - dialog.setText(String.format("Enviar link por email al candidato %s?", - assessment.getCandidate().getEmail())); - dialog.setCancelable(true); - dialog.setConfirmText("Enviar"); - dialog.setConfirmButtonTheme("primary"); - dialog.addConfirmListener((ComponentEventListener) confirmEvent -> { - try { - assessmentService.sendEmail(assessment); - } catch (Exception e) { - Notification.show("Error sending email: " + e.getMessage(), 10_000, - Notification.Position.TOP_CENTER); - } - }); - dialog.open(); - })); - grid.setDataProvider(new DataProvider<>() { - @Override - public boolean isInMemory() { - return false; - } - - @Override - public int size(final Query query) { - return assessmentService.getAssessments().size(); - } - - @SuppressWarnings("unused") - @Override - public Stream fetch(final Query query) { - int limit = query.getLimit(); - int pagerSize = query.getPageSize(); - int page = query.getPage(); - - return assessmentService.getAssessments().stream(); - } - - @Override - public void refreshItem(final Assessment assessment) { - // no-op - } - - @Override - public void refreshAll() { - - } - - @Override - public Registration addDataProviderListener(final DataProviderListener dataProviderListener) { - return null; - } - }); + grid.setDataProvider(new ListDataProvider<>(assessmentService.getAssessments())); grid.setAllRowsVisible(true); getCurrentPageLayout().add(hl, grid); diff --git a/src/main/java/com/primefactorsolutions/views/assessment/CandidatesListView.java b/src/main/java/com/primefactorsolutions/views/assessment/CandidatesListView.java index 9879f9f..78fd371 100644 --- a/src/main/java/com/primefactorsolutions/views/assessment/CandidatesListView.java +++ b/src/main/java/com/primefactorsolutions/views/assessment/CandidatesListView.java @@ -4,25 +4,24 @@ import com.primefactorsolutions.model.Candidate; import com.primefactorsolutions.service.CandidateService; import com.primefactorsolutions.views.BaseView; import com.primefactorsolutions.views.MainLayout; +import com.primefactorsolutions.views.util.MenuBarUtils; 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.button.ButtonVariant; +import com.vaadin.flow.component.icon.VaadinIcon; 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.data.provider.ListDataProvider; 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 com.vaadin.flow.spring.security.AuthenticationContext; import jakarta.annotation.security.PermitAll; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.grid.VGrid; -import java.util.stream.Stream; +import java.util.Map; @SpringComponent @Scope("prototype") @@ -36,6 +35,7 @@ public class CandidatesListView extends BaseView { super(authenticationContext); final HorizontalLayout hl = new HorizontalLayout(); final Button addCandidate = new Button("Add Candidate"); + addCandidate.addThemeVariants(ButtonVariant.LUMO_PRIMARY); addCandidate.addClickListener((ComponentEventListener>) buttonClickEvent -> { this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, "new")); }); @@ -44,48 +44,10 @@ public class CandidatesListView extends BaseView { final VGrid grid = new VGrid<>(Candidate.class); grid.setColumns("id", "email"); grid.setAllRowsVisible(true); - grid.addComponentColumn((ValueProvider) candidate -> { - final Button edit = new Button("Edit"); - edit.addClickListener((ComponentEventListener>) buttonClickEvent -> - this.getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString()))); - return edit; - }); - - grid.setDataProvider(new DataProvider<>() { - @Override - public boolean isInMemory() { - return false; - } - - @Override - public int size(final Query query) { - return candidateService.getCandidates().size(); - } - - @SuppressWarnings("unused") - @Override - public Stream fetch(final Query query) { - int limit = query.getLimit(); - int pagerSize = query.getPageSize(); - int page = query.getPage(); - return candidateService.getCandidates().stream(); - } - - @Override - public void refreshItem(final Candidate candidate) { - // no-op - } - - @Override - public void refreshAll() { - // no-op - } - - @Override - public Registration addDataProviderListener(final DataProviderListener dataProviderListener) { - return null; - } - }); + grid.addComponentColumn(candidate -> + MenuBarUtils.menuBar(Map.of(Pair.of("Edit", VaadinIcon.PENCIL), menuItemClickEvent -> + getUI().flatMap(ui -> ui.navigate(CandidateView.class, candidate.getId().toString()))))); + grid.setDataProvider(new ListDataProvider<>(candidateService.getCandidates())); getCurrentPageLayout().add(hl, grid); } diff --git a/src/main/java/com/primefactorsolutions/views/assessment/QuestionsListView.java b/src/main/java/com/primefactorsolutions/views/assessment/QuestionsListView.java index db2ff44..2696d3b 100644 --- a/src/main/java/com/primefactorsolutions/views/assessment/QuestionsListView.java +++ b/src/main/java/com/primefactorsolutions/views/assessment/QuestionsListView.java @@ -4,26 +4,22 @@ import com.primefactorsolutions.model.Question; import com.primefactorsolutions.service.QuestionService; import com.primefactorsolutions.views.BaseView; import com.primefactorsolutions.views.MainLayout; +import com.primefactorsolutions.views.util.MenuBarUtils; 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.button.ButtonVariant; 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.data.provider.ListDataProvider; 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 com.vaadin.flow.spring.security.AuthenticationContext; import jakarta.annotation.security.PermitAll; +import org.apache.commons.lang3.tuple.Pair; import org.springframework.context.annotation.Scope; import org.vaadin.firitin.components.grid.VGrid; -import java.util.stream.Stream; - @SpringComponent @Scope("prototype") @PageTitle("Questions") @@ -36,53 +32,17 @@ public class QuestionsListView extends BaseView { final HorizontalLayout hl = new HorizontalLayout(); final Button addQuestion = new Button("Add Question"); + addQuestion.addThemeVariants(ButtonVariant.LUMO_PRIMARY); addQuestion.addClickListener((ComponentEventListener>) buttonClickEvent -> this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, "new"))); hl.add(addQuestion); final VGrid grid = new VGrid<>(Question.class); grid.setColumns("id", "title"); - grid.addComponentColumn((ValueProvider) question -> { - final Button edit = new Button("Edit"); - edit.addClickListener((ComponentEventListener>) buttonClickEvent -> - this.getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString()))); - return edit; - }); - grid.setDataProvider(new DataProvider<>() { - @Override - public boolean isInMemory() { - return false; - } - - @Override - public int size(final Query query) { - return questionService.getQuestions().size(); - } - - @SuppressWarnings("unused") - @Override - public Stream fetch(final Query query) { - int limit = query.getLimit(); - int pagerSize = query.getPageSize(); - int page = query.getPage(); - return questionService.getQuestions().stream(); - } - - @Override - public void refreshItem(final Question question) { - // no-op - } - - @Override - public void refreshAll() { - // no-op - } - - @Override - public Registration addDataProviderListener(final DataProviderListener dataProviderListener) { - return null; - } - }); + grid.addComponentColumn(question -> + MenuBarUtils.menuBar(Pair.of("Edit", __ -> + getUI().flatMap(ui -> ui.navigate(QuestionView.class, question.getId().toString()))))); + grid.setDataProvider(new ListDataProvider<>(questionService.getQuestions())); grid.setAllRowsVisible(true); getCurrentPageLayout().add(hl, grid); diff --git a/src/main/java/com/primefactorsolutions/views/util/MenuBarUtils.java b/src/main/java/com/primefactorsolutions/views/util/MenuBarUtils.java index 35d97c4..601b4da 100644 --- a/src/main/java/com/primefactorsolutions/views/util/MenuBarUtils.java +++ b/src/main/java/com/primefactorsolutions/views/util/MenuBarUtils.java @@ -1,13 +1,25 @@ package com.primefactorsolutions.views.util; +import com.vaadin.flow.component.ClickEvent; +import com.vaadin.flow.component.ComponentEventListener; import com.vaadin.flow.component.contextmenu.MenuItem; import com.vaadin.flow.component.icon.Icon; import com.vaadin.flow.component.icon.VaadinIcon; import com.vaadin.flow.component.menubar.MenuBar; +import com.vaadin.flow.component.menubar.MenuBarVariant; import lombok.experimental.UtilityClass; +import org.apache.commons.lang3.tuple.Pair; + +import java.util.Map; @UtilityClass public class MenuBarUtils { + public static final Map ICON_MAP = Map.of( + "View", VaadinIcon.EYE, + "Edit", VaadinIcon.PENCIL, + "Copy", VaadinIcon.COPY, + "Email", VaadinIcon.ENVELOPE + ); public static MenuItem createIconItem(final MenuBar menu, final VaadinIcon iconName, final String ariaLabel) { final Icon icon = new Icon(iconName); @@ -16,4 +28,31 @@ public class MenuBarUtils { return item; } + + public static MenuBar menuBar(final Map, + ComponentEventListener>> entries) { + final MenuBar menuBar = new MenuBar(); + menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE); + + entries.forEach((key, value) -> { + final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, key.getValue(), key.getKey()); + viewItem.addClickListener(value); + }); + + return menuBar; + } + + @SafeVarargs + public static MenuBar menuBar(final Pair>>... entries) { + final MenuBar menuBar = new MenuBar(); + menuBar.addThemeVariants(MenuBarVariant.LUMO_TERTIARY_INLINE); + + for (Pair>> entry: entries) { + final MenuItem viewItem = MenuBarUtils.createIconItem(menuBar, + ICON_MAP.get(entry.getKey()), entry.getKey()); + viewItem.addClickListener(entry.getValue()); + } + + return menuBar; + } }