diff --git a/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java b/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java index 7a8c3a58d..2d2663d6a 100644 --- a/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java +++ b/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/TargetView.java @@ -16,7 +16,11 @@ import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; +import java.util.Set; +import java.time.ZoneOffset; import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionStage; +import java.util.function.Function; import java.util.stream.Stream; import jakarta.annotation.security.RolesAllowed; @@ -24,7 +28,9 @@ import jakarta.annotation.security.RolesAllowed; import com.vaadin.flow.component.Component; import com.vaadin.flow.component.Key; import com.vaadin.flow.component.button.Button; +import com.vaadin.flow.component.button.ButtonVariant; import com.vaadin.flow.component.checkbox.CheckboxGroup; +import com.vaadin.flow.component.datetimepicker.DateTimePicker; import com.vaadin.flow.component.dependency.Uses; import com.vaadin.flow.component.formlayout.FormLayout; import com.vaadin.flow.component.grid.Grid; @@ -34,11 +40,15 @@ import com.vaadin.flow.component.orderedlayout.FlexComponent; 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.Text; 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.router.PageTitle; import com.vaadin.flow.router.Route; +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.distributionset.MgmtTargetAssignmentRequestBody; import org.eclipse.hawkbit.mgmt.json.model.tag.MgmtTag; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTarget; import org.eclipse.hawkbit.mgmt.json.model.target.MgmtTargetRequestBody; @@ -91,6 +101,16 @@ public class TargetView extends TableView { hawkbitClient.getTargetRestApi().deleteTarget(toDelete.getControllerId())); return CompletableFuture.completedFuture(null); }); + + Function, CompletionStage> assignHandler = source -> new AssignDialog( + hawkbitClient, source.getSelectedItems()).result(); + + final Button assignBtn = Utils.tooltip(new Button(VaadinIcon.LINK.create()), "Assign"); + assignBtn.addThemeVariants(ButtonVariant.LUMO_PRIMARY); + assignBtn.addClickListener(e -> assignHandler + .apply(selectionGrid) + .thenAccept(v -> selectionGrid.refreshGrid(false))); + controlsLayout.addComponentAtIndex(0, assignBtn); } private static class SimpleFilter implements Filter.Rsql { @@ -325,4 +345,99 @@ public class TargetView extends TableView { }); } } + + private static class AssignDialog extends Utils.BaseDialog { + + private final Select distributionSet; + private final Select actionType; + private final DateTimePicker forceTime = new DateTimePicker("Force Time"); + private final Button assign = new Button("Assign"); + + private AssignDialog(final HawkbitMgmtClient hawkbitClient, Set selectedTargets) { + super("Assign Distribution Set"); + + distributionSet = new Select<>( + "Distribution Set", + this::readyToAssign, + Optional.ofNullable( + hawkbitClient.getDistributionSetRestApi() + .getDistributionSets(0, 30, Constants.NAME_ASC, null) + .getBody()) + .map(body -> body.getContent().toArray(new MgmtDistributionSet[0])) + .orElseGet(() -> new MgmtDistributionSet[0])); + distributionSet.setRequiredIndicatorVisible(true); + distributionSet.setItemLabelGenerator(distributionSetO -> + distributionSetO.getName() + ":" + distributionSetO.getVersion()); + distributionSet.setWidthFull(); + + actionType = new Select<>(); + actionType.setLabel(Constants.ACTION_TYPE); + actionType.setItems(MgmtActionType.values()); + actionType.setValue(MgmtActionType.FORCED); + final ComponentRenderer actionTypeRenderer = new ComponentRenderer<>(actionTypeO -> + switch (actionTypeO) { + case SOFT -> new Text(Constants.SOFT); + case FORCED -> new Text(Constants.FORCED); + case DOWNLOAD_ONLY -> new Text(Constants.DOWNLOAD_ONLY); + case TIMEFORCED -> forceTime; + }); + actionType.addValueChangeListener(e -> actionType.setRenderer(actionTypeRenderer)); + actionType.setItemLabelGenerator(startTypeO -> + switch (startTypeO) { + case SOFT -> Constants.SOFT; + case FORCED -> Constants.FORCED; + case DOWNLOAD_ONLY -> Constants.DOWNLOAD_ONLY; + case TIMEFORCED -> "Time Forced at " + (forceTime.isEmpty() ? "" : " " + forceTime.getValue()); + }); + actionType.setWidthFull(); + + assign.setEnabled(false); + addAssignClickListener(hawkbitClient, selectedTargets); + final Button cancel = Utils.tooltip(new Button("Cancel"), "Cancel (Esc)"); + cancel.addClickListener(e -> close()); + cancel.addClickShortcut(Key.ESCAPE); + final HorizontalLayout actions = new HorizontalLayout(assign, cancel); + actions.setJustifyContentMode(FlexComponent.JustifyContentMode.END); + actions.setSizeFull(); + + final VerticalLayout layout = new VerticalLayout(); + layout.setSizeFull(); + layout.setSpacing(false); + layout.add( + distributionSet, actionType, actions); + add(layout); + open(); + } + + private void readyToAssign(final Object v) { + final boolean assignEnabled = !distributionSet.isEmpty(); + if (assign.isEnabled() != assignEnabled) { + assign.setEnabled(assignEnabled); + } + } + + private void addAssignClickListener(final HawkbitMgmtClient hawkbitClient, final Set selectedTargets) { + assign.addClickListener(e -> { + close(); + List requests = new LinkedList(); + + for (final MgmtTarget target : selectedTargets) { + + MgmtTargetAssignmentRequestBody request = new MgmtTargetAssignmentRequestBody(target.getControllerId()); + + request.setType(actionType.getValue()); + if (actionType.getValue() == MgmtActionType.TIMEFORCED) { + request.setForcetime( + forceTime.isEmpty() ? + System.currentTimeMillis() : + forceTime.getValue().toEpochSecond(ZoneOffset.UTC) * 1000); + } + + requests.add(request); + } + + hawkbitClient.getDistributionSetRestApi().createAssignedTarget(distributionSet.getValue().getId(), requests, null).getBody(); + }); + } + } } diff --git a/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java b/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java index cfb80f11d..d6c2e6d6c 100644 --- a/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java +++ b/hawkbit-simple-ui/src/main/java/org/eclipse/hawkbit/ui/simple/view/util/TableView.java @@ -15,8 +15,12 @@ import java.util.function.Function; import java.util.stream.Stream; import com.vaadin.flow.component.html.Div; +import com.vaadin.flow.component.orderedlayout.FlexComponent; +import com.vaadin.flow.component.orderedlayout.HorizontalLayout; import com.vaadin.flow.component.orderedlayout.VerticalLayout; import com.vaadin.flow.data.provider.Query; +import com.vaadin.flow.theme.lumo.LumoUtility; + import org.eclipse.hawkbit.ui.simple.view.Constants; @SuppressWarnings("java:S119") // better readability @@ -24,16 +28,19 @@ public class TableView extends Div implements Constants { protected SelectionGrid selectionGrid; private final Filter filter; + final VerticalLayout gridLayout; + protected final HorizontalLayout controlsLayout; public TableView( final Filter.Rsql rsql, final SelectionGrid.EntityRepresentation entityRepresentation, final BiFunction, String, Stream> queryFn) { - this(rsql, null, entityRepresentation, queryFn, null, null); + this(rsql, null, entityRepresentation, queryFn); } public TableView( - final Filter.Rsql rsql, final Filter.Rsql alternativeRsql, + final Filter.Rsql rsql, + final Filter.Rsql alternativeRsql, final SelectionGrid.EntityRepresentation entityRepresentation, final BiFunction, String, Stream> queryFn) { this(rsql, alternativeRsql, entityRepresentation, queryFn, null, null); @@ -59,13 +66,19 @@ public class TableView extends Div implements Constants { setSizeFull(); - final VerticalLayout layout = new VerticalLayout(filter, selectionGrid); - layout.setSizeFull(); - layout.setPadding(false); - layout.setSpacing(false); + gridLayout = new VerticalLayout(filter, selectionGrid); + gridLayout.setSizeFull(); + gridLayout.setPadding(false); + gridLayout.setSpacing(false); if (addHandler != null || removeHandler != null) { - layout.add(Utils.addRemoveControls(addHandler, removeHandler, selectionGrid, false)); + controlsLayout = Utils.addRemoveControls(addHandler, removeHandler, selectionGrid, false); + } else { + controlsLayout = new HorizontalLayout(); + controlsLayout.setWidthFull(); + controlsLayout.addClassNames(LumoUtility.Padding.Horizontal.XLARGE, LumoUtility.Padding.Vertical.SMALL, LumoUtility.BoxSizing.BORDER); + controlsLayout.setAlignItems(FlexComponent.Alignment.BASELINE); } - add(layout); + gridLayout.add(controlsLayout); + add(gridLayout); } }