From 87eefe6e9c06ec8e23b7e02f32dfc4a8a120514f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Mauger?= Date: Fri, 20 Feb 2026 10:33:01 +0100 Subject: [PATCH] feat: add target filter query view (#2892) * feat: add target filter query view * fix: add missing file header, also add query column and details panel in TargetFilterQueryView --------- Co-authored-by: MICKAEL MAUGER --- .../org/eclipse/hawkbit/ui/MainLayout.java | 4 + .../eclipse/hawkbit/ui/view/RolloutView.java | 2 +- .../ui/view/TargetFilterQueryView.java | 405 ++++++++++++++++++ .../eclipse/hawkbit/ui/view/TargetView.java | 29 +- .../eclipse/hawkbit/ui/view/util/Utils.java | 45 +- 5 files changed, 450 insertions(+), 35 deletions(-) create mode 100644 hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetFilterQueryView.java diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MainLayout.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MainLayout.java index 87b55fe4f..d8641e534 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MainLayout.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/MainLayout.java @@ -45,6 +45,7 @@ import org.eclipse.hawkbit.ui.view.ConfigView; import org.eclipse.hawkbit.ui.view.DistributionSetView; import org.eclipse.hawkbit.ui.view.RolloutView; import org.eclipse.hawkbit.ui.view.SoftwareModuleView; +import org.eclipse.hawkbit.ui.view.TargetFilterQueryView; import org.eclipse.hawkbit.ui.view.TargetView; /** @@ -117,6 +118,9 @@ public final class MainLayout extends AppLayout { if (accessChecker.hasAccess(TargetView.class)) { nav.addItem(new SideNavItem("Targets", TargetView.class, VaadinIcon.FILTER.create())); } + if (accessChecker.hasAccess(TargetFilterQueryView.class)) { + nav.addItem(new SideNavItem("Target Filter Queries", TargetFilterQueryView.class, VaadinIcon.FILTER.create())); + } if (accessChecker.hasAccess(RolloutView.class)) { nav.addItem(new SideNavItem("Rollouts", RolloutView.class, VaadinIcon.COGS.create())); } diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/RolloutView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/RolloutView.java index 29f111681..6e5f8b8f2 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/RolloutView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/RolloutView.java @@ -337,7 +337,7 @@ public final class RolloutView extends TableView description.setMinLength(2); description.setWidthFull(); - actionType = Utils.actionTypeControls(forceTime); + actionType = Utils.actionTypeControls(MgmtActionType.FORCED, forceTime); startType = new Select<>(); startType.setValue(StartType.MANUAL); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetFilterQueryView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetFilterQueryView.java new file mode 100644 index 000000000..9d6370caa --- /dev/null +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetFilterQueryView.java @@ -0,0 +1,405 @@ +/** + * Copyright (c) 2026 Contributors to the Eclipse Foundation + * + * This program and the accompanying materials are made + * available under the terms of the Eclipse Public License 2.0 + * which is available at https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.hawkbit.ui.view; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.vaadin.flow.component.Component; +import com.vaadin.flow.component.Key; +import com.vaadin.flow.component.Unit; +import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; +import com.vaadin.flow.component.combobox.ComboBox; +import com.vaadin.flow.component.confirmdialog.ConfirmDialog; +import com.vaadin.flow.component.dependency.Uses; +import com.vaadin.flow.component.formlayout.FormLayout; +import com.vaadin.flow.component.grid.Grid; +import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.html.Paragraph; +import com.vaadin.flow.component.html.Span; +import com.vaadin.flow.component.icon.Icon; +import com.vaadin.flow.component.icon.VaadinIcon; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; +import com.vaadin.flow.component.orderedlayout.VerticalLayout; +import com.vaadin.flow.component.select.Select; +import com.vaadin.flow.component.tabs.TabSheet; +import com.vaadin.flow.component.textfield.TextArea; +import com.vaadin.flow.component.textfield.TextField; +import com.vaadin.flow.data.renderer.ComponentRenderer; +import com.vaadin.flow.dom.Style; +import com.vaadin.flow.router.PageTitle; +import com.vaadin.flow.router.Route; +import com.vaadin.flow.theme.lumo.LumoUtility; +import jakarta.annotation.security.RolesAllowed; +import lombok.Getter; +import org.eclipse.hawkbit.mgmt.json.model.PagedList; +import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtActionType; +import org.eclipse.hawkbit.mgmt.json.model.distributionset.MgmtDistributionSet; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtDistributionSetAutoAssignment; +import org.eclipse.hawkbit.mgmt.json.model.targetfilter.MgmtTargetFilterQuery; +import org.eclipse.hawkbit.ui.HawkbitMgmtClient; +import org.eclipse.hawkbit.ui.MainLayout; +import org.eclipse.hawkbit.ui.view.util.Filter; +import org.eclipse.hawkbit.ui.view.util.SelectionGrid; +import org.eclipse.hawkbit.ui.view.util.TableView; +import org.eclipse.hawkbit.ui.view.util.Utils; + +import java.util.*; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Stream; + +@PageTitle("Target Filter Queries") +@Route(value = "target_filter_queries", layout = MainLayout.class) +@RolesAllowed({ "TARGET_READ" }) +@Uses(Icon.class) +public class TargetFilterQueryView extends TableView { + public TargetFilterQueryView(final HawkbitMgmtClient hawkbitClient) { + super( + new TargetFilterQueryFilter(), + null, + new SelectionGrid.EntityRepresentation<>(TargetFilterQueryGridItem.class, TargetFilterQueryGridItem::getId) { + + @Override + protected void addColumns(final Grid grid) { + grid.addColumn(MgmtTargetFilterQuery::getId).setHeader(Constants.ID).setAutoWidth(true).setKey("id").setSortable(true); + grid.addColumn(MgmtTargetFilterQuery::getName).setHeader(Constants.NAME).setAutoWidth(true).setKey("name").setSortable(true).setResizable(true); + grid.addColumn(new ComponentRenderer<>(QueryCell::new)).setHeader("Query").setAutoWidth(true).setKey("query").setResizable(true); + grid.addColumn(Utils.localDateTimeRenderer(MgmtTargetFilterQuery::getLastModifiedAt)).setHeader(Constants.LAST_MODIFIED_AT).setKey("lastModifiedAt") + .setSortable(true).setAutoWidth(true).setResizable(true); + grid.addColumn(new ComponentRenderer<>(DistributionSetCell::new)).setHeader(Constants.DISTRIBUTION_SET).setAutoWidth(true).setResizable(true); + + grid.addComponentColumn(rollout -> new Actions(rollout, grid, hawkbitClient)).setHeader( + Constants.ACTIONS).setAutoWidth(true); + } + }, + (query, filter) -> Optional.ofNullable( + hawkbitClient.getTargetFilterQueryRestApi() + .getFilters(filter, query.getOffset(), query.getPageSize(), Utils.getSortParam(query.getSortOrders(), Constants.NAME_ASC), "compact") + .getBody()) + .stream() + .map(PagedList::getContent) + .flatMap(List::stream) + .map(m -> TargetFilterQueryGridItem.from(hawkbitClient, m)), + null, + selectionGrid -> { + selectionGrid.getSelectedItems() + .forEach(toDelete -> hawkbitClient.getTargetFilterQueryRestApi().deleteFilter(toDelete.getId())); + return CompletableFuture.completedFuture(null); + }, + filterQuery -> { + final TargetFilterQueryDetailedView detailedView = new TargetFilterQueryDetailedView(); + detailedView.setItem(filterQuery); + return detailedView; + } + ); + } + + private static class TargetFilterQueryFilter implements Filter.Rsql { + + private final TextField name = Utils.textField(Constants.NAME); + + private TargetFilterQueryFilter() { + name.setPlaceholder(""); + } + + @Override + public List components() { + return List.of(name); + } + + @Override + public String filter() { + return Filter.filter( + Map.of( + "name", name.getOptionalValue().map(s -> "*" + s + "*") + )); + } + } + + private static class QueryCell extends Div { + + private QueryCell(final TargetFilterQueryGridItem filterQuery) { + String query = filterQuery.getQuery(); + if (query != null) { + setText(query); + setTitle(query); + } + getStyle().setOverflow(Style.Overflow.HIDDEN); + getStyle().set("text-overflow", "ellipsis"); + setWhiteSpace(WhiteSpace.NOWRAP); + setMaxWidth(400, Unit.PIXELS); + } + } + + private static class DistributionSetCell extends HorizontalLayout { + + private DistributionSetCell(final TargetFilterQueryGridItem filterQuery) { + filterQuery.getDs().ifPresent(ds -> { + Icon icon = getActionTypeIcon(filterQuery.getAutoAssignActionType()); + icon.getStyle().setFlexShrink("0"); + + Span dsName = new Span(ds.getName() + ":" + ds.getVersion()); + dsName.getStyle().setOverflow(Style.Overflow.HIDDEN); + dsName.getStyle().set("text-overflow", "ellipsis"); + dsName.getStyle().setWhiteSpace(Style.WhiteSpace.NOWRAP); + + add(icon, dsName); + }); + setAlignItems(Alignment.CENTER); + setSpacing(true); + getStyle().setFlexWrap(Style.FlexWrap.NOWRAP); + } + + private Icon getActionTypeIcon(MgmtActionType actionType) { + Icon icon = switch (actionType) { + case FORCED -> VaadinIcon.BOLT.create(); + case SOFT -> VaadinIcon.USER_CHECK.create(); + case DOWNLOAD_ONLY -> VaadinIcon.DOWNLOAD.create(); + default -> VaadinIcon.QUESTION_CIRCLE.create(); + }; + icon.addClassNames(LumoUtility.IconSize.SMALL); + return Utils.tooltip(icon, actionType.getName()); + } + } + + private static class Actions extends HorizontalLayout { + + private final Grid grid; + private final transient HawkbitMgmtClient hawkbitClient; + + private Actions(final MgmtTargetFilterQuery filter, final Grid grid, + final HawkbitMgmtClient hawkbitClient) { + this.grid = grid; + this.hawkbitClient = hawkbitClient; + init(filter); + } + + private void init(final MgmtTargetFilterQuery filter) { + if (filter.getAutoAssignDistributionSet() == null) { + Button autoAssignButton = new Button(VaadinIcon.LINK.create()); + autoAssignButton.addClickListener(e -> + new AutoAssignDialog(filter.getId(), hawkbitClient, () -> refresh(filter.getId())).open() + ); + add(Utils.tooltip(autoAssignButton, "Auto assign")); + } else { + Button unassignButton = new Button(VaadinIcon.UNLINK.create()); + unassignButton.addClickListener(e -> { + ConfirmDialog dialog = Utils.confirmDialog("Unassign Distribution Set", + "Are you sure you want to unassign the distribution set of target filter query '" + filter.getName() + "'?", + "Unassign", + () -> { + hawkbitClient.getTargetFilterQueryRestApi().deleteAssignedDistributionSet(filter.getId()); + refresh(filter.getId()); + }); + dialog.open(); + }); + add(Utils.tooltip(unassignButton, "Unassign")); + } + Button deleteButton = new Button(VaadinIcon.TRASH.create()); + deleteButton.addClickListener(e -> { + ConfirmDialog dialog = Utils.confirmDialog("Delete Target Filter Query", + "Are you sure you want to delete the target filter query '" + filter.getName() + "'?", + "Delete", + () -> { + hawkbitClient.getTargetFilterQueryRestApi().deleteFilter(filter.getId()); + grid.getDataProvider().refreshAll(); + }); + dialog.open(); + }); + add(Utils.tooltip(deleteButton, "Delete")); + } + + private void refresh(Long filterId) { + removeAll(); + final MgmtTargetFilterQuery body = hawkbitClient.getTargetFilterQueryRestApi().getFilter(filterId).getBody(); + if (body != null) { + grid.getDataProvider().refreshItem(TargetFilterQueryGridItem.from(hawkbitClient, body)); + init(body); + } + } + } + + private static class AutoAssignDialog extends Utils.BaseDialog { + + private final Long filterId; + private final Select actionType; + private final ComboBox distributionSet; + private final Button assign = new Button("Assign"); + + private AutoAssignDialog(final Long filterId, final HawkbitMgmtClient hawkbitClient, Runnable onSuccess) { + super("Select auto assignment distribution set"); + + this.filterId = filterId; + + Paragraph description = new Paragraph("When an auto assign distribution set is selected, " + + "it will be automatically assigned to all targets that match the target filter."); + + actionType = Utils.actionTypeControls(new MgmtActionType[]{MgmtActionType.SOFT, MgmtActionType.FORCED, MgmtActionType.DOWNLOAD_ONLY}, MgmtActionType.FORCED, null); + + distributionSet = Utils.nameComboBox("Distribution Set", this::readyToAssign, query -> Optional.ofNullable( + hawkbitClient.getDistributionSetRestApi() + .getDistributionSets( + query.getFilter().orElse(null), + query.getOffset(), + query.getLimit(), + Constants.NAME_ASC) + .getBody()).stream().flatMap(body -> body.getContent().stream())); + distributionSet.setItemLabelGenerator(ds -> ds.getName() + ":" + ds.getVersion()); + distributionSet.focus(); + distributionSet.setRequiredIndicatorVisible(true); + distributionSet.setWidthFull(); + + assign.setEnabled(false); + assign.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + addAssignClickListener(hawkbitClient, onSuccess); + final Button cancel = Utils.tooltip(new Button(CANCEL), CANCEL_ESC); + cancel.addClickListener(e -> close()); + cancel.addClickShortcut(Key.ESCAPE); + getFooter().add(cancel); + getFooter().add(assign); + + final VerticalLayout layout = new VerticalLayout(); + layout.setSizeFull(); + layout.setSpacing(false); + layout.add(description, actionType, distributionSet); + add(layout); + open(); + } + + private void readyToAssign(final Object v) { + final boolean createEnabled = !distributionSet.isEmpty(); + if (assign.isEnabled() != createEnabled) { + assign.setEnabled(createEnabled); + } + } + + private void addAssignClickListener(final HawkbitMgmtClient hawkbitClient, Runnable onSuccess) { + assign.addClickListener(e -> { + MgmtDistributionSetAutoAssignment newAssignment = new MgmtDistributionSetAutoAssignment(); + newAssignment.setId(distributionSet.getValue().getId()); + newAssignment.setType(actionType.getValue()); + hawkbitClient.getTargetFilterQueryRestApi().postAssignedDistributionSet(filterId, newAssignment); + onSuccess.run(); + close(); + }); + } + } + + private static class TargetFilterQueryDetailedView extends VerticalLayout { + + private final Span filterName; + private final TargetFilterQueryDetails details; + + private TargetFilterQueryDetailedView() { + filterName = new Span(); + details = new TargetFilterQueryDetails(); + setWidthFull(); + + add(filterName); + final TabSheet tabSheet = new TabSheet(); + tabSheet.setWidthFull(); + tabSheet.add("Details", details); + add(tabSheet); + } + + private void setItem(final TargetFilterQueryGridItem filterQuery) { + this.filterName.setText(filterQuery.getName()); + this.details.setItem(filterQuery); + } + } + + private static class TargetFilterQueryDetails extends FormLayout { + + private final TextField name = Utils.textField(Constants.NAME); + private final TextArea query = new TextArea("Query"); + private final TextField createdBy = Utils.textField(Constants.CREATED_BY); + private final TextField createdAt = Utils.textField(Constants.CREATED_AT); + private final TextField lastModifiedBy = Utils.textField(Constants.LAST_MODIFIED_BY); + private final TextField lastModifiedAt = Utils.textField(Constants.LAST_MODIFIED_AT); + private final TextField autoAssignDistributionSet = Utils.textField("Auto Assign Distribution Set"); + private final TextField autoAssignActionType = Utils.textField("Auto Assign Action Type"); + private final TextField autoAssignWeight = Utils.textField("Auto Assign Weight"); + private final TextField confirmationRequired = Utils.textField("Confirmation Required"); + + private TargetFilterQueryDetails() { + query.setMinLength(2); + Stream.of( + name, query, + createdBy, createdAt, + lastModifiedBy, lastModifiedAt, + autoAssignDistributionSet, autoAssignActionType, + autoAssignWeight, confirmationRequired + ) + .forEach(field -> { + field.setReadOnly(true); + add(field); + }); + + setResponsiveSteps(new FormLayout.ResponsiveStep("0", 2)); + setColspan(query, 2); + } + + private void setItem(final TargetFilterQueryGridItem filterQuery) { + name.setValue(filterQuery.getName() != null ? filterQuery.getName() : ""); + query.setValue(filterQuery.getQuery() != null ? filterQuery.getQuery() : ""); + createdBy.setValue(filterQuery.getCreatedBy() != null ? filterQuery.getCreatedBy() : ""); + createdAt.setValue(Utils.localDateTimeFromTs(filterQuery.getCreatedAt())); + lastModifiedBy.setValue(filterQuery.getLastModifiedBy() != null ? filterQuery.getLastModifiedBy() : ""); + lastModifiedAt.setValue(Utils.localDateTimeFromTs(filterQuery.getLastModifiedAt())); + + filterQuery.getDs().ifPresentOrElse( + ds -> autoAssignDistributionSet.setValue(ds.getName() + ":" + ds.getVersion()), + () -> autoAssignDistributionSet.setValue("") + ); + autoAssignActionType.setValue(filterQuery.getAutoAssignActionType() != null ? + filterQuery.getAutoAssignActionType().getName() : ""); + autoAssignWeight.setValue(filterQuery.getAutoAssignWeight() != null ? + filterQuery.getAutoAssignWeight().toString() : ""); + confirmationRequired.setValue(filterQuery.getConfirmationRequired() != null ? + filterQuery.getConfirmationRequired().toString() : ""); + } + } + + // todo change /targetfilters api to reduce api calls ? + @Getter + public static class TargetFilterQueryGridItem extends MgmtTargetFilterQuery { + + TargetFilterQueryGridItem() { + super(); + } + + private Optional ds; + static ObjectMapper objectMapper = new ObjectMapper(); + + public static TargetFilterQueryGridItem from(final HawkbitMgmtClient hawkbitClient, MgmtTargetFilterQuery filter) { + TargetFilterQueryGridItem filterGridItem = objectMapper.convertValue(filter, TargetFilterQueryGridItem.class); + + if (filterGridItem.getAutoAssignDistributionSet() != null) { + filterGridItem.ds = Optional.ofNullable( + hawkbitClient.getTargetFilterQueryRestApi().getAssignedDistributionSet(filterGridItem.getId()).getBody() + ); + } else { + filterGridItem.ds = Optional.empty(); + } + return filterGridItem; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof TargetFilterQueryGridItem other)) return false; + return Objects.equals(getId(), other.getId()); + } + + @Override + public int hashCode() { + return Objects.hashCode(getId()); + } + } +} diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetView.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetView.java index 0785bb79a..4ee41ca14 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetView.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/TargetView.java @@ -638,11 +638,10 @@ public final class TargetView extends TableView metadataArea.setEmptyStateText("No metadata found"); metadataArea.addColumn(MgmtMetadata::getKey).setHeader(KEY).setAutoWidth(true); metadataArea.addColumn(MgmtMetadata::getValue).setHeader(VALUE).setAutoWidth(true); - metadataArea.addComponentColumn(metadata -> { - final Button deleteBtn = Utils.tooltip(new Button(VaadinIcon.TRASH.create()), "Delete Metadata"); - deleteBtn.addClickListener(e -> confirmDeleteDialog(metadata.getKey())); - return deleteBtn; - }).setHeader("Actions").setAutoWidth(true).setFlexGrow(0); + metadataArea.addComponentColumn(metadata -> Utils.deleteButton("Delete metadata", () -> { + hawkbitClient.getTargetRestApi().deleteMetadata(target.getControllerId(), metadata.getKey()); + refreshMetadatas(); + })).setHeader("Actions").setAutoWidth(true).setFlexGrow(0); metadataArea.setWidthFull(); add(metadataArea); @@ -671,24 +670,6 @@ public final class TargetView extends TableView .map(PagedList::getContent) .orElse(Collections.emptyList())); } - - private void confirmDeleteDialog(String key) { - final ConfirmDialog dialog = new ConfirmDialog(); - dialog.setHeader("Confirm Deletion"); - dialog.setText("Are you sure you want to delete metadata " + key + "?"); - - dialog.setCancelable(true); - dialog.addCancelListener(event -> dialog.close()); - - dialog.setConfirmButtonTheme(ButtonVariant.LUMO_ERROR.getVariantName()); - dialog.setConfirmText("Delete"); - dialog.addConfirmListener(event -> { - hawkbitClient.getTargetRestApi().deleteMetadata(target.getControllerId(), key); - refreshMetadatas(); - dialog.close(); - }); - dialog.open(); - } } public static final class TargetActionsHistoryLayout extends VerticalLayout { @@ -899,7 +880,7 @@ public final class TargetView extends TableView distributionSet.setItemLabelGenerator(distributionSetO -> distributionSetO.getName() + ":" + distributionSetO.getVersion()); distributionSet.setWidthFull(); - actionType = Utils.actionTypeControls(forceTime); + actionType = Utils.actionTypeControls(MgmtActionType.FORCED, forceTime); assign.setEnabled(false); assign.addThemeVariants(ButtonVariant.LUMO_PRIMARY); diff --git a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java index 7e71fe8b2..dcca971a4 100644 --- a/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java +++ b/hawkbit-ui/src/main/java/org/eclipse/hawkbit/ui/view/util/Utils.java @@ -112,6 +112,15 @@ public class Utils { return combo; } + public static Button deleteButton(String tooltipText, Runnable deleteAction) { + final Button button = Utils.tooltip(new Button(VaadinIcon.TRASH.create()), tooltipText); + button.addClickListener(e -> { + ConfirmDialog dialog = Utils.deleteConfirmDialog(deleteAction); + dialog.open(); + }); + return button; + } + @SuppressWarnings("java:S119") // better readability public static HorizontalLayout addRemoveControls( final Function, CompletionStage> addHandler, @@ -137,7 +146,10 @@ public class Utils { layout.add(addBtn); } if (removeHandler != null) { - final ConfirmDialog dialog = promptForDeleteConfirmation(removeHandler, selectionGrid); + final ConfirmDialog dialog = deleteConfirmDialog( + () -> removeHandler + .apply(selectionGrid) + .thenAccept(v -> selectionGrid.refreshGrid(false))); final Button removeBtn = tooltip(new Button(VaadinIcon.MINUS.create()), "Remove"); removeBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY, ButtonVariant.LUMO_CONTRAST); removeBtn.addClickListener(e -> dialog.open()); @@ -147,19 +159,29 @@ public class Utils { return layout; } - private static ConfirmDialog promptForDeleteConfirmation( - final Function, CompletionStage> removeHandler, final SelectionGrid selectionGrid) { + private static ConfirmDialog deleteConfirmDialog(final Runnable removeHandler) { + return confirmDialog("Confirm Deletion", + "Are you sure you want to delete the selected items? This action cannot be undone.", + "Delete", + removeHandler); + } + + public static ConfirmDialog confirmDialog( + final String header, + final String text, + final String confirmText, + final Runnable onConfirm) { final ConfirmDialog dialog = new ConfirmDialog(); - dialog.setHeader("Confirm Deletion"); - dialog.setText("Are you sure you want to delete the selected items? This action cannot be undone."); + dialog.setHeader(header); + dialog.setText(text); dialog.setCancelable(true); dialog.addCancelListener(event -> dialog.close()); dialog.setConfirmButtonTheme(ButtonVariant.LUMO_ERROR.getVariantName()); - dialog.setConfirmText("Delete"); + dialog.setConfirmText(confirmText); dialog.addConfirmListener(event -> { - removeHandler.apply(selectionGrid).thenAccept(v -> selectionGrid.refreshGrid(false)); + onConfirm.run(); dialog.close(); }); return dialog; @@ -210,12 +232,15 @@ public class Utils { return icon; } - public static Select actionTypeControls(DateTimePicker forceTime) { + public static Select actionTypeControls(MgmtActionType defaultValue, DateTimePicker forceTime) { + return actionTypeControls(MgmtActionType.values(), defaultValue, forceTime); + } + public static Select actionTypeControls(MgmtActionType[] displayedValues, MgmtActionType defaultValue, DateTimePicker forceTime) { Select actionType = new Select<>(); actionType.setLabel(Constants.ACTION_TYPE); - actionType.setItems(MgmtActionType.values()); - actionType.setValue(MgmtActionType.FORCED); + actionType.setItems(displayedValues); + actionType.setValue(defaultValue); final ComponentRenderer actionTypeRenderer = new ComponentRenderer<>(actionTypeO -> switch (actionTypeO) { case SOFT -> new Text(Constants.SOFT); case FORCED -> new Text(Constants.FORCED);